add unit tests for minesweeper logic and score handling

This commit is contained in:
SowinskiBraeden committed 2025-11-29 14:50:15 -08:00
1 parent eb16d809cc
commit 972ef0c8d9
2 files changed
+373 -31

No files matched your search

@@ -0,0 +1,90 @@
package ca.bcit.comp2522.project;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.io.File;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* MinesScore test to test score file appending,
* reading, high-score comparisons
*
* @author Braeden Sowinski
* @version 1.0.0
*/
public class MinesScoreTest
{
@Test
public void testAppendScoreThenRead(@TempDir Path tempDir)
{
final File file;
file = tempDir.resolve("scores.txt").toFile();
final LocalDateTime now;
now = LocalDateTime.now();
final MinesScore score;
score = new MinesScore(now, 45, MinesScore.DIFFICULTY_EASY, true);
MinesScore.appendScoreToFile(score, file.getAbsolutePath());
final List<MinesScore> scores;
scores = MinesScore.readScoresFromFile(file.getAbsolutePath());
assertEquals(1, scores.size());
assertEquals(45, scores.get(0).getSeconds());
assertEquals(MinesScore.DIFFICULTY_EASY, scores.get(0).getDifficulty());
assertTrue(scores.get(0).getRandomMode());
}
@ParameterizedTest
@ValueSource(strings = { "easy", "medium", "hard" })
public void testDifficultyValidationAcceptsValidValues(final String difficulty)
{
final LocalDateTime now;
now = LocalDateTime.now();
final MinesScore score;
score = new MinesScore(now, 10, difficulty, false);
assertEquals(difficulty, score.getDifficulty());
}
@Test
public void testHighScoreComparisonCorrect()
{
final LocalDateTime now;
now = LocalDateTime.now();
final MinesScore best;
best = new MinesScore(now, 20, MinesScore.DIFFICULTY_HARD, false);
final MinesScore challenger;
challenger = new MinesScore(now, 10, MinesScore.DIFFICULTY_HARD, false);
final List<MinesScore> scores;
scores = List.of(best);
assertTrue(MinesScore.isHighScore(challenger, scores));
assertFalse(MinesScore.isHighScore(best, scores));
}
@Test
public void testReadScoresHandlesBlankFile(@TempDir Path tempDir)
{
final File file;
file = tempDir.resolve("empty.txt").toFile();
final List<MinesScore> result;
result = MinesScore.readScoresFromFile(file.getAbsolutePath());
assertTrue(result.isEmpty());
}
}
+283 -31
View File
@@ -1,61 +1,313 @@
package ca.bcit.comp2522.project;
import ca.bcit.comp2522.project.Mines;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import static org.junit.jupiter.api.Assertions.*;
/**
* Lesson 10: unit tests for MinesGame.
* MinesTest game logic, field array logic such as
* popFieldVoid, validates correct cells are revealed,
* flag cycling and tracking, invalid moves such as
* revealing a flagged cell, revealing mine triggers
* loss, etc.
*
* @author Braeden Sowinski
* @version 1.0.0
*/
public class MinesTest
{
private static final int TEST_WIDTH = 5;
private static final int TEST_HEIGHT = 5;
private static final int TEST_MINES = 3;
private Mines mines;
@BeforeEach
public void setUp()
{
final int width;
final int height;
final int totalMines;
final boolean randomMode;
width = 3;
height = 3;
totalMines = 1;
randomMode = false;
mines = new Mines(width, height, totalMines, randomMode);
}
// --- Reflection helpers -------------------------------------------------
private int[] getFieldArray(final Mines game) throws Exception
{
final Field fieldField;
fieldField = Mines.class.getDeclaredField("field");
fieldField.setAccessible(true);
final Object value;
value = fieldField.get(game);
return (int[]) value;
}
private void setFieldArray(final Mines game, final int[] newField) throws Exception
{
final Field fieldField;
fieldField = Mines.class.getDeclaredField("field");
fieldField.setAccessible(true);
fieldField.set(game, newField);
}
// --- Basic tests ---------------------------------------------------------
@Test
public void newGameHasCorrectMineCount()
public void testFieldArraySizeMatchesWidthTimesHeight() throws Exception
{
final Mines game;
int mineCount;
final int[] fieldArray;
fieldArray = getFieldArray(mines);
game = new Mines(TEST_WIDTH, TEST_HEIGHT, TEST_MINES, false);
mineCount = 0;
final int expectedSize;
expectedSize = mines.getWidth() * mines.getHeight();
for (int i = 0; i < TEST_WIDTH * TEST_HEIGHT; i++)
{
if (game.isMine(i))
{
mineCount++;
}
}
assertEquals(TEST_MINES, mineCount);
assertEquals(expectedSize, fieldArray.length);
}
@Test
public void revealNonMineDoesNotLose()
throws Exception
public void testToggleFlagCyclesCorrectly()
{
final Mines game;
int safeIndex;
final int index;
index = 0;
game = new Mines(TEST_WIDTH, TEST_HEIGHT, TEST_MINES, false);
safeIndex = -1;
final int result1;
result1 = mines.toggleFlag(index);
assertEquals(Mines.FLAG, result1);
for (int i = 0; i < TEST_WIDTH * TEST_HEIGHT; i++)
final int result2;
result2 = mines.toggleFlag(index);
assertEquals(Mines.FLAG_QUESTION, result2);
final int result3;
result3 = mines.toggleFlag(index);
assertEquals(Mines.NO_FLAG, result3);
}
@Test
public void testRevealThrowsOnFlaggedCell() throws Exception
{
final int index;
index = 0;
mines.toggleFlag(index);
assertThrows(InvalidMoveException.class, () -> mines.reveal(index));
}
@Test
public void testRevealMineReturnsTrue() throws Exception
{
final int size;
size = mines.getWidth() * mines.getHeight();
boolean foundMine;
foundMine = false;
for (int i = 0; i < size; i++)
{
if (!game.isMine(i))
if (mines.isMine(i))
{
safeIndex = i;
final boolean hitMine;
hitMine = mines.reveal(i);
assertTrue(hitMine);
foundMine = true;
break;
}
}
assertNotEquals(-1, safeIndex);
assertTrue(foundMine);
}
assertFalse(game.reveal(safeIndex));
assertTrue(game.isRevealed(safeIndex));
@Test
public void testHasWonDetectsWinState() throws Exception
{
final int size;
size = mines.getWidth() * mines.getHeight();
for (int i = 0; i < size; i++)
{
if (!mines.isMine(i))
{
mines.reveal(i);
}
}
assertTrue(mines.hasWon());
}
// --- Advanced tests (using reflection to control the board) -------------
@Test
public void testPopVoidExpansionRevealsConnectedRegion() throws Exception
{
/*
Board layout (4x4), indices:
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
Values:
0 0 1 M
0 0 1 1
0 0 0 0
M 1 0 0
When revealing index 0, all connected 0s should be revealed.
*/
final int width;
final int height;
final int totalMines;
final boolean randomMode;
width = 4;
height = 4;
totalMines = 0;
randomMode = false;
final Mines board;
board = new Mines(width, height, totalMines, randomMode);
final int[] field;
field = new int[]{
0, 0, 1, -1,
0, 0, 1, 1,
0, 0, 0, 0,
-1, 1, 0, 0
};
setFieldArray(board, field);
final int revealIndex;
revealIndex = 0;
board.reveal(revealIndex);
// verify zero region auto-expands
assertTrue(board.isRevealed(0));
assertTrue(board.isRevealed(1));
assertTrue(board.isRevealed(4));
assertTrue(board.isRevealed(5));
assertTrue(board.isRevealed(6));
assertTrue(board.isRevealed(9));
assertTrue(board.isRevealed(10));
assertTrue(board.isRevealed(14));
}
@Test
public void testRandomizeRemainingKeepsFlaggedMines() throws Exception
{
final int width;
final int height;
final int totalMines;
final boolean randomMode;
width = 4;
height = 4;
totalMines = 0;
randomMode = true; // must be true for randomizeRemaining to do anything
final Mines board;
board = new Mines(width, height, totalMines, randomMode);
final int[] field;
field = new int[]{
-1, 0, 0, 0,
0, 0, 0, 0,
0, 0, -1, 0,
0, 0, 0, 0
};
setFieldArray(board, field);
final int flaggedMineIndex;
flaggedMineIndex = 0;
board.toggleFlag(flaggedMineIndex);
board.randomizeRemaining();
// flagged mine should still be a mine
assertTrue(board.isMine(flaggedMineIndex));
}
@Test
public void testHasWonTrueWhenAllNonMinesRevealedOnCustomBoard() throws Exception
{
final int width;
final int height;
final int totalMines;
final boolean randomMode;
width = 3;
height = 3;
totalMines = 1;
randomMode = false;
final Mines board;
board = new Mines(width, height, totalMines, randomMode);
final int[] field;
field = new int[]{
-1, 1, 0,
1, 1, 0,
0, 0, 0
};
setFieldArray(board, field);
for (int i = 0; i < 9; i++)
{
if (!board.isMine(i))
{
board.reveal(i);
}
}
assertTrue(board.hasWon());
}
@Test
public void testRevealMineReturnsTrueOnCustomBoard() throws Exception
{
final int width;
final int height;
final int totalMines;
final boolean randomMode;
width = 3;
height = 3;
totalMines = 1;
randomMode = false;
final Mines board;
board = new Mines(width, height, totalMines, randomMode);
final int[] field;
field = new int[]{
0, -1, 0,
0, 0, 0,
0, 0, 0
};
setFieldArray(board, field);
final int mineIndex;
mineIndex = 1;
final boolean hitMine;
hitMine = board.reveal(mineIndex);
assertTrue(hitMine);
}
}