add unit tests for minesweeper logic and score handling
This commit is contained in:
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,61 +1,313 @@
|
|||||||
package ca.bcit.comp2522.project;
|
package ca.bcit.comp2522.project;
|
||||||
|
|
||||||
import ca.bcit.comp2522.project.Mines;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
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
|
public class MinesTest
|
||||||
{
|
{
|
||||||
private static final int TEST_WIDTH = 5;
|
private Mines mines;
|
||||||
private static final int TEST_HEIGHT = 5;
|
|
||||||
private static final int TEST_MINES = 3;
|
@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
|
@Test
|
||||||
public void newGameHasCorrectMineCount()
|
public void testFieldArraySizeMatchesWidthTimesHeight() throws Exception
|
||||||
{
|
{
|
||||||
final Mines game;
|
final int[] fieldArray;
|
||||||
int mineCount;
|
fieldArray = getFieldArray(mines);
|
||||||
|
|
||||||
game = new Mines(TEST_WIDTH, TEST_HEIGHT, TEST_MINES, false);
|
final int expectedSize;
|
||||||
mineCount = 0;
|
expectedSize = mines.getWidth() * mines.getHeight();
|
||||||
|
|
||||||
for (int i = 0; i < TEST_WIDTH * TEST_HEIGHT; i++)
|
assertEquals(expectedSize, fieldArray.length);
|
||||||
{
|
|
||||||
if (game.isMine(i))
|
|
||||||
{
|
|
||||||
mineCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(TEST_MINES, mineCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void revealNonMineDoesNotLose()
|
public void testToggleFlagCyclesCorrectly()
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
final Mines game;
|
final int index;
|
||||||
int safeIndex;
|
index = 0;
|
||||||
|
|
||||||
game = new Mines(TEST_WIDTH, TEST_HEIGHT, TEST_MINES, false);
|
final int result1;
|
||||||
safeIndex = -1;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotEquals(-1, safeIndex);
|
assertTrue(foundMine);
|
||||||
|
}
|
||||||
|
|
||||||
assertFalse(game.reveal(safeIndex));
|
@Test
|
||||||
assertTrue(game.isRevealed(safeIndex));
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user