diff --git a/README.md b/README.md index bfdd07d3..bba577be 100644 --- a/README.md +++ b/README.md @@ -123,8 +123,6 @@ For VSCode or Neovim users, you know what you are doing. ### Debug and Testing -This is not setup yet! Remove this when setup is complete and add GitHub action. - Run unit tests: ```bash @@ -137,6 +135,17 @@ Run unit tests with coverage: ./gradlew test jacocoTestReport ``` +The test coverage reports for each subproject can be found at: +- [Chessevolved model](chessevolved_model/build/reports/jacoco/test/html/index.html) +- [Chessevolved presenter](chessevolved_presenter/build/reports/jacoco/test/html/index.html) +- [Lwjgl3 (Desktop launcher)](lwjgl3/build/reports/jacoco/test/html/index.html) + +If Jacoco does not generate coverage reports when running the previous command, run the following: + +```bash +./gradlew test --rerun jacocoTestReport +``` + ### Dependencies - [KTX](https://libktx.github.io/) - Kotlin extensions for LibGDX diff --git a/build.gradle b/build.gradle index ffdf1779..896b73be 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,7 @@ plugins { allprojects { apply plugin: 'eclipse' apply plugin: 'idea' + apply plugin: 'jacoco' // This allows you to "Build and run using IntelliJ IDEA", an option in IDEA's Settings. idea { diff --git a/chessevolved_model/build.gradle b/chessevolved_model/build.gradle index 850612e1..aa19e0da 100644 --- a/chessevolved_model/build.gradle +++ b/chessevolved_model/build.gradle @@ -57,4 +57,13 @@ dependencies { implementation("io.github.cdimascio:dotenv-kotlin:6.5.1") implementation project(":chessevolved_shared") + + // Testing with kotlin.test and JUnit + testImplementation 'org.jetbrains.kotlin:kotlin-test' + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.1" +} + +test { + useJUnitPlatform() + workingDir = rootProject.file('assets').path } diff --git a/chessevolved_model/src/test/kotlin/io/github/chessevolved/entities/BoardSquareFactoryTest.kt b/chessevolved_model/src/test/kotlin/io/github/chessevolved/entities/BoardSquareFactoryTest.kt new file mode 100644 index 00000000..dfdf511e --- /dev/null +++ b/chessevolved_model/src/test/kotlin/io/github/chessevolved/entities/BoardSquareFactoryTest.kt @@ -0,0 +1,32 @@ +package io.github.chessevolved.entities + +import com.badlogic.ashley.core.Engine +import com.badlogic.gdx.assets.AssetManager +import com.badlogic.gdx.scenes.scene2d.Stage +import io.github.chessevolved.data.Position +import io.github.chessevolved.enums.PlayerColor +import io.github.chessevolved.enums.WeatherEvent +import io.github.chessevolved.singletons.EcsEngine +import org.junit.jupiter.api.Test +import kotlin.test.assertFails + +class BoardSquareFactoryTest { + private val engine: Engine = EcsEngine + private val assetManager: AssetManager = AssetManager() + private val boardSquareFactory: BoardSquareFactory = BoardSquareFactory(engine, assetManager) + + /** + * Attempt to create a square at an invalid location + */ + @Test + fun createBoardSquare() { + assertFails({ + boardSquareFactory.createBoardSquare( + Position(1000, 1000), + WeatherEvent.NONE, + PlayerColor.BLACK, + Stage(), + ) + }) + } +} diff --git a/chessevolved_model/src/test/kotlin/io/github/chessevolved/entities/PieceFactoryTest.kt b/chessevolved_model/src/test/kotlin/io/github/chessevolved/entities/PieceFactoryTest.kt new file mode 100644 index 00000000..54216544 --- /dev/null +++ b/chessevolved_model/src/test/kotlin/io/github/chessevolved/entities/PieceFactoryTest.kt @@ -0,0 +1,101 @@ +package io.github.chessevolved.entities + +import com.badlogic.ashley.core.Engine +import com.badlogic.gdx.assets.AssetManager +import com.badlogic.gdx.scenes.scene2d.Stage +import io.github.chessevolved.data.Position +import io.github.chessevolved.enums.PlayerColor +import io.github.chessevolved.singletons.EcsEngine +import org.junit.jupiter.api.Test +import kotlin.test.assertFails + +class PieceFactoryTest { + private val engine: Engine = EcsEngine + private val assetManager: AssetManager = AssetManager() + private val pieceFactory: PieceFactory = PieceFactory(engine, assetManager) + + /** + * Attempt to create a pawn at an invalid location + */ + @Test + fun createPawn() { + assertFails({ + pieceFactory.createPawn( + false, + Position(1000, 1000), + PlayerColor.BLACK, + Stage(), + ) + }) + } + + /** + * Attempt to create a knight at an invalid location + */ + @Test + fun createKnight() { + assertFails({ + pieceFactory.createKnight( + Position(1000, 1000), + PlayerColor.BLACK, + Stage(), + ) + }) + } + + /** + * Attempt to create a bishop at an invalid location + */ + @Test + fun createBishop() { + assertFails({ + pieceFactory.createBishop( + Position(1000, 1000), + PlayerColor.BLACK, + Stage(), + ) + }) + } + + /** + * Attempt to create a rook at an invalid location + */ + @Test + fun createRook() { + assertFails({ + pieceFactory.createRook( + Position(1000, 1000), + PlayerColor.BLACK, + Stage(), + ) + }) + } + + /** + * Attempt to create a queen at an invalid location + */ + @Test + fun createQueen() { + assertFails({ + pieceFactory.createQueen( + Position(1000, 1000), + PlayerColor.BLACK, + Stage(), + ) + }) + } + + /** + * Attempt to create a king at an invalid location + */ + @Test + fun createKing() { + assertFails({ + pieceFactory.createKing( + Position(1000, 1000), + PlayerColor.BLACK, + Stage(), + ) + }) + } +} diff --git a/chessevolved_model/src/test/kotlin/io/github/chessevolved/singletons/GameSettingsTest.kt b/chessevolved_model/src/test/kotlin/io/github/chessevolved/singletons/GameSettingsTest.kt new file mode 100644 index 00000000..73f35562 --- /dev/null +++ b/chessevolved_model/src/test/kotlin/io/github/chessevolved/singletons/GameSettingsTest.kt @@ -0,0 +1,32 @@ +package io.github.chessevolved.singletons + +import io.github.chessevolved.dtos.SettingsDto +import org.junit.jupiter.api.Assertions.assertEquals +import kotlin.test.Test + +class GameSettingsTest { + @Test + fun isFOWEnabled() { + assertEquals(false, GameSettings.isFOWEnabled()) + } + + @Test + fun getBoardSize() { + assertEquals(8, GameSettings.getBoardSize()) + } + + @Test + fun getGameSettings() { + val settingsDTO: SettingsDto = SettingsDto(false, 8) + assertEquals(settingsDTO, GameSettings.getGameSettings()) + } + + @Test + fun setGameSettings() { + val settingsDTO: SettingsDto = SettingsDto(true, 16) + GameSettings.setGameSettings(settingsDTO) + assertEquals(settingsDTO, GameSettings.getGameSettings()) + assertEquals(true, GameSettings.isFOWEnabled()) + assertEquals(16, GameSettings.getBoardSize()) + } +} diff --git a/chessevolved_model/src/test/kotlin/io/github/chessevolved/singletons/GameTest.kt b/chessevolved_model/src/test/kotlin/io/github/chessevolved/singletons/GameTest.kt new file mode 100644 index 00000000..f565f462 --- /dev/null +++ b/chessevolved_model/src/test/kotlin/io/github/chessevolved/singletons/GameTest.kt @@ -0,0 +1,57 @@ +package io.github.chessevolved.singletons + +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertFalse +import kotlin.test.Test +import kotlin.test.assertFails + +class GameTest { + suspend fun joinGame() { + Game.joinGame("HY76UYTERFRGET") + } + + /** + * Attempt to join a game that does not exists + */ + @Test + fun testJoinGame() = + runTest { // Required to test suspend functions + assertFails({ joinGame() }) + } + + suspend fun leaveGame() { + Game.leaveGame() + } + + /** + * Attempt to leave a game despite not being in a game + */ + @Test + fun testLeaveGame() = + runTest { + assertFails({ leaveGame() }) + } + + suspend fun askForRematch() { + Game.askForRematch() + } + + /** + * Attempt to ask for a rematch despite not being in a game + */ + @Test + fun testAskForRematch() = + runTest { + assertFails({ askForRematch() }) + } + + @Test + fun getWantsRematch() { + assertFalse(Game.getWantsRematch()) + } + + @Test + fun isInGame() { + assertFalse(Game.isInGame()) + } +} diff --git a/chessevolved_model/src/test/kotlin/io/github/chessevolved/singletons/LobbyTest.kt b/chessevolved_model/src/test/kotlin/io/github/chessevolved/singletons/LobbyTest.kt new file mode 100644 index 00000000..866d7aca --- /dev/null +++ b/chessevolved_model/src/test/kotlin/io/github/chessevolved/singletons/LobbyTest.kt @@ -0,0 +1,86 @@ +package io.github.chessevolved.singletons + +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.assertThrows +import kotlin.test.Test +import kotlin.test.assertFails + +class LobbyTest { + suspend fun joinLobby() { + Lobby.joinLobby("E6U5Y5GTRFEZE45") + } + + /** + * Attempt to join a lobby that does not exists + */ + @Test + fun testJoinLobby() = + runTest { // Required to test suspend functions + assertFails({ joinLobby() }) + } + + suspend fun joinRematchLobbyAsHost() { + Lobby.joinRematchLobbyAsHost() + } + + /** + * Attempt to join a rematch lobby as a host that does not exists + */ + @Test + fun testJoinRematchLobbyAsHost() = + runTest { // Required to test suspend functions + assertFails({ joinRematchLobbyAsHost() }) + } + + suspend fun joinRematchLobbyNonHost() { + Lobby.joinRematchLobbyNonHost() + } + + /** + * Attempt to join a rematch lobby as a non-host that does not exists + */ + @Test + fun testJoinRematchLobbyNonHost() = + runTest { // Required to test suspend functions + assertFails({ joinRematchLobbyNonHost() }) + } + + suspend fun leaveLobby() { + Lobby.leaveLobby() + } + + /** + * Attempt to leave a lobby despite not being in a lobby + */ + @Test + fun testLeaveLobby() = + runTest { // Required to test suspend functions + assertFails({ leaveLobby() }) + } + + suspend fun leaveLobbyWithoutUpdating() { + Lobby.leaveLobbyWithoutUpdating() + } + + /** + * Attempt to leave a lobby as a second player despite not being in a lobby + */ + @Test + fun testLeaveLobbyWithoutUpdating() = + runTest { // Required to test suspend functions + assertFails({ leaveLobbyWithoutUpdating() }) + } + + suspend fun setLobbySettings() { + Lobby.setLobbySettings() + } + + /** + * Attempt to change lobby settings despite not being in a lobby + */ + @Test + fun testSetLobbySettings() = + runTest { // Required to test suspend functions + assertThrows({ setLobbySettings() }) + } +} diff --git a/chessevolved_presenter/build.gradle b/chessevolved_presenter/build.gradle index 27791db3..45b9d6a7 100644 --- a/chessevolved_presenter/build.gradle +++ b/chessevolved_presenter/build.gradle @@ -37,4 +37,11 @@ dependencies { implementation project(":chessevolved_model") implementation project(":chessevolved_view") implementation project(":chessevolved_shared") + + // Testing with kotlin.test and JUnit + testImplementation 'org.jetbrains.kotlin:kotlin-test' +} + +test { + useJUnitPlatform() } diff --git a/chessevolved_presenter/src/main/kotlin/io/github/chessevolved/presenters/MockPresenter.kt b/chessevolved_presenter/src/main/kotlin/io/github/chessevolved/presenters/MockPresenter.kt new file mode 100644 index 00000000..e252d111 --- /dev/null +++ b/chessevolved_presenter/src/main/kotlin/io/github/chessevolved/presenters/MockPresenter.kt @@ -0,0 +1,25 @@ +package io.github.chessevolved.presenters + +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import io.github.chessevolved.Navigator +import io.github.chessevolved.views.IView + +class MockPresenter( + private val view: IView, + private val navigator: Navigator, +) : IPresenter { + override fun render(sb: SpriteBatch) { + } + + override fun resize( + width: Int, + height: Int, + ) { + } + + override fun dispose() { + } + + override fun setInputProcessor() { + } +} diff --git a/chessevolved_presenter/src/test/kotlin/io/github/chessevolved/PresenterManagerTest.kt b/chessevolved_presenter/src/test/kotlin/io/github/chessevolved/PresenterManagerTest.kt new file mode 100644 index 00000000..97ff8a77 --- /dev/null +++ b/chessevolved_presenter/src/test/kotlin/io/github/chessevolved/PresenterManagerTest.kt @@ -0,0 +1,49 @@ +package io.github.chessevolved + +import com.badlogic.gdx.assets.AssetManager +import io.github.chessevolved.presenters.IPresenter +import io.github.chessevolved.presenters.MockPresenter +import io.github.chessevolved.views.MockView +import java.util.ArrayDeque +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class PresenterManagerTest { + private var presenters = ArrayDeque() + private val presenter = MockPresenter(MockView(""), Navigator(AssetManager())) + + @Test + fun push() { + presenters.push(presenter) + PresenterManager.push(presenter) + assertEquals(presenters.peekFirst(), PresenterManager.getCurrent()) + presenters = ArrayDeque() + } + + @Test + fun pop() { + presenters.push(presenter) + PresenterManager.push(presenter) + presenters.pop() + PresenterManager.pop() + assertEquals(presenters.peekFirst(), PresenterManager.getCurrent()) + presenters = ArrayDeque() + } + + @Test + fun set() { + presenters.push(presenter) + PresenterManager.set(presenter) + assertEquals(presenters.peekFirst(), PresenterManager.getCurrent()) + presenters = ArrayDeque() + } + + @Test + fun isEmpty() { + while (!PresenterManager.isEmpty()) { + PresenterManager.pop() + } + assertTrue { PresenterManager.isEmpty() } + } +} diff --git a/chessevolved_view/build.gradle b/chessevolved_view/build.gradle index b14498df..74b8c012 100644 --- a/chessevolved_view/build.gradle +++ b/chessevolved_view/build.gradle @@ -39,4 +39,11 @@ dependencies { } implementation project(":chessevolved_shared") + + // Testing with kotlin.test and JUnit + testImplementation 'org.jetbrains.kotlin:kotlin-test' +} + +test { + useJUnitPlatform() } diff --git a/chessevolved_view/src/main/kotlin/io/github/chessevolved/views/MockView.kt b/chessevolved_view/src/main/kotlin/io/github/chessevolved/views/MockView.kt new file mode 100644 index 00000000..490008ca --- /dev/null +++ b/chessevolved_view/src/main/kotlin/io/github/chessevolved/views/MockView.kt @@ -0,0 +1,23 @@ +package io.github.chessevolved.views + +class MockView( + private val lobbyCode: String, +) : IView { + override fun init() { + } + + override fun render() { + } + + override fun resize( + width: Int, + height: Int, + ) { + } + + override fun dispose() { + } + + override fun setInputProcessor() { + } +} diff --git a/lwjgl3/build.gradle b/lwjgl3/build.gradle index 869d7f74..b55bbd4b 100644 --- a/lwjgl3/build.gradle +++ b/lwjgl3/build.gradle @@ -37,6 +37,9 @@ dependencies { implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" implementation project(':chessevolved_presenter') + // Testing with kotlin.test and JUnit + testImplementation 'org.jetbrains.kotlin:kotlin-test' + if(enableGraalNative == 'true') { implementation "io.github.berstanio:gdx-svmhelper-backend-lwjgl3:$graalHelperVersion" implementation "io.github.berstanio:gdx-svmhelper-extension-freetype:$graalHelperVersion" @@ -44,6 +47,10 @@ dependencies { } +test { + useJUnitPlatform() +} + def os = System.properties['os.name'].toLowerCase() run { diff --git a/lwjgl3/src/test/kotlin/io/github/chessevolved/lwjgl3/Lwjgl3LauncherTest.kt b/lwjgl3/src/test/kotlin/io/github/chessevolved/lwjgl3/Lwjgl3LauncherTest.kt new file mode 100644 index 00000000..94155359 --- /dev/null +++ b/lwjgl3/src/test/kotlin/io/github/chessevolved/lwjgl3/Lwjgl3LauncherTest.kt @@ -0,0 +1,28 @@ +package io.github.chessevolved.lwjgl3 + +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration +import io.github.chessevolved.ChessEvolvedGame +import org.junit.jupiter.api.assertDoesNotThrow +import kotlin.test.Test + +class Lwjgl3LauncherTest { + @Test + fun testUI() { + val config: Lwjgl3ApplicationConfiguration = + Lwjgl3ApplicationConfiguration().apply { + setTitle("Chess Evolved") + setWindowedMode(360, 800) + setWindowIcon( + *( + arrayOf(128, 64, 32, 16) + .map { "libgdx$it.png" } + .toTypedArray() + ), + ) + } + config.disableAudio(true) + val testApplication: Lwjgl3Application = Lwjgl3Application(ChessEvolvedGame(), config) + assertDoesNotThrow({ testApplication.exit() }) + } +}