diff --git a/pom.xml b/pom.xml index a9d5cb8..fcdf0a2 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,31 @@ 5.8.1 test + + org.junit.jupiter + junit-jupiter + 5.9.3 + test + + + + org.powermock + powermock-module-junit4 + 2.0.9 + test + + + org.powermock + powermock-api-mockito2 + 2.0.9 + test + + + org.powermock + powermock-module-junit4-rule + 2.0.9 + test + org.apache.maven.plugins @@ -28,6 +53,14 @@ 3.6.0 + + org.mockito + mockito-core + 5.15.2 + test + + + diff --git a/src/main/java/de/deversmann/facade/Facade.java b/src/main/java/de/deversmann/facade/Facade.java index 2fc2428..0c95a1a 100644 --- a/src/main/java/de/deversmann/facade/Facade.java +++ b/src/main/java/de/deversmann/facade/Facade.java @@ -78,10 +78,6 @@ public class Facade { return highscoreManager.getAverageTime(currentPuzzleName); } - public String getCurrentPuzzleName() { - return currentPuzzleName; - } - public void saveCurrentPuzzleHighscoreToFile() throws IOException { if (currentPuzzleName == null) return; String base = currentPuzzleName.replaceAll(".*/", ""); diff --git a/src/main/java/de/deversmann/gui/GameView.java b/src/main/java/de/deversmann/gui/GameView.java index 66a14cd..34f38df 100644 --- a/src/main/java/de/deversmann/gui/GameView.java +++ b/src/main/java/de/deversmann/gui/GameView.java @@ -13,16 +13,16 @@ import java.util.Set; public class GameView extends JFrame { private final Facade facade; - private SpielfeldGUI spielfeldGUI; + SpielfeldGUI spielfeldGUI; - private JComboBox cbSpielfelder; - private JButton btnLoad; - private JButton btnRandom; - private JButton btnCheckSolution; - private JButton btnShowSolution; - private JButton btnReset; - private JLabel timeLabel; - private Timer timer; + JComboBox cbSpielfelder; + JButton btnLoad; + JButton btnRandom; + JButton btnCheckSolution; + JButton btnShowSolution; + JButton btnReset; + JLabel timeLabel; + Timer timer; private final String[] spielfelder = { "4x4.csv", @@ -98,7 +98,7 @@ public class GameView extends JFrame { timer.start(); } - private void ladeSpielfeld(String pfad) { + void ladeSpielfeld(String pfad) { try { facade.ladeSpielfeld(pfad); facade.loadCurrentPuzzleHighscoreFromFile(); @@ -117,7 +117,7 @@ public class GameView extends JFrame { } } - private void checkSolution() { + void checkSolution() { var blackCells = spielfeldGUI.getBlackCells(); var solutionCoordinates = facade.getLoesungsKoordinaten(); if (solutionCoordinates == null) { @@ -201,7 +201,7 @@ public class GameView extends JFrame { JOptionPane.showMessageDialog(this, sb.toString(), "Ergebnis", JOptionPane.INFORMATION_MESSAGE); } - private boolean containsCell(List blackCells, String sol) { + boolean containsCell(List blackCells, String sol) { String[] parts = sol.split(","); int rr = Integer.parseInt(parts[0]); int cc = Integer.parseInt(parts[1]); @@ -213,7 +213,7 @@ public class GameView extends JFrame { return false; } - private void showSolution() { + void showSolution() { var feld = facade.getAktuellesPuzzle(); if (feld == null) return; spielfeldGUI.updateGrid(feld); @@ -227,7 +227,7 @@ public class GameView extends JFrame { spielfeldGUI.setAllNonBlackToWhite(); } - private void resetField() { + void resetField() { var feld = facade.getAktuellesPuzzle(); if (feld != null) { spielfeldGUI.updateGrid(feld); diff --git a/src/test/java/de/deversmann/domain/CSVReaderTest.java b/src/test/java/de/deversmann/domain/CSVReaderTest.java new file mode 100644 index 0000000..99b2e82 --- /dev/null +++ b/src/test/java/de/deversmann/domain/CSVReaderTest.java @@ -0,0 +1,80 @@ +package de.deversmann.domain; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.*; + +class CSVReaderTest { + + @Test + void testReadPuzzleWithSolutionRealData() throws IOException { + String csvContent = """ + 4,5,6,7 + 8,9,10,11 + 12,13,14,15 + 16,17,18,19 + //Lösung + 2,2 + 3,3 + """; + Path tempFile = Files.createTempFile("realPuzzle", ".csv"); + Files.writeString(tempFile, csvContent); + + CSVReader reader = new CSVReader(); + PuzzleData data = reader.readPuzzleWithSolution(tempFile.toString()); + + int[][] expectedPuzzle = { + {4, 5, 6, 7}, + {8, 9, 10, 11}, + {12, 13, 14, 15}, + {16, 17, 18, 19} + }; + assertArrayEquals(expectedPuzzle, data.getPuzzle()); + assertEquals(2, data.getSolutionCoordinates().size()); + assertArrayEquals(new int[]{2, 2}, data.getSolutionCoordinates().get(0)); + assertArrayEquals(new int[]{3, 3}, data.getSolutionCoordinates().get(1)); + + Files.delete(tempFile); + } + + @Test + void testReadPuzzleWithInvalidData() throws IOException { + String csvContent = """ + 1,2,3 + 4,5 + //Lösung + a,b + """; + Path tempFile = Files.createTempFile("invalidPuzzle", ".csv"); + Files.writeString(tempFile, csvContent); + + CSVReader reader = new CSVReader(); + + assertThrows(NumberFormatException.class, () -> reader.readPuzzleWithSolution(tempFile.toString())); + + Files.delete(tempFile); + } + + @Test + void testReadPuzzleWithoutSolution() throws IOException { + String csvContent = """ + 1,2,3 + 4,5,6 + 7,8,9 + """; + Path tempFile = Files.createTempFile("noSolutionPuzzle", ".csv"); + Files.writeString(tempFile, csvContent); + + CSVReader reader = new CSVReader(); + PuzzleData data = reader.readPuzzleWithSolution(tempFile.toString()); + + assertNotNull(data.getPuzzle()); + assertTrue(data.getSolutionCoordinates().isEmpty()); + + Files.delete(tempFile); + } +} \ No newline at end of file diff --git a/src/test/java/de/deversmann/domain/HighscoreManagerTest.java b/src/test/java/de/deversmann/domain/HighscoreManagerTest.java new file mode 100644 index 0000000..aa9a371 --- /dev/null +++ b/src/test/java/de/deversmann/domain/HighscoreManagerTest.java @@ -0,0 +1,63 @@ +package de.deversmann.domain; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class HighscoreManagerTest { + + @Test + void testAddAndRetrieveHighscores() { + HighscoreManager manager = new HighscoreManager(); + manager.addHighscore("Puzzle1", "Alice", 120, 2); + + List highscores = manager.getHighscores("Puzzle1"); + + assertEquals(1, highscores.size()); + assertEquals("Alice", highscores.get(0).getPlayerName()); + assertEquals(120, highscores.get(0).getTimeSeconds()); + assertEquals(2, highscores.get(0).getErrorCount()); + } + + @Test + void testSaveAndLoadHighscores() throws IOException { + HighscoreManager manager = new HighscoreManager(); + manager.addHighscore("Puzzle1", "Alice", 120, 2); + + Path tempFile = Files.createTempFile("testHighscores", ".txt"); + manager.saveSinglePuzzle("Puzzle1", tempFile.toString()); + + HighscoreManager newManager = new HighscoreManager(); + newManager.loadSinglePuzzle("Puzzle1", tempFile.toString()); + + List highscores = newManager.getHighscores("Puzzle1"); + + assertEquals(1, highscores.size()); + assertEquals("Alice", highscores.get(0).getPlayerName()); + assertEquals(120, highscores.get(0).getTimeSeconds()); + assertEquals(2, highscores.get(0).getErrorCount()); + + Files.delete(tempFile); + } + + @Test + void testGetAverageTimeNoEntries() { + HighscoreManager manager = new HighscoreManager(); + double avgTime = manager.getAverageTime("Puzzle1"); + + assertEquals(-1, avgTime, "Average time should be -1 for no entries."); + } + + @Test + void testLoadSinglePuzzleFileNotFound() { + HighscoreManager manager = new HighscoreManager(); + + assertDoesNotThrow(() -> manager.loadSinglePuzzle("Puzzle1", "nonexistent.txt")); + assertTrue(manager.getHighscores("Puzzle1").isEmpty()); + } +} \ No newline at end of file diff --git a/src/test/java/de/deversmann/domain/PuzzleDataTest.java b/src/test/java/de/deversmann/domain/PuzzleDataTest.java new file mode 100644 index 0000000..c10c798 --- /dev/null +++ b/src/test/java/de/deversmann/domain/PuzzleDataTest.java @@ -0,0 +1,30 @@ +package de.deversmann.domain; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class PuzzleDataTest { + + @Test + void testGetPuzzle() { + int[][] puzzle = {{1, 2}, {3, 4}}; + List solution = List.of(new int[]{1, 1}, new int[]{2, 2}); + + PuzzleData data = new PuzzleData(puzzle, solution); + + assertArrayEquals(puzzle, data.getPuzzle()); + } + + @Test + void testGetSolutionCoordinates() { + int[][] puzzle = {{1, 2}, {3, 4}}; + List solution = List.of(new int[]{1, 1}, new int[]{2, 2}); + + PuzzleData data = new PuzzleData(puzzle, solution); + + assertEquals(solution, data.getSolutionCoordinates()); + } +} \ No newline at end of file diff --git a/src/test/java/de/deversmann/facade/FacadeTest.java b/src/test/java/de/deversmann/facade/FacadeTest.java new file mode 100644 index 0000000..741925b --- /dev/null +++ b/src/test/java/de/deversmann/facade/FacadeTest.java @@ -0,0 +1,153 @@ +package de.deversmann.facade; + +import de.deversmann.domain.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; + +import java.io.IOException; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class FacadeTest { + + private CSVReader csvReaderMock; + private HighscoreManager highscoreManagerMock; + private Zeiterfassung zeiterfassungMock; + private Facade facade; + + @BeforeEach + void setup() throws Exception { + facade = new Facade(); + + csvReaderMock = mock(CSVReader.class); + highscoreManagerMock = mock(HighscoreManager.class); + zeiterfassungMock = mock(Zeiterfassung.class); + + PowerMockito.field(Facade.class, "csvReader").set(facade, csvReaderMock); + PowerMockito.field(Facade.class, "highscoreManager").set(facade, highscoreManagerMock); + PowerMockito.field(Facade.class, "zeiterfassung").set(facade, zeiterfassungMock); + } + + @Test + void testLadeSpielfeldMitEchtenDaten() throws IOException { + PuzzleData puzzleData = new PuzzleData(new int[][]{{1, 2, 3, 4}}, List.of(new int[]{1, 1})); + when(csvReaderMock.readPuzzleWithSolution(anyString())).thenReturn(puzzleData); + + facade.ladeSpielfeld("test.csv"); + + assertNotNull(facade.getAktuellesPuzzle()); + assertEquals(1, facade.getLoesungsKoordinaten().size()); + } + + @Test + void testStopZeiterfassung() { + when(zeiterfassungMock.getElapsedTimeInSeconds()).thenReturn(42L); + + long elapsedTime = facade.stopZeiterfassung(); + + verify(zeiterfassungMock, times(1)).stop(); + assertEquals(42L, elapsedTime); + } + + @Test + void testGetElapsedTimeSoFar() { + when(zeiterfassungMock.getElapsedTimeInSeconds()).thenReturn(15L); + + long elapsedTime = facade.getElapsedTimeSoFar(); + + verify(zeiterfassungMock, times(1)).getElapsedTimeInSeconds(); + assertEquals(15L, elapsedTime); + } + + @Test + void testIncrementErrorCount() { + assertEquals(0, facade.getCurrentErrorCount()); + + facade.incrementErrorCount(); + + assertEquals(1, facade.getCurrentErrorCount()); + } + + @Test + void testAddHighscoreForCurrentPuzzle() throws IOException { + facade.ladeSpielfeld("test.csv"); + + facade.addHighscoreForCurrentPuzzle("Player1", 100L, 3); + + verify(highscoreManagerMock, times(1)) + .addHighscore(eq("test.csv"), eq("Player1"), eq(100L), eq(3)); + } + + @Test + void testGetHighscoresForCurrentPuzzle() throws IOException { + when(highscoreManagerMock.getHighscores("test.csv")) + .thenReturn(List.of(new HighscoreEntry("Player1", 100L, 2))); + + facade.ladeSpielfeld("test.csv"); + List highscores = facade.getHighscoresForCurrentPuzzle(); + + assertEquals(1, highscores.size()); + assertEquals("Player1", highscores.get(0).getPlayerName()); + } + + @Test + void testGetAverageTimeForCurrentPuzzle() throws IOException { + when(highscoreManagerMock.getAverageTime("test.csv")).thenReturn(75.0); + + facade.ladeSpielfeld("test.csv"); + double avgTime = facade.getAverageTimeForCurrentPuzzle(); + + assertEquals(75.0, avgTime, 0.01); + } + + @Test + void testSaveCurrentPuzzleHighscoreToFile() throws IOException { + facade.ladeSpielfeld("test.csv"); + + facade.saveCurrentPuzzleHighscoreToFile(); + + verify(highscoreManagerMock, times(1)) + .saveSinglePuzzle(eq("test.csv"), anyString()); + } + + @Test + void testLoadCurrentPuzzleHighscoreFromFile() throws IOException { + facade.ladeSpielfeld("test.csv"); + + facade.loadCurrentPuzzleHighscoreFromFile(); + + verify(highscoreManagerMock, times(1)) + .loadSinglePuzzle(eq("test.csv"), anyString()); + } + + @Test + void testGetAktuellesPuzzle() throws IOException { + PuzzleData puzzleData = new PuzzleData(new int[][]{{1, 2, 3, 4}}, List.of()); + when(csvReaderMock.readPuzzleWithSolution(anyString())).thenReturn(puzzleData); + + facade.ladeSpielfeld("test.csv"); + + int[][] puzzle = facade.getAktuellesPuzzle(); + + assertNotNull(puzzle); + assertEquals(4, puzzle[0].length); + } + + @Test + void testGetLoesungsKoordinaten() throws IOException { + PuzzleData puzzleData = new PuzzleData(new int[][]{{1, 2}}, List.of(new int[]{1, 1})); + when(csvReaderMock.readPuzzleWithSolution(anyString())).thenReturn(puzzleData); + + facade.ladeSpielfeld("test.csv"); + + List solutionCoordinates = facade.getLoesungsKoordinaten(); + + assertNotNull(solutionCoordinates); + assertEquals(1, solutionCoordinates.size()); + assertArrayEquals(new int[]{1, 1}, solutionCoordinates.get(0)); + } +} \ No newline at end of file