Skip to content

Commit

Permalink
Rewrite data tests and use factories
Browse files Browse the repository at this point in the history
Tests still fail, but fewer than last time.
  • Loading branch information
FWDekker committed Sep 7, 2023
1 parent 1464c42 commit cfbc0b9
Show file tree
Hide file tree
Showing 49 changed files with 1,796 additions and 1,888 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ dependencies {
implementation("com.vdurmont:emoji-java:${properties("emojiVersion")}")
api("org.jetbrains.kotlin:kotlin-reflect")

testImplementation("org.assertj:assertj-core:${properties("assertjVersion")}")
testImplementation("org.assertj:assertj-swing-junit:${properties("assertjSwingVersion")}")
testRuntimeOnly("org.junit.platform:junit-platform-runner:${properties("junitRunnerVersion")}")
testImplementation("org.junit.jupiter:junit-jupiter-api:${properties("junitVersion")}")
testImplementation("org.junit.jupiter:junit-jupiter-engine:${properties("junitVersion")}")
testImplementation("io.kotest:kotest-assertions-core:${properties("kotestVersion")}")
testImplementation("io.kotest:kotest-framework-datatest:${properties("kotestVersion")}")
testImplementation("io.kotest:kotest-runner-junit5:${properties("kotestVersion")}")

detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:${properties("detektVersion")}")
Expand Down
4 changes: 1 addition & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ kotlinApiVersion=1.6
# Dependencies
# * Detekt should also be updated in `plugins` block.
# * RgxGen should also be updated in `StringSchemeEditor` link.
# * Check releases at https://github.com/assertj/assertj-swing/ for valid AssertJ version combinations.
assertjVersion=3.17.2
assertjSwingVersion=3.17.1
dateparserVersion=1.0.11
detektVersion=1.23.0
Expand All @@ -47,5 +45,5 @@ org.gradle.configuration-cache=true
# Kotlin
kotlin.code.style=official
kotlin.stdlib.default.dependency=false
# TODO: Workaround for https://jb.gg/intellij-platform-kotlin-oom
# TODO: Workaround for https://jb.gg/intellij-platform-kotlin-oom, will not be necessary as of Kotlin 1.9.0
kotlin.incremental.useClasspathSnapshot=false
3 changes: 3 additions & 0 deletions src/main/kotlin/com/fwdekker/randomness/Bundle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ object Bundle {

/**
* Returns `true` if [format] is a format string for `this` string, optionally after inserting [args] into [format].
*
* @throws java.util.MissingFormatArgumentException if [args] has fewer arguments than required for [format]
*/
// TODO: Simplify documentation everywhere throughout the project to remove redundant `@param` specifications
fun String.matchesFormat(format: String, vararg args: String) =
Regex(format.format(*args).replace(Regex("%[0-9]+\\\$[Ss]"), ".*")).matches(this)
2 changes: 1 addition & 1 deletion src/main/kotlin/com/fwdekker/randomness/uuid/UuidScheme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ data class UuidScheme(

override fun doValidate() =
if (type !in listOf(TYPE_1, TYPE_4)) Bundle("uuid.error.unknown_type", type)
else arrayDecorator.doValidate()
else affixDecorator.doValidate() ?: arrayDecorator.doValidate()

override fun deepCopy(retainUuid: Boolean) =
copy(
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/fwdekker/randomness/word/WordScheme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ data class WordScheme(

override fun doValidate() =
if (words.isEmpty()) Bundle("word.error.empty_word_list")
else arrayDecorator.doValidate()
else affixDecorator.doValidate() ?: arrayDecorator.doValidate()

override fun deepCopy(retainUuid: Boolean) =
copy(
Expand Down
46 changes: 46 additions & 0 deletions src/test/kotlin/com/fwdekker/randomness/AssertJHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ package com.fwdekker.randomness
import com.fwdekker.randomness.ui.JDateTimeField
import com.intellij.openapi.actionSystem.impl.ActionButton
import com.intellij.ui.dsl.builder.MutableProperty
import io.kotest.core.TestConfiguration
import io.kotest.core.spec.AfterAny
import io.kotest.core.spec.BeforeAny
import io.kotest.core.spec.style.scopes.ContainerScope
import io.kotest.core.test.TestType
import org.assertj.swing.core.GenericTypeMatcher
import org.assertj.swing.edt.GuiActionRunner
import org.assertj.swing.fixture.FrameFixture
Expand Down Expand Up @@ -84,3 +89,44 @@ fun JSpinnerFixture.valueProp() = this.prop({ this.target()::getValue }, { this.
fun JTextComponentFixture.dateTimeProp() = (this.target() as JDateTimeField)::longValue.prop()

fun JTextComponentFixture.textProp() = this.prop({ this.target()::getText }, { this.target()::setText })


/**
* Runs [before] before every test, but, unlike, [TestConfiguration.beforeAny], does not run before other scopes.
*/
fun TestConfiguration.beforeNonContainer(before: BeforeAny) {
this.beforeAny {
if (it.type != TestType.Container)
before(it)
}
}

/**
* Runs [before] before every test, but, unlike, [ContainerScope.beforeAny], does not run before other scopes.
*/
fun ContainerScope.beforeNonContainer(before: BeforeAny) {
this.beforeAny {
if (it.type != TestType.Container)
before(it)
}
}

/**
* Runs [after] after every test, but, unlike, [TestConfiguration.afterAny], does not run after other scopes.
*/
fun TestConfiguration.afterNonContainer(after: AfterAny) {
this.afterAny {
if (it.a.type != TestType.Container)
after(it)
}
}

/**
* Runs [after] after every test, but, unlike, [ContainerScope.afterAny], does not run after other scopes.
*/
fun ContainerScope.afterNonContainer(after: AfterAny) {
this.afterAny {
if (it.a.type != TestType.Container)
after(it)
}
}
51 changes: 17 additions & 34 deletions src/test/kotlin/com/fwdekker/randomness/BundleTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ package com.fwdekker.randomness

import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.data.forAll
import io.kotest.data.headers
import io.kotest.data.row
import io.kotest.data.table
import io.kotest.datatest.withData
import io.kotest.matchers.shouldBe
import java.util.MissingFormatArgumentException
import java.util.MissingResourceException


Expand All @@ -20,7 +19,7 @@ object BundleTest : FunSpec({
}

test("returns the string at the given key") {
Bundle("string.title") shouldBe "string"
Bundle("string.title") shouldBe "String"
}
}

Expand All @@ -34,39 +33,23 @@ object BundleTest : FunSpec({
}
}


context("matchesFormat") {
test("match without args in input") {
forAll(
table(
headers("input", "format", "matches"),
row("no args, matching", "no args, matching", true),
row("no args, mismatching", "different", false),
row("one arg, matching", "one %1\$s, matching", true),
row("one arg, mismatching", "one arg, but %1\$s", false),
row("multiple args, matching", "%1\$s args, %2\$s", true),
row("multiple args, mismatching", "multiple %1\$s, but %2\$s", false),
)
) { input, format, matches -> input.matchesFormat(format) shouldBe matches }
test("throws a MissingFormatArgumentException if insufficient arguments are given") {
shouldThrow<MissingFormatArgumentException> { "anything".matchesFormat("%1\$s") }
}

test("match with args") {
forAll(
table(
headers("input", "format", "args", "matches"),
row("no args, matching", "no args, matching", arrayOf(), true),
row("no args, mismatching", "no args, different", arrayOf(), false),
row("one arg, matching", "%1\$s arg, matching", arrayOf("one"), true),
row("one arg, mismatching", "%1\$s arg, different format", arrayOf("one"), false),
row("one arg, mismatching", "%1\$s arg, mismatching", arrayOf("different"), false),
row("incomplete args, matching", "%1\$s args, %2\$s", arrayOf("incomplete"), true),
row("incomplete args, mismatching", "%1\$s format, %2\$s", arrayOf("incomplete"), false),
row("incomplete args, mismatching", "%1\$s args, %2\$s", arrayOf("different"), false),
row("complete args, matching", "%1\$s args, %2\$s", arrayOf("complete", "matching"), true),
row("complete args, mismatching", "%1\$s format, %2\$s", arrayOf("complete", "mismatching"), false),
row("complete args, mismatching", "%1\$s args, %2\$s", arrayOf("different", "mismatching"), false),
)
) { input, format, args, matches -> input.matchesFormat(format, *args) shouldBe matches }
withData(
nameFn = { it.a },
row("no args, match", "no args, match", arrayOf(), true),
row("no args, mismatch", "no args, different", arrayOf(), false),
row("one arg, match", "%1\$s arg, match", arrayOf("one"), true),
row("one arg, mismatch in format", "%1\$s arg, different format", arrayOf("one"), false),
row("one arg, mismatch in arg", "%1\$s arg, mismatch in arg", arrayOf("different"), false),
row("two args, match", "%1\$s args, %2\$s", arrayOf("two", "match"), true),
row("two args, mismatch in format", "%1\$s args, different %2\$s", arrayOf("two", "format"), false),
row("two args, mismatch in arg", "%1\$s args, mismatch %2\$s", arrayOf("different", "arg"), false),
) { (input, format, args, matches) ->
input.matchesFormat(format, *args) shouldBe matches
}
}
})
71 changes: 71 additions & 0 deletions src/test/kotlin/com/fwdekker/randomness/EditorTestFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.fwdekker.randomness

import com.intellij.ui.dsl.builder.MutableProperty
import io.kotest.core.factory.TestFactory
import io.kotest.core.spec.style.funSpec
import io.kotest.data.Row3
import io.kotest.datatest.withData
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe


/**
* Tests that the [editor] makes no changes by default.
*/
fun editorApplyTestFactory(editor: () -> SchemeEditor<*>) = funSpec {
context("apply") {
test("makes no changes by default") {
val before = editor().scheme.deepCopy(retainUuid = true)

guiRun { editor().apply() }

before shouldBe editor().scheme
}
}
}

/**
* Tests that the [SchemeEditor.apply] method
*/
fun <S : Scheme, T : Any?> editorFieldsTestFactory(
editor: () -> SchemeEditor<S>,
fields: Map<String, Row3<() -> MutableProperty<T>, () -> MutableProperty<T>, T>>,
): TestFactory {
return funSpec {
context("bindings") {
context("'apply' stores the editor's field in the scheme's field") {
withData(fields) { (editorProperty, schemeProperty, value) ->
schemeProperty().get() shouldNotBe value

guiRun { editorProperty().set(value) }
guiRun { editor().apply() }

schemeProperty().get() shouldBe value
}
}

context("'reset' loads the scheme's field into the editor's field") {
withData(fields) { (editorProperty, schemeProperty, value) ->
guiGet { editorProperty().get() } shouldNotBe value

schemeProperty().set(value)
guiRun { editor().reset() }

guiGet { editorProperty().get() } shouldBe value
}
}

context("'addChangeListener' is invoked when the editor's field is changed") {
withData(fields) { (editorProperty, _, value) ->
var invoked = 0
editor().addChangeListener { invoked++ }

guiRun { editorProperty().set(value) }

invoked shouldBeGreaterThanOrEqual 1
}
}
}
}
}
4 changes: 2 additions & 2 deletions src/test/kotlin/com/fwdekker/randomness/ErrorReporterTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ object ErrorReporterTest : FunSpec({
lateinit var reporter: ErrorReporter


beforeEach {
beforeNonContainer {
ideaFixture = IdeaTestFixtureFactory.getFixtureFactory().createBareFixture()
ideaFixture.setUp()

reporter = ErrorReporter()
}

afterEach {
afterNonContainer {
ideaFixture.tearDown()
}

Expand Down
13 changes: 6 additions & 7 deletions src/test/kotlin/com/fwdekker/randomness/IconsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ object TypeIconTest : FunSpec({
FailOnThreadViolationRepaintManager.install()
}

beforeEach {
beforeNonContainer {
image = BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB)
}

Expand Down Expand Up @@ -112,7 +112,7 @@ object OverlayIconTest : FunSpec({
FailOnThreadViolationRepaintManager.install()
}

beforeEach {
beforeNonContainer {
image = BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB)
}

Expand Down Expand Up @@ -177,9 +177,8 @@ object OverlayIconTest : FunSpec({
object OverlayedIconTest : FunSpec({
context("constructor") {
test("fails if the base image is not square") {
shouldThrow<IllegalArgumentException> {
OverlayedIcon(PlainIcon(186, 132), emptyList())
}.message shouldBe "Base must be square."
shouldThrow<IllegalArgumentException> { OverlayedIcon(PlainIcon(186, 132), emptyList()) }
.message shouldBe "Base must be square."
}

test("fails if an overlay is not square") {
Expand All @@ -188,7 +187,7 @@ object OverlayedIconTest : FunSpec({
}

test("fails if overlays have different sizes") {
shouldThrow<IllegalArgumentException> { OverlayedIcon(PlainIcon(), listOf(PlainIcon(32, 32), PlainIcon(34, 34))) }
shouldThrow<IllegalArgumentException> { OverlayedIcon(PlainIcon(), listOf(PlainIcon(), PlainIcon(34, 34))) }
.message shouldBe "All overlays must have same size."
}
}
Expand All @@ -215,7 +214,7 @@ object OverlayedIconTest : FunSpec({
FailOnThreadViolationRepaintManager.install()
}

beforeEach {
beforeNonContainer {
image = BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB)
}

Expand Down
1 change: 1 addition & 0 deletions src/test/kotlin/com/fwdekker/randomness/Matchers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ infix fun IntArray.shouldContainExactly(collection: Array<Int>) =
* Matches a nullable string against the [Bundle] entry at [key] formatted with [args].
*
* @see matchesFormat
* @throws java.util.MissingFormatArgumentException if [args] has fewer arguments than required for [format]
*/
fun matchBundle(key: String, vararg args: String): Matcher<String?> =
Matcher { string ->
Expand Down
12 changes: 6 additions & 6 deletions src/test/kotlin/com/fwdekker/randomness/SchemeEditorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ object SchemeEditorTest : FunSpec({
FailOnThreadViolationRepaintManager.install()
}

beforeEach {
beforeNonContainer {
ideaFixture = IdeaTestFixtureFactory.getFixtureFactory().createBareFixture()
ideaFixture.setUp()
}

afterEach {
afterNonContainer {
frame.cleanUp()
ideaFixture.tearDown()
}
Expand Down Expand Up @@ -84,7 +84,7 @@ object SchemeEditorTest : FunSpec({
panel {
row("Label") {
textField()
radioButton("Button").withName("button").visible(false)
spinner(0..10).withName("spinner").visible(false)
checkBox("Check").withName("checkbox")
}
}
Expand All @@ -100,7 +100,7 @@ object SchemeEditorTest : FunSpec({
panel {
row("Label") {
textField()
radioButton("Button").withName("button").visible(false)
spinner(0..10).withName("spinner").visible(false)
}
}
}
Expand All @@ -122,7 +122,7 @@ object SchemeEditorTest : FunSpec({

guiRun { frame.textBox().target().text = "new" }
frame.textBox().requireText("new")
editor.reset()
guiRun { editor.reset() }

frame.textBox().requireText("old")
}
Expand All @@ -146,7 +146,7 @@ object SchemeEditorTest : FunSpec({

guiRun { frame.textBox().target().text = "new" }
frame.textBox().requireText("new")
editor.reset()
guiRun { editor.reset() }

frame.textBox().requireText("old")
}
Expand Down
Loading

0 comments on commit cfbc0b9

Please sign in to comment.