Skip to content

Commit

Permalink
Merge pull request #541 from pcholt/main
Browse files Browse the repository at this point in the history
JVM Test framework
  • Loading branch information
coding-horror committed Jan 29, 2022
2 parents 6d28b10 + b34014c commit f1e2ea2
Show file tree
Hide file tree
Showing 17 changed files with 302 additions and 30 deletions.
37 changes: 37 additions & 0 deletions 00_Utilities/jvmTestUtils/kotlin/test/ConsoleTest.kt
Original file line number Diff line number Diff line change
@@ -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(), " ")
}
6 changes: 3 additions & 3 deletions 03_Animal/java/Animal.java → 03_Animal/java/src/Animal.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ public static void main(String[] args) {
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);
Expand Down
55 changes: 55 additions & 0 deletions 03_Animal/java/test/AnimalJavaTest.kt
Original file line number Diff line number Diff line change
@@ -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.
"""
}

File renamed without changes.
55 changes: 55 additions & 0 deletions 03_Animal/kotlin/test/AnimalKtTest.kt
Original file line number Diff line number Diff line change
@@ -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.
"""
}

101 changes: 91 additions & 10 deletions buildJvm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
```
22 changes: 15 additions & 7 deletions buildJvm/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand All @@ -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) =
Expand Down
7 changes: 7 additions & 0 deletions buildJvm/build_00_utilities/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
sourceSets {
test {
java {
srcDirs "../../$testSource"
}
}
}
1 change: 1 addition & 0 deletions buildJvm/build_00_utilities/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
testSource=00_Utilities/jvmTestUtils/kotlin/test
11 changes: 11 additions & 0 deletions buildJvm/build_01_Acey_Ducey_java17/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
sourceSets {
main {
java {
srcDirs "../../$gameSource"
}
}
}

application {
mainClass = gameMain
}
2 changes: 2 additions & 0 deletions buildJvm/build_01_Acey_Ducey_java17/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gameSource=01_Acey_Ducey/java/src
gameMain=AceyDucey17
9 changes: 9 additions & 0 deletions buildJvm/build_03_Animal_java/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@ sourceSets {
srcDirs "../../$gameSource"
}
}
test {
java {
srcDirs "../../$gameTest"
}
}
}

application {
mainClass = gameMain
}

dependencies {
testImplementation(project(":build_00_utilities").sourceSets.test.output)
}
3 changes: 2 additions & 1 deletion buildJvm/build_03_Animal_java/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
gameSource=03_Animal/java
gameSource=03_Animal/java/src
gameTest=03_Animal/java/test
gameMain=Animal
9 changes: 9 additions & 0 deletions buildJvm/build_03_Animal_kotlin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@ sourceSets {
srcDirs "../../$gameSource"
}
}
test {
java {
srcDirs "../../$gameTest"
}
}
}

application {
mainClass = gameMain
}

dependencies {
testImplementation(project(":build_00_utilities").sourceSets.test.output)
}
3 changes: 2 additions & 1 deletion buildJvm/build_03_Animal_kotlin/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
gameSource=03_Animal/kotlin
gameSource=03_Animal/kotlin/src
gameTest=03_Animal/kotlin/test
gameMain=AnimalKt

0 comments on commit f1e2ea2

Please sign in to comment.