diff --git a/00_Utilities/jvmTestUtils/kotlin/test/ConsoleTest.kt b/00_Utilities/jvmTestUtils/kotlin/test/ConsoleTest.kt new file mode 100644 index 00000000..d58af170 --- /dev/null +++ b/00_Utilities/jvmTestUtils/kotlin/test/ConsoleTest.kt @@ -0,0 +1,37 @@ +package com.pcholt.console.testutils + +import com.google.common.truth.Truth +import org.junit.Rule +import org.junit.contrib.java.lang.system.SystemOutRule +import org.junit.contrib.java.lang.system.TextFromStandardInputStream + +abstract class ConsoleTest { + @get:Rule + val inputRule = TextFromStandardInputStream.emptyStandardInputStream() + + @get:Rule + val systemOutRule = SystemOutRule().enableLog() + + val regexInputCommand = "\\{(.*)}".toRegex() + + fun assertConversation(conversation: String, runMain: () -> Unit) { + + inputRule.provideLines(*regexInputCommand + .findAll(conversation) + .map { it.groupValues[1] } + .toList().toTypedArray()) + + runMain() + + Truth.assertThat( + systemOutRule.log.trimWhiteSpace() + ) + .isEqualTo( + regexInputCommand + .replace(conversation, "").trimWhiteSpace() + ) + } + + private fun String.trimWhiteSpace() = + replace("[\\s]+".toRegex(), " ") +} \ No newline at end of file diff --git a/03_Animal/java/Animal.java b/03_Animal/java/src/Animal.java similarity index 98% rename from 03_Animal/java/Animal.java rename to 03_Animal/java/src/Animal.java index 681425c1..c4222c5f 100644 --- a/03_Animal/java/Animal.java +++ b/03_Animal/java/src/Animal.java @@ -74,11 +74,11 @@ public class Animal { private static void askForInformationAndSave(Scanner scan, AnimalNode current, QuestionNode previous, boolean previousToCurrentDecisionChoice) { //Failed to get it right and ran out of questions //Let's ask the user for the new information - System.out.print("THE ANIMAL YOU WERE THINKING OF WAS A "); + System.out.print("THE ANIMAL YOU WERE THINKING OF WAS A ? "); String animal = scan.nextLine(); - System.out.printf("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A %s FROM A %s ", animal, current.getAnimal()); + System.out.printf("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A %s FROM A %s ? ", animal, current.getAnimal()); String newQuestion = scan.nextLine(); - System.out.printf("FOR A %s THE ANSWER WOULD BE ", animal); + System.out.printf("FOR A %s THE ANSWER WOULD BE ? ", animal); boolean newAnswer = readYesOrNo(scan); //Add it to our question store addNewAnimal(current, previous, animal, newQuestion, newAnswer, previousToCurrentDecisionChoice); diff --git a/03_Animal/java/test/AnimalJavaTest.kt b/03_Animal/java/test/AnimalJavaTest.kt new file mode 100644 index 00000000..013655a3 --- /dev/null +++ b/03_Animal/java/test/AnimalJavaTest.kt @@ -0,0 +1,55 @@ +import com.pcholt.console.testutils.ConsoleTest +import org.junit.Test + +class AnimalJavaTest : ConsoleTest() { + + @Test + fun `given a standard setup, find the fish`() { + assertConversation( + """ + $title + ARE YOU THINKING OF AN ANIMAL ? {YES} + DOES IT SWIM ? {YES} + IS IT A FISH ? {YES} + WHY NOT TRY ANOTHER ANIMAL? + ARE YOU THINKING OF AN ANIMAL ? {QUIT} + """ + ) { + Animal.main(emptyArray()) + } + } + + @Test + fun `given a standard setup, create a cow, and verify`() { + assertConversation( + """ + $title + ARE YOU THINKING OF AN ANIMAL ? {YES} + DOES IT SWIM ? {NO} + IS IT A BIRD ? {NO} + THE ANIMAL YOU WERE THINKING OF WAS A ? {COW} + PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A + COW FROM A BIRD + ? {DOES IT EAT GRASS} + FOR A COW THE ANSWER WOULD BE ? {YES} + ARE YOU THINKING OF AN ANIMAL ? {YES} + DOES IT SWIM ? {NO} + DOES IT EAT GRASS ? {YES} + IS IT A COW ? {YES} + WHY NOT TRY ANOTHER ANIMAL? + ARE YOU THINKING OF AN ANIMAL ? {QUIT} + """ + ) { + Animal.main(emptyArray()) + } + } + + private val title = """ + ANIMAL + CREATIVE COMPUTING MORRISTOWN, NEW JERSEY + + PLAY 'GUESS THE ANIMAL' + THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT. + """ +} + diff --git a/03_Animal/kotlin/Animal.kt b/03_Animal/kotlin/src/Animal.kt similarity index 100% rename from 03_Animal/kotlin/Animal.kt rename to 03_Animal/kotlin/src/Animal.kt diff --git a/03_Animal/kotlin/test/AnimalKtTest.kt b/03_Animal/kotlin/test/AnimalKtTest.kt new file mode 100644 index 00000000..e93857ea --- /dev/null +++ b/03_Animal/kotlin/test/AnimalKtTest.kt @@ -0,0 +1,55 @@ +import com.pcholt.console.testutils.ConsoleTest +import org.junit.Test + +class AnimalKtTest : ConsoleTest() { + + @Test + fun `given a standard setup, find the fish`() { + assertConversation( + """ + $title + ARE YOU THINKING OF AN ANIMAL? {YES} + DOES IT SWIM? {YES} + IS IT A FISH? {YES} + WHY NOT TRY ANOTHER ANIMAL? + ARE YOU THINKING OF AN ANIMAL? {QUIT} + """ + ) { + main() + } + } + + @Test + fun `given a standard setup, create a cow, and verify`() { + assertConversation( + """ + $title + ARE YOU THINKING OF AN ANIMAL? {YES} + DOES IT SWIM? {NO} + IS IT A BIRD? {NO} + THE ANIMAL YOU WERE THINKING OF WAS A? {COW} + PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A + COW FROM A BIRD + ? {DOES IT EAT GRASS} + FOR A COW THE ANSWER WOULD BE? {YES} + ARE YOU THINKING OF AN ANIMAL? {YES} + DOES IT SWIM? {NO} + DOES IT EAT GRASS? {YES} + IS IT A COW? {YES} + WHY NOT TRY ANOTHER ANIMAL? + ARE YOU THINKING OF AN ANIMAL? {QUIT} + """ + ) { + main() + } + } + + private val title = """ + ANIMAL + CREATIVE COMPUTING MORRISTOWN, NEW JERSEY + + PLAY 'GUESS THE ANIMAL' + THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT. + """ +} + diff --git a/buildJvm/README.md b/buildJvm/README.md index 4ecba004..c08811b9 100644 --- a/buildJvm/README.md +++ b/buildJvm/README.md @@ -67,16 +67,16 @@ directory for the java or kotlin file, and the class that contains the `main` me The `build.gradle` file **should** be identical to all the other `build.gradle` files in all the other subprojects: ```groovy - sourceSets { - main { - java { - srcDirs "../../$gameSource" - } - } - } - application { - mainClass = gameMain - } + sourceSets { + main { + java { + srcDirs "../../$gameSource" + } + } + } + application { + mainClass = gameMain + } ``` The `gradle.properties` file should look like this: @@ -92,3 +92,84 @@ project to the list. ```groovy include ":build_91_Train_java" ``` + +### Adding a game with tests + +You can add tests for JVM games with a `build.gradle` looking a little different. +Use the build files from `03_Animal` as a template to add tests: + +```groovy +sourceSets { + main { + java { + srcDirs "../../$gameSource" + } + } + test { + java { + srcDirs "../../$gameTest" + } + } +} + +application { + mainClass = gameMain +} + +dependencies { + testImplementation(project(":build_00_utilities").sourceSets.test.output) +} +``` + +The gradle.properties needs an additional directory name for the tests, as `gameTest` : +``` +gameSource=03_Animal/java/src +gameTest=03_Animal/java/test +gameMain=Animal +``` + +Each project should have its own test, and shouldn't share test source directories +with other projects, even if they are for the same game. + +Tests are constructed by subclassing `ConsoleTest`. This allows you to use the +`assertConversation` function to check for correct interactive conversations. +```kotlin +import com.pcholt.console.testutils.ConsoleTest +import org.junit.Test + +class AnimalJavaTest : ConsoleTest() { + @Test + fun `should have a simple conversation`() { + assertConversation( + """ + WHAT'S YOUR NAME? {PAUL} + YOUR NAME IS PAUL? {YES} + THANKS FOR PLAYING + """ + ) { + // The game's Main method + main() + } + } +} +``` + +Curly brackets are the expected user input. +Note - this is actually just a way of defining the expected input as "PAUL" and "YES" +and not that the input happens at the exact prompt position. Thus this is equivalent: +```kotlin +""" +{PAUL} {YES} WHAT'S YOUR NAME? +YOUR NAME IS PAUL? +THANKS FOR PLAYING +""" +``` + +Amounts of whitespace are not counted, but whitespace is significant: You will get a failure if +your game emits `"NAME?"` when it expects `"NAME ?"`. + +Run all the tests from within the buildJvm project directory: +```bash +cd buildJvm +./gradlew test +``` diff --git a/buildJvm/build.gradle.kts b/buildJvm/build.gradle.kts index a43017d0..6cfba2e7 100644 --- a/buildJvm/build.gradle.kts +++ b/buildJvm/build.gradle.kts @@ -7,17 +7,13 @@ version = "unspecified" repositories { mavenCentral() + google() } dependencies { implementation(kotlin("stdlib")) } -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } -} task("distributeBin", Copy::class) { from(filesType("bin")) @@ -43,9 +39,21 @@ task("copyAll") { subprojects { apply(plugin = "application") apply(plugin = "kotlin") - repositories { - mavenCentral() + apply(plugin = "java") + repositories { + mavenCentral() + } + dependencies { + testImplementation("junit:junit:4.13.2") + testImplementation("com.github.stefanbirkner:system-rules:1.19.0") + testImplementation("com.google.truth:truth:1.1.3") + } + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) } + } + } fun filesType(type: String) = diff --git a/buildJvm/build_00_utilities/build.gradle b/buildJvm/build_00_utilities/build.gradle new file mode 100644 index 00000000..72b8b8c4 --- /dev/null +++ b/buildJvm/build_00_utilities/build.gradle @@ -0,0 +1,7 @@ +sourceSets { + test { + java { + srcDirs "../../$testSource" + } + } +} diff --git a/buildJvm/build_00_utilities/gradle.properties b/buildJvm/build_00_utilities/gradle.properties new file mode 100644 index 00000000..2ad9bb1a --- /dev/null +++ b/buildJvm/build_00_utilities/gradle.properties @@ -0,0 +1 @@ +testSource=00_Utilities/jvmTestUtils/kotlin/test diff --git a/buildJvm/build_01_Acey_Ducey_java17/build.gradle b/buildJvm/build_01_Acey_Ducey_java17/build.gradle new file mode 100644 index 00000000..132b05dd --- /dev/null +++ b/buildJvm/build_01_Acey_Ducey_java17/build.gradle @@ -0,0 +1,11 @@ +sourceSets { + main { + java { + srcDirs "../../$gameSource" + } + } +} + +application { + mainClass = gameMain +} diff --git a/buildJvm/build_01_Acey_Ducey_java17/gradle.properties b/buildJvm/build_01_Acey_Ducey_java17/gradle.properties new file mode 100644 index 00000000..d708d964 --- /dev/null +++ b/buildJvm/build_01_Acey_Ducey_java17/gradle.properties @@ -0,0 +1,2 @@ +gameSource=01_Acey_Ducey/java/src +gameMain=AceyDucey17 diff --git a/buildJvm/build_03_Animal_java/build.gradle b/buildJvm/build_03_Animal_java/build.gradle index 132b05dd..cc447526 100644 --- a/buildJvm/build_03_Animal_java/build.gradle +++ b/buildJvm/build_03_Animal_java/build.gradle @@ -4,8 +4,17 @@ sourceSets { srcDirs "../../$gameSource" } } + test { + java { + srcDirs "../../$gameTest" + } + } } application { mainClass = gameMain } + +dependencies { + testImplementation(project(":build_00_utilities").sourceSets.test.output) +} diff --git a/buildJvm/build_03_Animal_java/gradle.properties b/buildJvm/build_03_Animal_java/gradle.properties index 679638f2..496fc2d7 100644 --- a/buildJvm/build_03_Animal_java/gradle.properties +++ b/buildJvm/build_03_Animal_java/gradle.properties @@ -1,2 +1,3 @@ -gameSource=03_Animal/java +gameSource=03_Animal/java/src +gameTest=03_Animal/java/test gameMain=Animal diff --git a/buildJvm/build_03_Animal_kotlin/build.gradle b/buildJvm/build_03_Animal_kotlin/build.gradle index 132b05dd..cc447526 100644 --- a/buildJvm/build_03_Animal_kotlin/build.gradle +++ b/buildJvm/build_03_Animal_kotlin/build.gradle @@ -4,8 +4,17 @@ sourceSets { srcDirs "../../$gameSource" } } + test { + java { + srcDirs "../../$gameTest" + } + } } application { mainClass = gameMain } + +dependencies { + testImplementation(project(":build_00_utilities").sourceSets.test.output) +} diff --git a/buildJvm/build_03_Animal_kotlin/gradle.properties b/buildJvm/build_03_Animal_kotlin/gradle.properties index ea63333f..a3fdf99f 100644 --- a/buildJvm/build_03_Animal_kotlin/gradle.properties +++ b/buildJvm/build_03_Animal_kotlin/gradle.properties @@ -1,2 +1,3 @@ -gameSource=03_Animal/kotlin +gameSource=03_Animal/kotlin/src +gameTest=03_Animal/kotlin/test gameMain=AnimalKt diff --git a/buildJvm/build_96_Word_java/build.gradle b/buildJvm/build_96_Word_java/build.gradle index 3432056c..132b05dd 100644 --- a/buildJvm/build_96_Word_java/build.gradle +++ b/buildJvm/build_96_Word_java/build.gradle @@ -1,7 +1,3 @@ -plugins { - id 'application' -} - sourceSets { main { java { @@ -10,10 +6,6 @@ sourceSets { } } -repositories { - mavenCentral() -} - application { mainClass = gameMain } diff --git a/buildJvm/settings.gradle b/buildJvm/settings.gradle index b7ce0404..c5c4cbd8 100644 --- a/buildJvm/settings.gradle +++ b/buildJvm/settings.gradle @@ -1,5 +1,7 @@ rootProject.name = 'BasicComputerGames' +include ":build_00_utilities" + include ":build_01_Acey_Ducey_kotlin" include ":build_03_Animal_kotlin" include ":build_53_King_kotlin" @@ -10,6 +12,7 @@ include ":build_89_Tic-Tac-Toe_kotlin" include ":build_94_War_kotlin" include ":build_01_Acey_Ducey_java" +include ":build_01_Acey_Ducey_java17" include ":build_02_Amazing_java" include ":build_03_Animal_java" include ":build_04_Awari_java"