From fe072ea940d1e2ec2b7e4a571413a6c6ef928ccf Mon Sep 17 00:00:00 2001 From: Muna Kandel <mkandel2@myune.edu.au> Date: Sat, 23 Sep 2023 18:21:42 +0545 Subject: [PATCH] Fixes #14 Add Unit Tests for App Class and GamePanel class, refactor GamePanel class --- app/build.gradle | 3 + app/src/main/java/brickbreaker/Ball.java | 8 ++ .../brickbreaker/DefaultGameRenderer.java | 80 ++++++++++++ app/src/main/java/brickbreaker/GamePanel.java | 122 ++++++++---------- .../brickbreaker/Interfaces/GameRenderer.java | 19 +++ app/src/test/java/brickbreaker/AppTest.java | 22 +++- .../test/java/brickbreaker/GamePanelTest.java | 103 +++++++++++++++ 7 files changed, 289 insertions(+), 68 deletions(-) create mode 100644 app/src/main/java/brickbreaker/DefaultGameRenderer.java create mode 100644 app/src/main/java/brickbreaker/Interfaces/GameRenderer.java create mode 100644 app/src/test/java/brickbreaker/GamePanelTest.java diff --git a/app/build.gradle b/app/build.gradle index b8c6a0d..81ee478 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,6 +23,9 @@ dependencies { // This dependency is used by the application. implementation 'com.google.guava:guava:30.1.1-jre' implementation 'mysql:mysql-connector-java:8.0.28' +// Mockito + testImplementation 'org.mockito:mockito-core:3.10.0' + } application { diff --git a/app/src/main/java/brickbreaker/Ball.java b/app/src/main/java/brickbreaker/Ball.java index ca11a7d..dfe27b4 100644 --- a/app/src/main/java/brickbreaker/Ball.java +++ b/app/src/main/java/brickbreaker/Ball.java @@ -28,6 +28,14 @@ public class Ball { return this.position; } + public int getRadius() { + return this.radius; + } + + public Color getColor() { + return this.color; + } + public Vector getDirection() { return this.direction; } diff --git a/app/src/main/java/brickbreaker/DefaultGameRenderer.java b/app/src/main/java/brickbreaker/DefaultGameRenderer.java new file mode 100644 index 0000000..c8d30a2 --- /dev/null +++ b/app/src/main/java/brickbreaker/DefaultGameRenderer.java @@ -0,0 +1,80 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package brickbreaker; + +import brickbreaker.Interfaces.GameRenderer; +import java.awt.*; +import java.util.Iterator; +import java.util.List; +import javax.swing.JPanel; + +public class DefaultGameRenderer implements GameRenderer { + @Override + public void render( + GamePanel gamePanel, + Graphics graphics, + List<Ball> balls, + int paddle, + ScoringSystem scoringSystem, + int totalBricks + ) { + if (balls.isEmpty()) { + gamePanel.gameOver(graphics, "Game Over", Color.BLACK); + return; + } + + if (!gamePanel.play) { + graphics.setFont(new Font(gamePanel.font, Font.BOLD, 20)); + graphics.drawString("Press Enter to Start", 230, 350); + return; + } + + // Background color + graphics.setColor(Color.green); + graphics.fillRect(1, 1, 692, 592); + + // Render game map + gamePanel.gameMap.draw((Graphics2D) graphics); + + // Render paddle + graphics.setColor(Color.blue); + graphics.fillRect(paddle, 550, 100, 20); + + Iterator<Ball> ballsIterator = balls.iterator(); + + while (ballsIterator.hasNext()) { + Ball ball = ballsIterator.next(); + Vector ballPosition = ball.getPosition(); + + if (ballPosition.y > 560) { + ballsIterator.remove(); + continue; + } else { + ball.checkForWallCollisions(); + } + + ball.draw(graphics); + } + + // Render score + graphics.setColor(Color.black); + graphics.setFont(new Font("MV Boli", Font.BOLD, 25)); + graphics.drawString("Score: " + scoringSystem.getScore(), 520, 30); + + // Render high score + graphics.setColor(Color.black); + graphics.setFont(new Font("MV Boli", Font.BOLD, 25)); + graphics.drawString("High Score: " + scoringSystem.getHighScore(), 10, 30); + + if (totalBricks <= 0) { + Color color = new Color(0XFF6464); + gamePanel.gameOver(graphics, "You Won", color); + } + + if (balls.isEmpty()) { + gamePanel.gameOver(graphics, "Game Over", Color.BLACK); + } + } +} diff --git a/app/src/main/java/brickbreaker/GamePanel.java b/app/src/main/java/brickbreaker/GamePanel.java index c8f282d..ddb6017 100644 --- a/app/src/main/java/brickbreaker/GamePanel.java +++ b/app/src/main/java/brickbreaker/GamePanel.java @@ -3,6 +3,7 @@ package brickbreaker; import brickbreaker.GameComponents.Brick; import brickbreaker.Interfaces.BrickFactory; import brickbreaker.GameComponents.DefaultBrickFactory; +import brickbreaker.Interfaces.GameRenderer; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; @@ -13,23 +14,24 @@ import java.util.Iterator; import java.util.List; public class GamePanel extends JPanel implements ActionListener { - private boolean play = false; - private boolean gameOver = false; - private ScoringSystem scoringSystem; + public boolean play = false; + public boolean gameOver = false; + public ScoringSystem scoringSystem; - private int totalBricks; - private int rows = 10; - private int columns = 14; + public int totalBricks; + public int rows = 10; + public int columns = 14; - private Timer timer; - private int delay = 8; + public Timer timer; + public int delay = 8; - private int paddle = 310; + public int paddle = 310; - private List<Ball> balls = new ArrayList<>(); - private String font = "MV Boli"; - private JFrame frame; - private GameMapBuilder gameMap; + public List<Ball> balls = new ArrayList<>(); + public String font = "MV Boli"; + public JFrame frame; + public GameMapBuilder gameMap; + private final GameRenderer gameRenderer; public GamePanel(JFrame frame) { Color color = Color.decode("#8B4513"); @@ -45,8 +47,46 @@ public class GamePanel extends JPanel implements ActionListener { addKeyBindings(); scoringSystem = App.scoreSystem; + gameRenderer = new DefaultGameRenderer(); + } + + public GamePanel(JFrame frame, GameRenderer gameRenderer) { + Color color = Color.decode("#8B4513"); + this.balls.add(new Ball(350, 450, 2, -2, 20, color)); + this.totalBricks = rows * columns; + this.frame = frame; + BrickFactory brickfactory = new DefaultBrickFactory(); + gameMap = new GameMapBuilder(rows, columns, brickfactory); + setFocusable(true); + setFocusTraversalKeysEnabled(false); + timer = new Timer(delay, this); + timer.start(); + addKeyBindings(); + + scoringSystem = App.scoreSystem; + this.gameRenderer = gameRenderer; + } + + + public List<Ball> getBalls() { + return balls; + } + + // Getter for paddle + public int getPaddle() { + return paddle; + } + + // Getter for scoringSystem + public ScoringSystem getScoringSystem() { + return scoringSystem; } + // Getter for totalBricks + public int getTotalBricks() { + return totalBricks; + } + private void addKeyBindings() { InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); ActionMap actionMap = getActionMap(); @@ -110,63 +150,15 @@ public class GamePanel extends JPanel implements ActionListener { }); } + @Override protected void paintComponent(Graphics graphics) { super.paintComponent(graphics); - if (balls.isEmpty()) { - gameOver(graphics, "Game Over", Color.BLACK); - return; - } - - if(!play) { - graphics.setFont(new Font(font, Font.BOLD, 20)); - graphics.drawString("Press Enter to Start", 230, 350); - return; - } - // background color - graphics.setColor(Color.green); - graphics.fillRect(1, 1, 692, 592); - - gameMap.draw((Graphics2D) graphics); - - - graphics.setColor(Color.blue); - graphics.fillRect(paddle, 550, 100, 20); - - Iterator<Ball> ballsIterator = balls.iterator(); - - while (ballsIterator.hasNext()) { - Ball ball = ballsIterator.next(); - Vector ballPosition = ball.getPosition(); - if (ballPosition.y > 560) { - ballsIterator.remove(); - continue; - } else { - ball.checkForWallCollisions(); - } - - ball.draw(graphics); - } - - graphics.setColor(Color.black); - graphics.setFont(new Font("MV Boli", Font.BOLD, 25)); - graphics.drawString("Score: " + scoringSystem.getScore(), 520, 30); - - // Display the high score - graphics.setColor(Color.black); - graphics.setFont(new Font("MV Boli", Font.BOLD, 25)); - graphics.drawString("High Score: " + scoringSystem.getHighScore(), 10, 30); - if (totalBricks <= 0) { - Color color = new Color(0XFF6464); - gameOver(graphics, "You Won", color); + if (gameRenderer != null) { + gameRenderer.render(this, graphics, balls, paddle, scoringSystem, totalBricks); } - - if (balls.isEmpty()) { - gameOver(graphics, "Game Over", Color.BLACK); - } - } - + public void gameOver(Graphics graphics, String gameOverText, Color color) { gameOver = true; // Set the game over flag to true graphics.setColor(color); diff --git a/app/src/main/java/brickbreaker/Interfaces/GameRenderer.java b/app/src/main/java/brickbreaker/Interfaces/GameRenderer.java new file mode 100644 index 0000000..54ebc42 --- /dev/null +++ b/app/src/main/java/brickbreaker/Interfaces/GameRenderer.java @@ -0,0 +1,19 @@ + +package brickbreaker.Interfaces; + +import brickbreaker.Ball; +import brickbreaker.GamePanel; +import brickbreaker.ScoringSystem; +import java.awt.Graphics; +import java.util.List; + +public interface GameRenderer { + void render( + GamePanel gamePanel, + Graphics graphics, + List<Ball> balls, + int paddle, + ScoringSystem scoringSystem, + int totalBricks + ); +} \ No newline at end of file diff --git a/app/src/test/java/brickbreaker/AppTest.java b/app/src/test/java/brickbreaker/AppTest.java index e86eb8d..3381461 100644 --- a/app/src/test/java/brickbreaker/AppTest.java +++ b/app/src/test/java/brickbreaker/AppTest.java @@ -3,12 +3,28 @@ */ package brickbreaker; +import javax.swing.JFrame; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; class AppTest { - @Test void appHasAGreeting() { - App classUnderTest = new App(); - assertNotNull(classUnderTest.getGreeting(), "app should have a greeting"); + private JFrame testFrame; + + @BeforeEach + public void setUp() { + // Create a new JFrame for testing + testFrame = new JFrame(); + testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + testFrame.setVisible(false); // Set to invisible for testing + } + + @Test + public void testCreateGame() { + App.frame = testFrame; + + App.createGame(); + + assertNotNull(App.gamePanel); } } diff --git a/app/src/test/java/brickbreaker/GamePanelTest.java b/app/src/test/java/brickbreaker/GamePanelTest.java new file mode 100644 index 0000000..1b34924 --- /dev/null +++ b/app/src/test/java/brickbreaker/GamePanelTest.java @@ -0,0 +1,103 @@ +package brickbreaker; + +import brickbreaker.GameComponents.Brick; +import brickbreaker.GameComponents.DefaultBrickFactory; +import brickbreaker.Interfaces.GameRenderer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class GamePanelTest { + + @Mock + private JFrame frame; + + @Mock + private ScoringSystem scoringSystem; + + @Mock + private GameRenderer gameRenderer; + + private GamePanel gamePanel; + + @BeforeEach + public void setUp() { + MockitoAnnotations.initMocks(this); + gamePanel = new GamePanel(frame, gameRenderer); + gamePanel.scoringSystem = scoringSystem; + } + + @Test + public void testInitialization() { + assertEquals(1, gamePanel.getBalls().size()); + assertFalse(gamePanel.play); + assertFalse(gamePanel.gameOver); + assertNotNull(gamePanel.timer); + } + + @Test + public void testMoveRight() { + gamePanel.moveRight(); + assertTrue(gamePanel.play); + } + + @Test + public void testMoveLeft() { + gamePanel.moveLeft(); + assertTrue(gamePanel.play); + } + + @Test + public void testActionPerformed() { + Ball mockBall = mock(Ball.class); + when(mockBall.getRadius()).thenReturn(20); + when(mockBall.getPosition()).thenReturn(new Vector(100, 100)); + when(mockBall.getColor()).thenReturn(Color.decode("#8B4513")); + when(mockBall.getDirection()).thenReturn(new Vector(-1, 1)); + + List<Ball> balls = new ArrayList<>(); + balls.add(mockBall); + gamePanel.balls = balls; + gamePanel.play = true; + // Create a mock Brick + Brick mockBrick = mock(Brick.class); + when(mockBrick.getIsHit()).thenReturn(false); + when(mockBrick.getPosition()).thenReturn(new Vector(100, 100)); + when(mockBrick.getWidth()).thenReturn(50); + when(mockBrick.getHeight()).thenReturn(20); + + // Create a mock GameMapBuilder and set the map to contain the mock brick + Brick[][] mockMap = new Brick[][]{{mockBrick}}; + gamePanel.gameMap = new GameMapBuilder(1, 1, new DefaultBrickFactory()); + gamePanel.gameMap.map = mockMap; + + // Create a mock ActionEvent + ActionEvent mockActionEvent = mock(ActionEvent.class); + + // Call actionPerformed method with the mock ActionEvent + gamePanel.actionPerformed(mockActionEvent); + + // Verify that expected interactions with mocks occurred + + // Verify that getPosition and getDirection are called on mockBall + verify(mockBall, atLeast(1)).getPosition(); + verify(mockBall, atLeast(1)).move(); + + // Verify that getIsHit, getPosition, getWidth, and getHeight are called on mockBrick + verify(mockBrick, atLeast(1)).getIsHit(); + verify(mockBrick, atLeast(1)).getPosition(); + verify(mockBrick, atLeast(1)).getWidth(); + verify(mockBrick, atLeast(1)).getHeight(); + + } + +} \ No newline at end of file -- GitLab