diff --git a/build.gradle.kts b/build.gradle.kts index 8c20ac38a..20eecd964 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -187,21 +187,17 @@ subprojects { afterEvaluate { val jvmTest = tasks.findByName("jvmTest") if (jvmTest is org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest) { - val jvmTestFix = tasks.create("jvmTestFix", Test::class) - jvmTestFix.group = "verification" - //jvmTestFix.group = jvmTest.group - jvmTestFix.environment("UPDATE_TEST_REF", "true") - jvmTestFix.testClassesDirs = jvmTest.testClassesDirs - jvmTestFix.classpath = jvmTest.classpath - jvmTestFix.bootstrapClasspath = jvmTest.bootstrapClasspath - if (!beforeJava9) { - jvmTest.jvmArgs(*javaAddOpens) - jvmTestFix.jvmArgs(*javaAddOpens) - } - if (headlessTests) { - jvmTest.systemProperty("java.awt.headless", "true") - jvmTestFix.systemProperty("java.awt.headless", "true") + val jvmTestFix = tasks.create("jvmTestFix", Test::class) { + group = "verification" + environment("UPDATE_TEST_REF", "true") + testClassesDirs = jvmTest.testClassesDirs + classpath = jvmTest.classpath + bootstrapClasspath = jvmTest.bootstrapClasspath + if (!beforeJava9) jvmArgs(*javaAddOpens) + if (headlessTests) systemProperty("java.awt.headless", "true") } + if (!beforeJava9) jvmTest.jvmArgs(*javaAddOpens) + if (headlessTests) jvmTest.systemProperty("java.awt.headless", "true") } } diff --git a/korge/src/jvmTest/kotlin/com/soywiz/korge/test/assertEqualsFileReference.kt b/korge/src/jvmTest/kotlin/com/soywiz/korge/test/assertEqualsFileReference.kt index 3846fea5b..380aa9b6c 100644 --- a/korge/src/jvmTest/kotlin/com/soywiz/korge/test/assertEqualsFileReference.kt +++ b/korge/src/jvmTest/kotlin/com/soywiz/korge/test/assertEqualsFileReference.kt @@ -1,15 +1,8 @@ package com.soywiz.korge.test -import java.io.* -import kotlin.test.* +import com.soywiz.korio.test.* // Use ./gradlew jvmTestFix to update these files fun assertEqualsFileReference(path: String, content: String) { - val file = File("src/jvmTest/resources/$path").absoluteFile - if (System.getenv("UPDATE_TEST_REF") == "true") { - file.parentFile.mkdirs() - file.writeText(content) - } - //if (!file.exists()) assertTrue(false, "File '$file' doesn't exists") - assertEquals(file.takeIf { it.exists() }?.readText(), content) + assertEqualsJvmFileReference(path, content) } diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/Dyn.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/Dyn.kt index f08d4a7a8..9650bf8c2 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/Dyn.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/Dyn.kt @@ -169,6 +169,7 @@ inline class Dyn(val value: Any?) : Comparable { interface Invokable { fun invoke(name: String, args: Array): Any? + fun invokeOrThrow(name: String, args: Array): Any? = invoke(name, args) } interface SuspendInvokable { @@ -181,6 +182,12 @@ inline class Dyn(val value: Any?) : Comparable { else -> dynApi.invoke(value, name, args).dyn } + fun dynamicInvokeOrThrow(name: String, vararg args: Any?): Dyn = when (value) { + null -> error("Can't invoke '$name' on null") + is Invokable -> value.invokeOrThrow(name, args).dyn + else -> dynApi.invokeOrThrow(value, name, args).dyn + } + suspend fun suspendDynamicInvoke(name: String, vararg args: Any?): Dyn = when (value) { null -> null.dyn is Invokable -> value.invoke(name, args).dyn diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/DynApi.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/DynApi.kt index be47384b0..b0f6f2799 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/DynApi.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/DynApi.kt @@ -8,6 +8,7 @@ interface DynApi { fun get(instance: Any?, key: String): Any? fun set(instance: Any?, key: String, value: Any?) fun invoke(instance: Any?, key: String, args: Array): Any? + fun invokeOrThrow(instance: Any?, key: String, args: Array): Any? = invoke(instance, key, args) suspend fun suspendGet(instance: Any?, key: String): Any? = get(instance, key) suspend fun suspendSet(instance: Any?, key: String, value: Any?): Unit = set(instance, key, value) diff --git a/korio/src/jvmAndroidMain/kotlin/com/soywiz/korio/dynamic/DynApiJvm.kt b/korio/src/jvmAndroidMain/kotlin/com/soywiz/korio/dynamic/DynApiJvm.kt index fb1465557..272e8b8e5 100644 --- a/korio/src/jvmAndroidMain/kotlin/com/soywiz/korio/dynamic/DynApiJvm.kt +++ b/korio/src/jvmAndroidMain/kotlin/com/soywiz/korio/dynamic/DynApiJvm.kt @@ -86,9 +86,28 @@ internal actual object DynamicInternal : DynApi { } override fun invoke(instance: Any?, key: String, args: Array): Any? { - if (instance == null) return null + return invokeBase(instance, key, args, doThrow = false) + } + + override fun invokeOrThrow(instance: Any?, key: String, args: Array): Any? { + return invokeBase(instance, key, args, doThrow = true) + } + + fun invokeBase(instance: Any?, key: String, args: Array, doThrow: Boolean): Any? { + if (instance == null) { + if (doThrow) error("Can't invoke '$key' on null") + return null + } val method = tryGetMethod(if (instance is Class<*>) instance else instance.javaClass, key, args) - return method?.invoke(if (instance is Class<*>) null else instance, *args) + if (method == null) { + if (doThrow) error("Can't find method '$key' on ${instance::class}") + return null + } + return try { + method.invoke(if (instance is Class<*>) null else instance, *args) + } catch (e: InvocationTargetException) { + throw e.targetException ?: e + } } } diff --git a/korio/src/jvmMain/kotlin/com/soywiz/korio/test/assertEqualsJvmFileReference.kt b/korio/src/jvmMain/kotlin/com/soywiz/korio/test/assertEqualsJvmFileReference.kt new file mode 100644 index 000000000..acc562d34 --- /dev/null +++ b/korio/src/jvmMain/kotlin/com/soywiz/korio/test/assertEqualsJvmFileReference.kt @@ -0,0 +1,46 @@ +package com.soywiz.korio.test + +import com.soywiz.korio.dynamic.* +import java.io.* + +/** + * Checks that a string matches a file in `src/jvmTest/resources`. + * When environment variable UPDATE_TEST_REF=true, instead of checking, + * this functions updates the file with the expected content. + * + * This is similar to jest's snapshot testing: + * + * --- + * + * To simplify its usage, it is recommended to define a `jvmTestFix` gradle task as follows: + * + * Use ./gradlew jvmTestFix to update these files + * + * jvmTestFix gradle task should add environment("UPDATE_TEST_REF", "true") + * + * ```kotlin + * val jvmTestFix = tasks.create("jvmTestFix", Test::class) { + * group = "verification" + * environment("UPDATE_TEST_REF", "true") + * testClassesDirs = jvmTest.testClassesDirs + * classpath = jvmTest.classpath + * bootstrapClasspath = jvmTest.bootstrapClasspath + * systemProperty("java.awt.headless", "true") + * } + * ``` + */ +fun assertEqualsJvmFileReference(path: String, content: String) { + val file = File("src/jvmTest/resources/$path").absoluteFile + if (System.getenv("UPDATE_TEST_REF") == "true") { + file.parentFile.mkdirs() + file.writeText(content) + } + + val message: String? = null + val expected: String = file.takeIf { it.exists() }?.readText() ?: "" + val actual: String = content + + Dyn.global["kotlin.test.AssertionsKt"] + .dynamicInvokeOrThrow("getAsserter") + .dynamicInvokeOrThrow("assertEquals", message, expected, actual) +}