From 302e1a0e0b688e6b029cef2e6a7f81681cd6ecfe Mon Sep 17 00:00:00 2001 From: Paul Holt Date: Sat, 29 Jan 2022 22:36:29 +1100 Subject: [PATCH 1/4] Testing added! Now some full behaviour tests on both Animal JVM implementations. Still todo: Move the ConsoleTest library into its own module, add a gradle dependency on it. --- 03_Animal/java/{ => src}/Animal.java | 6 +-- 03_Animal/java/test/AnimalJavaTest.kt | 54 +++++++++++++++++++++++++++ 03_Animal/java/test/ConsoleTest.kt | 35 +++++++++++++++++ 03_Animal/kotlin/{ => src}/Animal.kt | 0 03_Animal/kotlin/test/AnimalKtTest.kt | 54 +++++++++++++++++++++++++++ 03_Animal/kotlin/test/ConsoleTest.kt | 35 +++++++++++++++++ 6 files changed, 181 insertions(+), 3 deletions(-) rename 03_Animal/java/{ => src}/Animal.java (98%) create mode 100644 03_Animal/java/test/AnimalJavaTest.kt create mode 100644 03_Animal/java/test/ConsoleTest.kt rename 03_Animal/kotlin/{ => src}/Animal.kt (100%) create mode 100644 03_Animal/kotlin/test/AnimalKtTest.kt create mode 100644 03_Animal/kotlin/test/ConsoleTest.kt 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..06513481 --- /dev/null +++ b/03_Animal/java/test/AnimalJavaTest.kt @@ -0,0 +1,54 @@ +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/java/test/ConsoleTest.kt b/03_Animal/java/test/ConsoleTest.kt new file mode 100644 index 00000000..3eebdcbb --- /dev/null +++ b/03_Animal/java/test/ConsoleTest.kt @@ -0,0 +1,35 @@ +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/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..38fae815 --- /dev/null +++ b/03_Animal/kotlin/test/AnimalKtTest.kt @@ -0,0 +1,54 @@ +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/03_Animal/kotlin/test/ConsoleTest.kt b/03_Animal/kotlin/test/ConsoleTest.kt new file mode 100644 index 00000000..3eebdcbb --- /dev/null +++ b/03_Animal/kotlin/test/ConsoleTest.kt @@ -0,0 +1,35 @@ +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 From f756389037a8abd02c37bde4d2479325741c48e6 Mon Sep 17 00:00:00 2001 From: Paul Holt Date: Sat, 29 Jan 2022 23:48:53 +1100 Subject: [PATCH 2/4] Createa a build_00_utilities project containing testing helpers, including the ConsoleTest abstract class. --- .../jvmTestUtils/kotlin}/test/ConsoleTest.kt | 2 ++ 03_Animal/java/test/AnimalJavaTest.kt | 1 + 03_Animal/kotlin/test/AnimalKtTest.kt | 1 + 03_Animal/kotlin/test/ConsoleTest.kt | 35 ------------------- 4 files changed, 4 insertions(+), 35 deletions(-) rename {03_Animal/java => 00_Utilities/jvmTestUtils/kotlin}/test/ConsoleTest.kt (96%) delete mode 100644 03_Animal/kotlin/test/ConsoleTest.kt diff --git a/03_Animal/java/test/ConsoleTest.kt b/00_Utilities/jvmTestUtils/kotlin/test/ConsoleTest.kt similarity index 96% rename from 03_Animal/java/test/ConsoleTest.kt rename to 00_Utilities/jvmTestUtils/kotlin/test/ConsoleTest.kt index 3eebdcbb..d58af170 100644 --- a/03_Animal/java/test/ConsoleTest.kt +++ b/00_Utilities/jvmTestUtils/kotlin/test/ConsoleTest.kt @@ -1,3 +1,5 @@ +package com.pcholt.console.testutils + import com.google.common.truth.Truth import org.junit.Rule import org.junit.contrib.java.lang.system.SystemOutRule diff --git a/03_Animal/java/test/AnimalJavaTest.kt b/03_Animal/java/test/AnimalJavaTest.kt index 06513481..013655a3 100644 --- a/03_Animal/java/test/AnimalJavaTest.kt +++ b/03_Animal/java/test/AnimalJavaTest.kt @@ -1,3 +1,4 @@ +import com.pcholt.console.testutils.ConsoleTest import org.junit.Test class AnimalJavaTest : ConsoleTest() { diff --git a/03_Animal/kotlin/test/AnimalKtTest.kt b/03_Animal/kotlin/test/AnimalKtTest.kt index 38fae815..e93857ea 100644 --- a/03_Animal/kotlin/test/AnimalKtTest.kt +++ b/03_Animal/kotlin/test/AnimalKtTest.kt @@ -1,3 +1,4 @@ +import com.pcholt.console.testutils.ConsoleTest import org.junit.Test class AnimalKtTest : ConsoleTest() { diff --git a/03_Animal/kotlin/test/ConsoleTest.kt b/03_Animal/kotlin/test/ConsoleTest.kt deleted file mode 100644 index 3eebdcbb..00000000 --- a/03_Animal/kotlin/test/ConsoleTest.kt +++ /dev/null @@ -1,35 +0,0 @@ -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 From 7674913388397e09c1cf15ef39726beb550e2733 Mon Sep 17 00:00:00 2001 From: Paul Holt Date: Sun, 30 Jan 2022 00:11:59 +1100 Subject: [PATCH 3/4] Description added to README --- buildJvm/README.md | 95 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/buildJvm/README.md b/buildJvm/README.md index 4ecba004..3db4014a 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,78 @@ 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 ?"`. From cefe471fdf79fd95589285bb92e4e8fcd0e286db Mon Sep 17 00:00:00 2001 From: Paul Holt Date: Sun, 30 Jan 2022 00:21:25 +1100 Subject: [PATCH 4/4] Description added to README --- buildJvm/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/buildJvm/README.md b/buildJvm/README.md index 3db4014a..c08811b9 100644 --- a/buildJvm/README.md +++ b/buildJvm/README.md @@ -167,3 +167,9 @@ 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 +```