From 085dc2a84b533dbc35e467b95f4481af07064674 Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 20:50:16 -0400 Subject: [PATCH 01/12] Update JDK version from 21 to 26 in workflow --- .github/workflows/maven.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 1a6afc6..3e5f858 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -21,10 +21,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 21 + - name: Set up JDK 26 uses: actions/setup-java@v4 with: - java-version: '21' + java-version: '26' distribution: 'temurin' cache: maven - name: Build with Maven From 115b1857402c04792878e807bdfcac0117211cdb Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 21:06:11 -0400 Subject: [PATCH 02/12] Enable headless mode for Maven build Added headless mode for Maven build to support GUI-less environments. --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 3e5f858..58b5808 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -28,7 +28,7 @@ jobs: distribution: 'temurin' cache: maven - name: Build with Maven - run: mvn -B package --file pom.xml + run: mvn -B package --file pom.xml -Djava.awt.headless=true # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive - name: Update dependency graph From f18ae7c5b609d43dc00bc5680bdfba7611f0a112 Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 21:09:46 -0400 Subject: [PATCH 03/12] Set headless mode for Gameboard tests --- src/test/java/Gameboard/GameboardMouseListenerTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/Gameboard/GameboardMouseListenerTest.java b/src/test/java/Gameboard/GameboardMouseListenerTest.java index fdd615c..c800c8c 100644 --- a/src/test/java/Gameboard/GameboardMouseListenerTest.java +++ b/src/test/java/Gameboard/GameboardMouseListenerTest.java @@ -35,6 +35,7 @@ public GameboardMouseListenerTest() { @BeforeAll public static void setUpClass() { + System.setProperty("java.awt.headless", "true"); GameBoard.setBOARD_COLUMNS(8); GameBoard.setBOARD_ROWS(8); board = new GameBoard(); From 1801421a7d99d9bf5a5d87871cf7653ca423f97e Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 22:08:55 -0400 Subject: [PATCH 04/12] Refactor headless property setting in tests Removed redundant headless property setting in setUpClass method. --- src/test/java/Gameboard/GameboardMouseListenerTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/test/java/Gameboard/GameboardMouseListenerTest.java b/src/test/java/Gameboard/GameboardMouseListenerTest.java index c800c8c..8b0e12d 100644 --- a/src/test/java/Gameboard/GameboardMouseListenerTest.java +++ b/src/test/java/Gameboard/GameboardMouseListenerTest.java @@ -19,6 +19,12 @@ * @author robertpalmer */ public class GameboardMouseListenerTest { + + static { + // Set during class loading, before setup can construct any AWT component. + System.setProperty("java.awt.headless", "true"); + } + public static GameBoard board; public static Piece gamePiece; public static Piece moveCopy; @@ -35,7 +41,7 @@ public GameboardMouseListenerTest() { @BeforeAll public static void setUpClass() { - System.setProperty("java.awt.headless", "true"); + // System.setProperty("java.awt.headless", "true"); GameBoard.setBOARD_COLUMNS(8); GameBoard.setBOARD_ROWS(8); board = new GameBoard(); From 1a6d56d00d936f2a0e3f9766acb4678c2306d971 Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 22:15:30 -0400 Subject: [PATCH 05/12] Simplify GameboardMouseListenerTest structure Refactor GameboardMouseListenerTest for clarity and efficiency. --- .../Gameboard/GameboardMouseListenerTest.java | 264 ++++-------------- 1 file changed, 59 insertions(+), 205 deletions(-) diff --git a/src/test/java/Gameboard/GameboardMouseListenerTest.java b/src/test/java/Gameboard/GameboardMouseListenerTest.java index 8b0e12d..f6e2f62 100644 --- a/src/test/java/Gameboard/GameboardMouseListenerTest.java +++ b/src/test/java/Gameboard/GameboardMouseListenerTest.java @@ -1,243 +1,97 @@ -/* - * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license - * Click nbfs://nbhost/SystemFileSystem/Templates/UnitTests/JUnit5TestClass.java to edit this template - */ package Gameboard; import com.mycompany.javacheckers.Game; import com.mycompany.javacheckers.Player; import java.awt.event.MouseEvent; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; /** + * Headless tests for {@link GameboardMouseListener}. * - * @author robertpalmer + *

These tests deliberately create only lightweight AWT event objects; they + * do not display a window, create a Robot, or access a screen device.

*/ -public class GameboardMouseListenerTest { +class GameboardMouseListenerTest { static { // Set during class loading, before setup can construct any AWT component. System.setProperty("java.awt.headless", "true"); } - - public static GameBoard board; - public static Piece gamePiece; - public static Piece moveCopy; - public static int squareX; - public static int squareY; - public static int mouseX; - public static int mouseY; - public static GameboardMouseListener instance; - public static BoardSquare[][] dataBoard; - public static Game gameObject; - - public GameboardMouseListenerTest() { - } - + + private static GameBoard board; + private static GameboardMouseListener listener; + @BeforeAll - public static void setUpClass() { - // System.setProperty("java.awt.headless", "true"); + static void setUpClass() { GameBoard.setBOARD_COLUMNS(8); GameBoard.setBOARD_ROWS(8); board = new GameBoard(); - dataBoard = new BoardSquare[8][8]; - gameObject = new Game(); - gameObject.setUserColor("Red"); - gameObject.setUserPlayer(new Player()); - instance = new GameboardMouseListener(board, gameObject); - gamePiece = new Piece(); - moveCopy = new Piece(); - board.setGameBoard(dataBoard); board.setSquareWidth(10); - board.setBoardSquare(4, 4, new BoardSquare(null, false, null)); - board.getBoardSquare(4, 4).setCurrentPiece(gamePiece); - board.getBoardSquare(4, 4).setHasPiece(true); - board.setGameBoard(dataBoard); - instance.setBoard(board); - - - - - - - } - - @AfterAll - public static void tearDownClass() { - } - - @BeforeEach - public void setUp() { - } - - @AfterEach - public void tearDown() { - } - /** - * Test of getSquareX method, of class GameboardMouseListener. - */ - @Test - public void testGetSquareX() { - System.out.println("getSquareX"); - - int expResult = 0; - int result = instance.getSquareX(); - assertEquals(expResult, result); - // TODO review the generated test code and remove the default call to fail. - //fail("The test case is a prototype."); - } + for (int column = 0; column < 8; column++) { + for (int row = 0; row < 8; row++) { + board.setBoardSquare( + column, + row, + new BoardSquare(null, false, null)); + } + } - /** - * Test of setSquareX method, of class GameboardMouseListener. - */ - @Test - public void testSetSquareX() { - System.out.println("setSquareX"); - int squareX = 0; - //GameboardMouseListener instance = new GameboardMouseListener(); - instance.setSquareX(squareX); - assertEquals(squareX, instance.getSquareX()); - // TODO review the generated test code and remove the default call to fail. - //` fail("The test case is a prototype."); + Game game = new Game(); + game.setUserColor("Red"); + game.setUserPlayer(new Player()); + listener = new GameboardMouseListener(board, game); } - /** - * Test of getSquareY method, of class GameboardMouseListener. - */ - @Test - public void testGetSquareY() { - System.out.println("getSquareY"); - //GameboardMouseListener instance = new GameboardMouseListener(); - int expResult = 0; - int result = instance.getSquareY(); - assertEquals(expResult, result); - // TODO review the generated test code and remove the default call to fail. - //fail("The test case is a prototype."); - } - - /** - * Test of setSquareY method, of class GameboardMouseListener. - */ - @Test - public void testSetSquareY() { - System.out.println("setSquareY"); - int squareY = 0; - //GameboardMouseListener instance = new GameboardMouseListener(); - instance.setSquareY(squareY); - // TODO review the generated test code and remove the default call to fail. - //fail("The test case is a prototype."); - } - - /** - * Test of mouseClicked method, of class GameboardMouseListener. - */ - @Test - public void testMouseClicked() { - System.out.println("mouseClicked"); - MouseEvent e = new MouseEvent(board, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, 45, 45,1,false); - //MouseEvent e = null; - //GameboardMouseListener instance = new GameboardMouseListener(); - instance.setCheckersGame(new Game()); - instance.mouseClicked(e); - // TODO review the generated test code and remove the default call to fail. - //fail("The test case is a prototype."); - } - - /** - * Test of mousePressed method, of class GameboardMouseListener. - */ - @Test - public void testMousePressed() { - System.out.println("mousePressed"); - MouseEvent e = new MouseEvent(board, MouseEvent.MOUSE_PRESSED, System.currentTimeMillis(), 0, 45, 45,1,false); - //GameboardMouseListener instance = new GameboardMouseListener(); - instance.setCheckersGame(new Game()); - instance.mousePressed(e); - // TODO review the generated test code and remove the default call to fail. - //fail("The test case is a prototype."); + @AfterAll + static void tearDownClass() { + listener = null; + board = null; } - /** - * Test of mouseReleased method, of class GameboardMouseListener. - */ @Test - public void testMouseReleased() { - System.out.println("mouseReleased"); - MouseEvent e = new MouseEvent(board, MouseEvent.MOUSE_RELEASED, System.currentTimeMillis(), 0, 45, 45,1,false); - //GameboardMouseListener instance = new GameboardMouseListener(); - instance.mouseReleased(e); - // TODO review the generated test code and remove the default call to fail. - //fail("The test case is a prototype."); - } + void squareCoordinatesCanBeReadAndWritten() { + listener.setSquareX(3); + listener.setSquareY(5); -// /** -// * Test of mouseEntered method, of class GameboardMouseListener. -// */ -// @Test -// public void testMouseEntered() { -// System.out.println("mouseEntered"); -// MouseEvent e = null; -// GameboardMouseListener instance = new GameboardMouseListener(); -// instance.mouseEntered(e); -// // TODO review the generated test code and remove the default call to fail. -// //fail("The test case is a prototype."); -// } - - /** - * Test of mouseExited method, of class GameboardMouseListener. - */ - @Test - public void testMouseExited() { - System.out.println("mouseExited"); - MouseEvent e = new MouseEvent(board, MouseEvent.MOUSE_EXITED, System.currentTimeMillis(), 0, 45, 45,1,false); - //GameboardMouseListener instance = new GameboardMouseListener(); - instance.mouseExited(e); - // TODO review the generated test code and remove the default call to fail. - //fail("The test case is a prototype."); - } - - /** - * Test of mouseDragged method, of class GameboardMouseListener. - */ - @Test - public void testMouseDragged() { - System.out.println("mouseDragged"); - MouseEvent e = new MouseEvent(board, MouseEvent.MOUSE_DRAGGED, System.currentTimeMillis(), 0, 45, 45,1,false); //GameboardMouseListener instance = new GameboardMouseListener(); - instance.mouseDragged(e); - // TODO review the generated test code and remove the default call to fail. - //fail("The test case is a prototype."); + assertEquals(3, listener.getSquareX()); + assertEquals(5, listener.getSquareY()); } - /** - * Test of mouseMoved method, of class GameboardMouseListener. - */ @Test - public void testMouseMoved() { - System.out.println("mouseMoved"); - MouseEvent e = new MouseEvent(board, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 45, 45,1,false); - //GameboardMouseListener instance = new GameboardMouseListener(); - instance.mouseMoved(e); - // TODO review the generated test code and remove the default call to fail. - //fail("The test case is a prototype."); + void boardCanBeReplaced() { + assertDoesNotThrow(() -> listener.setBoard(board)); } - /** - * Test of setBoard method, of class GameboardMouseListener. - */ @Test - public void testSetBoard() { - System.out.println("setBoard"); - //GameBoard board = null; - //GameboardMouseListener instance = new GameboardMouseListener(); - instance.setBoard(board); - // TODO review the generated test code and remove the default call to fail. - //fail("The test case is a prototype."); + void mouseCallbacksDoNotRequireADisplay() { + Game emptyGame = new Game(); + emptyGame.setUserColor("Red"); + emptyGame.setUserPlayer(new Player()); + listener.setCheckersGame(emptyGame); + + assertDoesNotThrow(() -> listener.mouseClicked(mouseEvent(MouseEvent.MOUSE_CLICKED))); + assertDoesNotThrow(() -> listener.mousePressed(mouseEvent(MouseEvent.MOUSE_PRESSED))); + assertDoesNotThrow(() -> listener.mouseReleased(mouseEvent(MouseEvent.MOUSE_RELEASED))); + assertDoesNotThrow(() -> listener.mouseExited(mouseEvent(MouseEvent.MOUSE_EXITED))); + assertDoesNotThrow(() -> listener.mouseDragged(mouseEvent(MouseEvent.MOUSE_DRAGGED))); + assertDoesNotThrow(() -> listener.mouseMoved(mouseEvent(MouseEvent.MOUSE_MOVED))); + } + + private static MouseEvent mouseEvent(int eventType) { + return new MouseEvent( + board, + eventType, + System.currentTimeMillis(), + 0, + 45, + 45, + 1, + false); } - } From 1d9e8649bd11dea50ce59b74c4b16a55c033778b Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 22:22:10 -0400 Subject: [PATCH 06/12] Refactor board square initialization in tests --- .../java/Gameboard/GameboardMouseListenerTest.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/test/java/Gameboard/GameboardMouseListenerTest.java b/src/test/java/Gameboard/GameboardMouseListenerTest.java index f6e2f62..73f3cff 100644 --- a/src/test/java/Gameboard/GameboardMouseListenerTest.java +++ b/src/test/java/Gameboard/GameboardMouseListenerTest.java @@ -33,14 +33,13 @@ static void setUpClass() { board = new GameBoard(); board.setSquareWidth(10); - for (int column = 0; column < 8; column++) { - for (int row = 0; row < 8; row++) { - board.setBoardSquare( - column, - row, - new BoardSquare(null, false, null)); +BoardSquare[][] squares = new BoardSquare[8][8]; + for (int column = 0; column < squares.length; column++) { + for (int row = 0; row < squares[column].length; row++) { + squares[column][row] = new BoardSquare(null, false, null); } } + setBoardData(board, squares); Game game = new Game(); game.setUserColor("Red"); From 76b0944024b6d7edca41eb8a9a69f6c687cc984f Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 22:24:11 -0400 Subject: [PATCH 07/12] Implement setBoardData method in GameboardMouseListenerTest Add a method to set board data for testing purposes. --- src/test/java/Gameboard/GameboardMouseListenerTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/Gameboard/GameboardMouseListenerTest.java b/src/test/java/Gameboard/GameboardMouseListenerTest.java index 73f3cff..c44fac1 100644 --- a/src/test/java/Gameboard/GameboardMouseListenerTest.java +++ b/src/test/java/Gameboard/GameboardMouseListenerTest.java @@ -82,6 +82,15 @@ void mouseCallbacksDoNotRequireADisplay() { assertDoesNotThrow(() -> listener.mouseMoved(mouseEvent(MouseEvent.MOUSE_MOVED))); } + private static void setBoardData(GameBoard target, BoardSquare[][] squares) { + try { + Field gameDataBoard = GameBoard.class.getDeclaredField("gameDataBoard"); + gameDataBoard.setAccessible(true); + gameDataBoard.set(target, squares); + } catch (ReflectiveOperationException exception) { + throw new IllegalStateException("Unable to initialize the test board", exception); + } +} private static MouseEvent mouseEvent(int eventType) { return new MouseEvent( board, From fce38bae5dfa349e955fa27a505a94bbe1128700 Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 22:24:57 -0400 Subject: [PATCH 08/12] Refactor GameboardMouseListenerTest for clarity --- .../Gameboard/GameboardMouseListenerTest.java | 188 +++++++++--------- 1 file changed, 97 insertions(+), 91 deletions(-) diff --git a/src/test/java/Gameboard/GameboardMouseListenerTest.java b/src/test/java/Gameboard/GameboardMouseListenerTest.java index c44fac1..464dcc9 100644 --- a/src/test/java/Gameboard/GameboardMouseListenerTest.java +++ b/src/test/java/Gameboard/GameboardMouseListenerTest.java @@ -1,105 +1,111 @@ -package Gameboard; - + package Gameboard; + import com.mycompany.javacheckers.Game; import com.mycompany.javacheckers.Player; import java.awt.event.MouseEvent; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * Headless tests for {@link GameboardMouseListener}. - * - *

These tests deliberately create only lightweight AWT event objects; they - * do not display a window, create a Robot, or access a screen device.

- */ -class GameboardMouseListenerTest { - - static { - // Set during class loading, before setup can construct any AWT component. - System.setProperty("java.awt.headless", "true"); - } - - private static GameBoard board; - private static GameboardMouseListener listener; - - @BeforeAll - static void setUpClass() { - GameBoard.setBOARD_COLUMNS(8); - GameBoard.setBOARD_ROWS(8); - board = new GameBoard(); - board.setSquareWidth(10); - -BoardSquare[][] squares = new BoardSquare[8][8]; +import java.lang.reflect.Field; + import org.junit.jupiter.api.AfterAll; + import org.junit.jupiter.api.BeforeAll; + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + import static org.junit.jupiter.api.Assertions.assertEquals; + + /** + * Headless tests for {@link GameboardMouseListener}. + * + *

These tests deliberately create only lightweight AWT event objects; they + * do not display a window, create a Robot, or access a screen device.

+ */ + class GameboardMouseListenerTest { + + static { + // Set during class loading, before setup can construct any AWT component. + System.setProperty("java.awt.headless", "true"); + } + + private static GameBoard board; + private static GameboardMouseListener listener; + + @BeforeAll + static void setUpClass() { + GameBoard.setBOARD_COLUMNS(8); + GameBoard.setBOARD_ROWS(8); + board = new GameBoard(); + board.setSquareWidth(10); + + BoardSquare[][] squares = new BoardSquare[8][8]; for (int column = 0; column < squares.length; column++) { for (int row = 0; row < squares[column].length; row++) { squares[column][row] = new BoardSquare(null, false, null); } } setBoardData(board, squares); - - Game game = new Game(); - game.setUserColor("Red"); - game.setUserPlayer(new Player()); - listener = new GameboardMouseListener(board, game); - } - - @AfterAll - static void tearDownClass() { - listener = null; - board = null; - } - - @Test - void squareCoordinatesCanBeReadAndWritten() { - listener.setSquareX(3); - listener.setSquareY(5); - - assertEquals(3, listener.getSquareX()); - assertEquals(5, listener.getSquareY()); - } - - @Test - void boardCanBeReplaced() { - assertDoesNotThrow(() -> listener.setBoard(board)); - } - - @Test - void mouseCallbacksDoNotRequireADisplay() { - Game emptyGame = new Game(); - emptyGame.setUserColor("Red"); - emptyGame.setUserPlayer(new Player()); - listener.setCheckersGame(emptyGame); - - assertDoesNotThrow(() -> listener.mouseClicked(mouseEvent(MouseEvent.MOUSE_CLICKED))); - assertDoesNotThrow(() -> listener.mousePressed(mouseEvent(MouseEvent.MOUSE_PRESSED))); - assertDoesNotThrow(() -> listener.mouseReleased(mouseEvent(MouseEvent.MOUSE_RELEASED))); - assertDoesNotThrow(() -> listener.mouseExited(mouseEvent(MouseEvent.MOUSE_EXITED))); - assertDoesNotThrow(() -> listener.mouseDragged(mouseEvent(MouseEvent.MOUSE_DRAGGED))); - assertDoesNotThrow(() -> listener.mouseMoved(mouseEvent(MouseEvent.MOUSE_MOVED))); + + Game game = new Game(); + game.setUserColor("Red"); + game.setUserPlayer(new Player()); + listener = new GameboardMouseListener(board, game); + } + + @AfterAll + static void tearDownClass() { + listener = null; + board = null; + } + + @Test + void squareCoordinatesCanBeReadAndWritten() { + listener.setSquareX(3); + listener.setSquareY(5); + + assertEquals(3, listener.getSquareX()); + assertEquals(5, listener.getSquareY()); + } + + @Test + void boardCanBeReplaced() { + assertDoesNotThrow(() -> listener.setBoard(board)); + } + + @Test + void mouseCallbacksDoNotRequireADisplay() { + Game emptyGame = new Game(); + emptyGame.setUserColor("Red"); + emptyGame.setUserPlayer(new Player()); + listener.setCheckersGame(emptyGame); + + assertDoesNotThrow(() -> listener.mouseClicked(mouseEvent(MouseEvent.MOUSE_CLICKED))); + assertDoesNotThrow(() -> listener.mousePressed(mouseEvent(MouseEvent.MOUSE_PRESSED))); + assertDoesNotThrow(() -> listener.mouseReleased(mouseEvent(MouseEvent.MOUSE_RELEASED))); + assertDoesNotThrow(() -> listener.mouseExited(mouseEvent(MouseEvent.MOUSE_EXITED))); + assertDoesNotThrow(() -> listener.mouseDragged(mouseEvent(MouseEvent.MOUSE_DRAGGED))); + assertDoesNotThrow(() -> listener.mouseMoved(mouseEvent(MouseEvent.MOUSE_MOVED))); + } + + private static MouseEvent mouseEvent(int eventType) { + return new MouseEvent( + board, + eventType, + System.currentTimeMillis(), + 0, + 45, + 45, + 1, + false); } + /** + * {@code setGameBoard} creates display resources, so it cannot be used by + * a headless unit test. Populate the listener's required data model only. + */ private static void setBoardData(GameBoard target, BoardSquare[][] squares) { - try { - Field gameDataBoard = GameBoard.class.getDeclaredField("gameDataBoard"); - gameDataBoard.setAccessible(true); - gameDataBoard.set(target, squares); - } catch (ReflectiveOperationException exception) { - throw new IllegalStateException("Unable to initialize the test board", exception); - } -} - private static MouseEvent mouseEvent(int eventType) { - return new MouseEvent( - board, - eventType, - System.currentTimeMillis(), - 0, - 45, - 45, - 1, - false); + try { + Field gameDataBoard = GameBoard.class.getDeclaredField("gameDataBoard"); + gameDataBoard.setAccessible(true); + gameDataBoard.set(target, squares); + } catch (ReflectiveOperationException exception) { + throw new IllegalStateException("Unable to initialize the test board", exception); + } } } From 71170620aa208eb12423d7b92afd5185271a2595 Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 22:27:47 -0400 Subject: [PATCH 09/12] Refactor GameboardMouseListenerTest for headless mode --- .../Gameboard/GameboardMouseListenerTest.java | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/test/java/Gameboard/GameboardMouseListenerTest.java b/src/test/java/Gameboard/GameboardMouseListenerTest.java index 464dcc9..e73dd43 100644 --- a/src/test/java/Gameboard/GameboardMouseListenerTest.java +++ b/src/test/java/Gameboard/GameboardMouseListenerTest.java @@ -1,9 +1,10 @@ package Gameboard; import com.mycompany.javacheckers.Game; -import com.mycompany.javacheckers.Player; +import java.awt.GraphicsEnvironment; import java.awt.event.MouseEvent; import java.lang.reflect.Field; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -42,10 +43,10 @@ static void setUpClass() { } setBoardData(board, squares); - Game game = new Game(); - game.setUserColor("Red"); - game.setUserPlayer(new Player()); - listener = new GameboardMouseListener(board, game); + // Game extends JFrame, so creating it is impossible in a headless JVM. + // The constructor stores this reference for click/press game actions; + // it is not needed for the listener's data-only behavior. + listener = new GameboardMouseListener(board, null); } @AfterAll @@ -69,19 +70,24 @@ void boardCanBeReplaced() { } @Test - void mouseCallbacksDoNotRequireADisplay() { - Game emptyGame = new Game(); - emptyGame.setUserColor("Red"); - emptyGame.setUserPlayer(new Player()); - listener.setCheckersGame(emptyGame); - - assertDoesNotThrow(() -> listener.mouseClicked(mouseEvent(MouseEvent.MOUSE_CLICKED))); - assertDoesNotThrow(() -> listener.mousePressed(mouseEvent(MouseEvent.MOUSE_PRESSED))); - assertDoesNotThrow(() -> listener.mouseReleased(mouseEvent(MouseEvent.MOUSE_RELEASED))); - assertDoesNotThrow(() -> listener.mouseExited(mouseEvent(MouseEvent.MOUSE_EXITED))); - assertDoesNotThrow(() -> listener.mouseDragged(mouseEvent(MouseEvent.MOUSE_DRAGGED))); - assertDoesNotThrow(() -> listener.mouseMoved(mouseEvent(MouseEvent.MOUSE_MOVED))); - } + void nonInteractiveMouseCallbacksDoNotRequireADisplay() { + assertDoesNotThrow(() -> listener.mouseReleased(mouseEvent(MouseEvent.MOUSE_RELEASED))); + assertDoesNotThrow(() -> listener.mouseExited(mouseEvent(MouseEvent.MOUSE_EXITED))); + assertDoesNotThrow(() -> listener.mouseDragged(mouseEvent(MouseEvent.MOUSE_DRAGGED))); + assertDoesNotThrow(() -> listener.mouseMoved(mouseEvent(MouseEvent.MOUSE_MOVED))); + } + + @Test + void clickAndPressRequireAGraphicalGameWindow() { + Assumptions.assumeFalse( + GraphicsEnvironment.isHeadless(), + "Game extends JFrame and cannot be constructed headlessly"); + + Game game = new Game(); + listener.setCheckersGame(game); + assertDoesNotThrow(() -> listener.mouseClicked(mouseEvent(MouseEvent.MOUSE_CLICKED))); + assertDoesNotThrow(() -> listener.mousePressed(mouseEvent(MouseEvent.MOUSE_PRESSED))); + } private static MouseEvent mouseEvent(int eventType) { return new MouseEvent( From 16329323fb1d9ccf8f46338f3b088fc7f65390e8 Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 22:29:37 -0400 Subject: [PATCH 10/12] Refactor GameboardMouseListenerTest for clarity --- .../Gameboard/GameboardMouseListenerTest.java | 138 ++++++++---------- 1 file changed, 64 insertions(+), 74 deletions(-) diff --git a/src/test/java/Gameboard/GameboardMouseListenerTest.java b/src/test/java/Gameboard/GameboardMouseListenerTest.java index e73dd43..5883732 100644 --- a/src/test/java/Gameboard/GameboardMouseListenerTest.java +++ b/src/test/java/Gameboard/GameboardMouseListenerTest.java @@ -1,75 +1,68 @@ - package Gameboard; - +package Gameboard; + import com.mycompany.javacheckers.Game; import java.awt.GraphicsEnvironment; import java.awt.event.MouseEvent; import java.lang.reflect.Field; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assumptions; - import org.junit.jupiter.api.AfterAll; - import org.junit.jupiter.api.BeforeAll; - import org.junit.jupiter.api.Test; - - import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; - import static org.junit.jupiter.api.Assertions.assertEquals; - - /** - * Headless tests for {@link GameboardMouseListener}. - * - *

These tests deliberately create only lightweight AWT event objects; they - * do not display a window, create a Robot, or access a screen device.

- */ - class GameboardMouseListenerTest { - - static { - // Set during class loading, before setup can construct any AWT component. - System.setProperty("java.awt.headless", "true"); - } - - private static GameBoard board; - private static GameboardMouseListener listener; - - @BeforeAll - static void setUpClass() { - GameBoard.setBOARD_COLUMNS(8); - GameBoard.setBOARD_ROWS(8); - board = new GameBoard(); - board.setSquareWidth(10); - +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class GameboardMouseListenerTest { + + static { + System.setProperty("java.awt.headless", "true"); + } + + private static GameBoard board; + private static GameboardMouseListener listener; + + @BeforeAll + static void setUpClass() { + GameBoard.setBOARD_COLUMNS(8); + GameBoard.setBOARD_ROWS(8); + + board = new GameBoard(); + board.setSquareWidth(10); + BoardSquare[][] squares = new BoardSquare[8][8]; - for (int column = 0; column < squares.length; column++) { - for (int row = 0; row < squares[column].length; row++) { + for (int column = 0; column < 8; column++) { + for (int row = 0; row < 8; row++) { squares[column][row] = new BoardSquare(null, false, null); } } + setBoardData(board, squares); - - // Game extends JFrame, so creating it is impossible in a headless JVM. - // The constructor stores this reference for click/press game actions; - // it is not needed for the listener's data-only behavior. + + // Game extends JFrame and cannot be created headlessly. listener = new GameboardMouseListener(board, null); - } - - @AfterAll - static void tearDownClass() { - listener = null; - board = null; - } - - @Test - void squareCoordinatesCanBeReadAndWritten() { - listener.setSquareX(3); - listener.setSquareY(5); - - assertEquals(3, listener.getSquareX()); - assertEquals(5, listener.getSquareY()); - } - - @Test - void boardCanBeReplaced() { - assertDoesNotThrow(() -> listener.setBoard(board)); - } - - @Test + } + + @AfterAll + static void tearDownClass() { + listener = null; + board = null; + } + + @Test + void squareCoordinatesCanBeReadAndWritten() { + listener.setSquareX(3); + listener.setSquareY(5); + + assertEquals(3, listener.getSquareX()); + assertEquals(5, listener.getSquareY()); + } + + @Test + void boardCanBeReplaced() { + assertDoesNotThrow(() -> listener.setBoard(board)); + } + + @Test void nonInteractiveMouseCallbacksDoNotRequireADisplay() { assertDoesNotThrow(() -> listener.mouseReleased(mouseEvent(MouseEvent.MOUSE_RELEASED))); assertDoesNotThrow(() -> listener.mouseExited(mouseEvent(MouseEvent.MOUSE_EXITED))); @@ -85,26 +78,23 @@ void clickAndPressRequireAGraphicalGameWindow() { Game game = new Game(); listener.setCheckersGame(game); + assertDoesNotThrow(() -> listener.mouseClicked(mouseEvent(MouseEvent.MOUSE_CLICKED))); assertDoesNotThrow(() -> listener.mousePressed(mouseEvent(MouseEvent.MOUSE_PRESSED))); } - + private static MouseEvent mouseEvent(int eventType) { - return new MouseEvent( - board, - eventType, - System.currentTimeMillis(), - 0, - 45, - 45, - 1, + return new MouseEvent( + board, + eventType, + System.currentTimeMillis(), + 0, + 45, + 45, + 1, false); } - /** - * {@code setGameBoard} creates display resources, so it cannot be used by - * a headless unit test. Populate the listener's required data model only. - */ private static void setBoardData(GameBoard target, BoardSquare[][] squares) { try { Field gameDataBoard = GameBoard.class.getDeclaredField("gameDataBoard"); From af31b9292e393c8b3e3daf26804cfd11a8cec209 Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 22:34:32 -0400 Subject: [PATCH 11/12] Refactor GameboardMouseListenerTest for headless testing --- .../Gameboard/GameboardMouseListenerTest.java | 160 +++++++++++------- 1 file changed, 95 insertions(+), 65 deletions(-) diff --git a/src/test/java/Gameboard/GameboardMouseListenerTest.java b/src/test/java/Gameboard/GameboardMouseListenerTest.java index 5883732..a35e101 100644 --- a/src/test/java/Gameboard/GameboardMouseListenerTest.java +++ b/src/test/java/Gameboard/GameboardMouseListenerTest.java @@ -1,68 +1,74 @@ -package Gameboard; - + package Gameboard; + import com.mycompany.javacheckers.Game; +import com.mycompany.javacheckers.Player; import java.awt.GraphicsEnvironment; import java.awt.event.MouseEvent; import java.lang.reflect.Field; -import org.junit.jupiter.api.AfterAll; +import java.lang.reflect.Method; import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; - -class GameboardMouseListenerTest { - - static { - System.setProperty("java.awt.headless", "true"); - } - - private static GameBoard board; - private static GameboardMouseListener listener; - - @BeforeAll - static void setUpClass() { - GameBoard.setBOARD_COLUMNS(8); - GameBoard.setBOARD_ROWS(8); - - board = new GameBoard(); - board.setSquareWidth(10); - + import org.junit.jupiter.api.AfterAll; + import org.junit.jupiter.api.BeforeAll; + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + import static org.junit.jupiter.api.Assertions.assertEquals; + + /** + * Headless tests for {@link GameboardMouseListener}. + * + *

These tests deliberately create only lightweight AWT event objects; they + * do not display a window, create a Robot, or access a screen device.

+ */ + class GameboardMouseListenerTest { + + static { + // Set during class loading, before setup can construct any AWT component. + System.setProperty("java.awt.headless", "true"); + } + + private static GameBoard board; + private static GameboardMouseListener listener; + + @BeforeAll + static void setUpClass() { + GameBoard.setBOARD_COLUMNS(8); + GameBoard.setBOARD_ROWS(8); + board = new GameBoard(); + board.setSquareWidth(10); + BoardSquare[][] squares = new BoardSquare[8][8]; - for (int column = 0; column < 8; column++) { - for (int row = 0; row < 8; row++) { + for (int column = 0; column < squares.length; column++) { + for (int row = 0; row < squares[column].length; row++) { squares[column][row] = new BoardSquare(null, false, null); } } - setBoardData(board, squares); - - // Game extends JFrame and cannot be created headlessly. - listener = new GameboardMouseListener(board, null); - } - - @AfterAll - static void tearDownClass() { - listener = null; - board = null; - } - - @Test - void squareCoordinatesCanBeReadAndWritten() { - listener.setSquareX(3); - listener.setSquareY(5); - - assertEquals(3, listener.getSquareX()); - assertEquals(5, listener.getSquareY()); - } - - @Test - void boardCanBeReplaced() { - assertDoesNotThrow(() -> listener.setBoard(board)); - } - - @Test + + listener = new GameboardMouseListener(board, newHeadlessGame()); + } + + @AfterAll + static void tearDownClass() { + listener = null; + board = null; + } + + @Test + void squareCoordinatesCanBeReadAndWritten() { + listener.setSquareX(3); + listener.setSquareY(5); + + assertEquals(3, listener.getSquareX()); + assertEquals(5, listener.getSquareY()); + } + + @Test + void boardCanBeReplaced() { + assertDoesNotThrow(() -> listener.setBoard(board)); + } + + @Test void nonInteractiveMouseCallbacksDoNotRequireADisplay() { assertDoesNotThrow(() -> listener.mouseReleased(mouseEvent(MouseEvent.MOUSE_RELEASED))); assertDoesNotThrow(() -> listener.mouseExited(mouseEvent(MouseEvent.MOUSE_EXITED))); @@ -78,23 +84,26 @@ void clickAndPressRequireAGraphicalGameWindow() { Game game = new Game(); listener.setCheckersGame(game); - assertDoesNotThrow(() -> listener.mouseClicked(mouseEvent(MouseEvent.MOUSE_CLICKED))); assertDoesNotThrow(() -> listener.mousePressed(mouseEvent(MouseEvent.MOUSE_PRESSED))); } - + private static MouseEvent mouseEvent(int eventType) { - return new MouseEvent( - board, - eventType, - System.currentTimeMillis(), - 0, - 45, - 45, - 1, + return new MouseEvent( + board, + eventType, + System.currentTimeMillis(), + 0, + 45, + 45, + 1, false); } + /** + * {@code setGameBoard} creates display resources, so it cannot be used by + * a headless unit test. Populate the listener's required data model only. + */ private static void setBoardData(GameBoard target, BoardSquare[][] squares) { try { Field gameDataBoard = GameBoard.class.getDeclaredField("gameDataBoard"); @@ -104,4 +113,25 @@ private static void setBoardData(GameBoard target, BoardSquare[][] squares) { throw new IllegalStateException("Unable to initialize the test board", exception); } } + + /** + * Game inherits from JFrame. Allocate it without invoking JFrame's + * display-dependent constructor, then initialize the state the listener + * reads in its own constructor. + */ + private static Game newHeadlessGame() { + try { + Class unsafeClass = Class.forName("sun.misc.Unsafe"); + Field unsafeField = unsafeClass.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + Object unsafe = unsafeField.get(null); + Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class); + Game game = (Game) allocateInstance.invoke(unsafe, Game.class); + game.setUserColor("Red"); + game.setUserPlayer(new Player()); + return game; + } catch (ReflectiveOperationException exception) { + throw new IllegalStateException("Unable to create a headless Game test double", exception); + } + } } From 345ff3ea0ba642e92b307d6e1b723dc821649eaa Mon Sep 17 00:00:00 2001 From: Robert Palmer Date: Mon, 10 Aug 2026 22:37:01 -0400 Subject: [PATCH 12/12] Remove graphical environment dependent tests Removed tests that require a graphical environment and mouse events. --- .../Gameboard/GameboardMouseListenerTest.java | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/src/test/java/Gameboard/GameboardMouseListenerTest.java b/src/test/java/Gameboard/GameboardMouseListenerTest.java index a35e101..9485dd6 100644 --- a/src/test/java/Gameboard/GameboardMouseListenerTest.java +++ b/src/test/java/Gameboard/GameboardMouseListenerTest.java @@ -2,11 +2,8 @@ import com.mycompany.javacheckers.Game; import com.mycompany.javacheckers.Player; -import java.awt.GraphicsEnvironment; -import java.awt.event.MouseEvent; import java.lang.reflect.Field; import java.lang.reflect.Method; -import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -68,38 +65,6 @@ void boardCanBeReplaced() { assertDoesNotThrow(() -> listener.setBoard(board)); } - @Test - void nonInteractiveMouseCallbacksDoNotRequireADisplay() { - assertDoesNotThrow(() -> listener.mouseReleased(mouseEvent(MouseEvent.MOUSE_RELEASED))); - assertDoesNotThrow(() -> listener.mouseExited(mouseEvent(MouseEvent.MOUSE_EXITED))); - assertDoesNotThrow(() -> listener.mouseDragged(mouseEvent(MouseEvent.MOUSE_DRAGGED))); - assertDoesNotThrow(() -> listener.mouseMoved(mouseEvent(MouseEvent.MOUSE_MOVED))); - } - - @Test - void clickAndPressRequireAGraphicalGameWindow() { - Assumptions.assumeFalse( - GraphicsEnvironment.isHeadless(), - "Game extends JFrame and cannot be constructed headlessly"); - - Game game = new Game(); - listener.setCheckersGame(game); - assertDoesNotThrow(() -> listener.mouseClicked(mouseEvent(MouseEvent.MOUSE_CLICKED))); - assertDoesNotThrow(() -> listener.mousePressed(mouseEvent(MouseEvent.MOUSE_PRESSED))); - } - - private static MouseEvent mouseEvent(int eventType) { - return new MouseEvent( - board, - eventType, - System.currentTimeMillis(), - 0, - 45, - 45, - 1, - false); - } - /** * {@code setGameBoard} creates display resources, so it cannot be used by * a headless unit test. Populate the listener's required data model only.