diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 913c3029eb1..81b8fa5f3c6 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -50,7 +50,7 @@ jobs: args: -P"kotest_enabledPublicationNamePrefixes=linux" # Wasm - os: ubuntu-latest - args: -P"kotest_enabledPublicationNamePrefixes=wasmJs" + args: -P"kotest_enabledPublicationNamePrefixes=wasm" # Android - os: ubuntu-latest args: -P"kotest_enabledPublicationNamePrefixes=android" diff --git a/.github/workflows/release_all.yml b/.github/workflows/release_all.yml index 828c518174d..1f8dc6c4d69 100644 --- a/.github/workflows/release_all.yml +++ b/.github/workflows/release_all.yml @@ -49,7 +49,7 @@ jobs: args: -P"kotest_enabledPublicationNamePrefixes=linux" # Wasm - os: ubuntu-latest - args: -P"kotest_enabledPublicationNamePrefixes=wasmJs" + args: -P"kotest_enabledPublicationNamePrefixes=wasm" # Publish 'common' components (KotlinMultiplatform,jvm,js) only on Linux, to avoid duplicate publications - os: ubuntu-latest args: -P"kotest_enabledPublicationNamePrefixes=android" diff --git a/documentation/docs/extensions/html_reporter.md b/documentation/docs/extensions/html_reporter.md index be1cc68ac85..0b1f725d03a 100644 --- a/documentation/docs/extensions/html_reporter.md +++ b/documentation/docs/extensions/html_reporter.md @@ -25,7 +25,7 @@ class ProjectConfig : AbstractProjectConfig() { override val specExecutionOrder = SpecExecutionOrder.Annotated - override fun extensions(): List = listOf( + override val extensions): List = listOf( JunitXmlReporter( includeContainers = false, useTestPathAsName = true, diff --git a/documentation/docs/extensions/junit_xml.md b/documentation/docs/extensions/junit_xml.md index a51cd0d2b50..6e93c9fddf3 100644 --- a/documentation/docs/extensions/junit_xml.md +++ b/documentation/docs/extensions/junit_xml.md @@ -26,7 +26,7 @@ To configure in your project, you need to add the `JunitXmlReporter` using [proj ```kotlin class MyConfig : AbstractProjectConfig() { - override fun extensions(): List = listOf( + override val extensions: List = listOf( JunitXmlReporter( includeContainers = false, // don't write out status for all tests useTestPathAsName = true, // use the full test path (ie, includes parent test names) diff --git a/documentation/docs/extensions/system.md b/documentation/docs/extensions/system.md index d6d819f442f..d6183056388 100644 --- a/documentation/docs/extensions/system.md +++ b/documentation/docs/extensions/system.md @@ -10,7 +10,8 @@ slug: system_extensions.html ## System Extensions -If you need to test code that uses `java.lang.System`, Kotest provides extensions that can alter the system and restore it after each test. This extension is only available on the JVM. +If you need to test code that uses `java.lang.System`, Kotest provides extensions that can alter the system and restore it after each test. +This extension is only available on the JVM. To use this extension, add the dependency to your project: @@ -19,86 +20,20 @@ To use this extension, add the dependency to your project: ```kotlin -io.kotest:kotest-extensions-jvm:${version} +io.kotest:kotest-extensions:${version} ``` :::caution This extension does not support concurrent test execution. Due to the JVM specification there can only be one instance of these extensions running (For example: Only one Environment map must exist). If you try to run more than one instance at a time, the result is undefined. ::: -### System Environment - -With *System Environment Extension* you can simulate how the System Environment is behaving. That is, what you're obtaining from `System.getenv()`. - -Kotest provides some extension functions that provides a System Environment in a specific scope: - -```kotlin -test("foo") { - withEnvironment("FooKey", "BarValue") { - System.getenv("FooKey") shouldBe "BarValue" // System environment overridden! - } -} -``` - -:::info -To use `withEnvironment` with JDK17+ you need to add `--add-opens=java.base/java.util=ALL-UNNAMED` -and `--add-opens=java.base/java.lang=ALL-UNNAMED` to the arguments for the JVM that runs the tests. - -If you run tests with gradle, you can add the following to your `build.gradle.kts`: - -```kotlin -tasks.withType().configureEach { - jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED", "--add-opens=java.base/java.lang=ALL-UNNAMED") -} -``` -::: - -You can also use multiple values in this extension, through a map or list of pairs. - -```kotlin -test("foo") { - withEnvironment(mapOf("FooKey" to "BarValue", "BarKey" to "FooValue")) { - // Use FooKey and BarKey - } -} -``` - -These functions will add the keys and values if they're not currently present in the environment, and will override them if they are. Any keys untouched by the function will remain in the environment, and won't be messed with. - -Instead of extension functions, you can also use the provided Listeners to apply these functionalities in a bigger scope. There's an alternative for the Spec/Per test level, and an alternative for the Project Level. - -```kotlin -class MyTest : FreeSpec() { - override fun listeners() = listOf( - SystemEnvironmentTestListener( - environment = mapOf( - "foo" to "bar", - "bar" to null, // Useful for resetting environment variables - ), - mode = OverrideMode.SetOrOverride, - ) - ) - - init { - "MyTest" { - System.getenv("foo") shouldBe "bar" - System.getenv("bar") shouldBe null - } - } -} -``` - -```kotlin -class ProjectConfig : AbstractProjectConfig() { - override fun listeners(): List = listOf(SystemEnvironmentProjectListener("foo", "bar")) -} -``` - - ### System Property Extension -In the same fashion as the Environment Extensions, you can override the System Properties (`System.getProperties()`): +You can override the System Properties (`System.getProperties()`) by either using a listener at the spec level, +or by using the `withSystemProperty` function to wrap any arbitrary code. + +With the function: ```kotlin withSystemProperty("foo", "bar") { @@ -106,11 +41,11 @@ withSystemProperty("foo", "bar") { } ``` -And with similar Listeners: +And as an extension: ```kotlin class MyTest : FreeSpec() { - override fun listeners() = listOf(SystemPropertyListener("foo", "bar")) + override val extensions = listOf(SystemPropertyTestListener("foo", "bar")) init { "MyTest" { @@ -120,50 +55,6 @@ class MyTest : FreeSpec() { } ``` - - -### System Security Manager - -Similarly, with System Security Manager you can override the System Security Manager (`System.getSecurityManager()`) - -```kotlin -withSecurityManager(myManager) { - // Usage of security manager -} -``` - -And the Listeners: - -```kotlin -class MyTest : FreeSpec() { - override fun listeners() = listOf(SecurityManagerListener(myManager)) - - init { - // Use my security manager - } -} -``` - -### System Exit Extensions - -Sometimes you want to test that your code calls `System.exit`. For that you can use the `System Exit Listeners`. The Listener will throw an exception when the `System.exit` is called, allowing you to catch it and verify: - -```kotlin -class MyTest : FreeSpec() { - override fun listeners() = listOf(SpecSystemExitListener) - - init { - "Catch exception" { - val thrown: SystemExitException = shouldThrow { - System.exit(22) - } - - thrown.exitCode shouldBe 22 - } - } -} -``` - ### No-stdout / no-stderr listeners Maybe you want to guarantee that you didn't leave any debug messages around, or that you're always using a Logger in your logging. @@ -172,7 +63,7 @@ For that, Kotest provides you with `NoSystemOutListener` and `NoSystemErrListene ```kotlin // In Project or in Spec -override fun listeners() = listOf(NoSystemOutListener, NoSystemErrListener) +override val extensions = listOf(NoSystemOutListener, NoSystemErrListener) ``` ### Locale/Timezone listeners @@ -190,11 +81,11 @@ withDefaultTimeZone(TimeZone.getTimeZone(ZoneId.of("America/Sao_Paulo"))) { } ``` -And with the listeners +And as an extension: ```kotlin // In Project or in Spec -override fun listeners() = listOf( +override val extensions = listOf( LocaleTestListener(Locale.FRANCE), TimeZoneTestListener(TimeZone.getTimeZone(ZoneId.of("America/Sao_Paulo"))) ) diff --git a/documentation/docs/extensions/wiremock.md b/documentation/docs/extensions/wiremock.md index 21bc5f12180..f0c028e5031 100644 --- a/documentation/docs/extensions/wiremock.md +++ b/documentation/docs/extensions/wiremock.md @@ -37,7 +37,7 @@ For example: class SomeTest : FunSpec({ val customerServiceServer = WireMockServer(9000) - listener(WireMockListener(customerServiceServer, ListenerMode.PER_SPEC)) + extension(WireMockListener(customerServiceServer, ListenerMode.PER_SPEC)) test("let me get customer information") { customerServiceServer.stubFor( @@ -62,7 +62,7 @@ You can use `WireMockServer.perSpec(customerServiceServer)` to achieve same resu class SomeTest : FunSpec({ val customerServiceServer = WireMockServer(9000) - listener(WireMockListener(customerServiceServer, ListenerMode.PER_TEST)) + extension(WireMockListener(customerServiceServer, ListenerMode.PER_TEST)) test("let me get customer information") { customerServiceServer.stubFor( diff --git a/documentation/docs/framework/setup.mdx b/documentation/docs/framework/setup.mdx index bf3ad8ce6ff..42f1dfcc0fb 100644 --- a/documentation/docs/framework/setup.mdx +++ b/documentation/docs/framework/setup.mdx @@ -24,6 +24,7 @@ values={[ {label: 'Kotlin/WasmJS', value: 'WasmJS'}, {label: 'Kotlin/Native', value: 'Native'}, {label: 'Android', value: 'Android'}, +{label: 'Multiplatform', value: 'Multiplatform'}, ]}> @@ -95,7 +96,7 @@ Add the `kotest-framework-engine` dependency to your `commonTest` or `jsTest` so kotlin { js() sourceSets { - jsTest { + commonTest { dependencies { implementation("io.kotest:kotest-framework-engine:") } @@ -136,7 +137,7 @@ Add the `kotest-framework-engine` dependency to your `commonTest` or `wasmJsTest kotlin { wasmJs() sourceSets { - wasmJsTest { + commonTest { dependencies { implementation("io.kotest:kotest-framework-engine:") } @@ -178,7 +179,7 @@ Add the `kotest-framework-engine` dependency to your `commonTest`, `nativeTest` kotlin { linuxX64() // add any supported native target sourceSets { - nativeTest { + commonTest { dependencies { implementation("io.kotest:kotest-framework-engine:") } @@ -235,7 +236,40 @@ A working multiplatform project with JVM, JS and native targets, and unit and da https://github.com/kotest/kotest-examples ::: -To configure the test framework for multiplatform, combie the steps for JVM, JS and Native as detailed in the previous tabs. +Add the [Kotest gradle plugin](https://plugins.gradle.org/plugin/io.kotest) and Google KSP plugin to to your build. + +For example: + +```kotlin +plugins { + id("io.kotest") version "" + id("com.google.devtools.ksp") version "-" +} +``` + +Add the `kotest-framework-engine` dependency to your `commonTest` source set: + +```kotlin +kotlin { + sourceSets { + commonTest { + dependencies { + implementation("io.kotest:kotest-framework-engine:") + } + } + } +} +``` + +Tests can be placed in either `commonTest` or a platform specific directory such as `jsTest` or `macosX64Test` etc. +Run your tests using the gradle `check` task, or a platform specific test task such as `macosX64Test` + +:::note +The JS, Wasm and native test engines are feature limited when compared to the JVM test engine. The major restriction is that annotation +based configuration will not work as Kotlin does not expose annotations at runtime to non-JVM platforms. +::: + + diff --git a/documentation/docs/release_6.0.md b/documentation/docs/release_6.0.md index a358df39bb2..09ae726e611 100644 --- a/documentation/docs/release_6.0.md +++ b/documentation/docs/release_6.0.md @@ -188,6 +188,11 @@ The following isolation modes are now deprecated due to undefined behavior in ed It is recommended to use `InstancePerRoot` instead. +### API changes + +* Removed `io.kotest.assertions.print.Print.print(A, level)` in favor of the now-undeprecated `print(A)`. +* Renamed `io.kotest.matchers.maps.contain` to `io.kotest.matchers.maps.mapcontain`. + ## Improvements ### Coroutine Debug Probes diff --git a/documentation/versioned_docs/version-6.0/extensions/html_reporter.md b/documentation/versioned_docs/version-6.0/extensions/html_reporter.md index be1cc68ac85..0b1f725d03a 100644 --- a/documentation/versioned_docs/version-6.0/extensions/html_reporter.md +++ b/documentation/versioned_docs/version-6.0/extensions/html_reporter.md @@ -25,7 +25,7 @@ class ProjectConfig : AbstractProjectConfig() { override val specExecutionOrder = SpecExecutionOrder.Annotated - override fun extensions(): List = listOf( + override val extensions): List = listOf( JunitXmlReporter( includeContainers = false, useTestPathAsName = true, diff --git a/documentation/versioned_docs/version-6.0/extensions/junit_xml.md b/documentation/versioned_docs/version-6.0/extensions/junit_xml.md index a51cd0d2b50..6e93c9fddf3 100644 --- a/documentation/versioned_docs/version-6.0/extensions/junit_xml.md +++ b/documentation/versioned_docs/version-6.0/extensions/junit_xml.md @@ -26,7 +26,7 @@ To configure in your project, you need to add the `JunitXmlReporter` using [proj ```kotlin class MyConfig : AbstractProjectConfig() { - override fun extensions(): List = listOf( + override val extensions: List = listOf( JunitXmlReporter( includeContainers = false, // don't write out status for all tests useTestPathAsName = true, // use the full test path (ie, includes parent test names) diff --git a/documentation/versioned_docs/version-6.0/extensions/system.md b/documentation/versioned_docs/version-6.0/extensions/system.md index d6d819f442f..d6183056388 100644 --- a/documentation/versioned_docs/version-6.0/extensions/system.md +++ b/documentation/versioned_docs/version-6.0/extensions/system.md @@ -10,7 +10,8 @@ slug: system_extensions.html ## System Extensions -If you need to test code that uses `java.lang.System`, Kotest provides extensions that can alter the system and restore it after each test. This extension is only available on the JVM. +If you need to test code that uses `java.lang.System`, Kotest provides extensions that can alter the system and restore it after each test. +This extension is only available on the JVM. To use this extension, add the dependency to your project: @@ -19,86 +20,20 @@ To use this extension, add the dependency to your project: ```kotlin -io.kotest:kotest-extensions-jvm:${version} +io.kotest:kotest-extensions:${version} ``` :::caution This extension does not support concurrent test execution. Due to the JVM specification there can only be one instance of these extensions running (For example: Only one Environment map must exist). If you try to run more than one instance at a time, the result is undefined. ::: -### System Environment - -With *System Environment Extension* you can simulate how the System Environment is behaving. That is, what you're obtaining from `System.getenv()`. - -Kotest provides some extension functions that provides a System Environment in a specific scope: - -```kotlin -test("foo") { - withEnvironment("FooKey", "BarValue") { - System.getenv("FooKey") shouldBe "BarValue" // System environment overridden! - } -} -``` - -:::info -To use `withEnvironment` with JDK17+ you need to add `--add-opens=java.base/java.util=ALL-UNNAMED` -and `--add-opens=java.base/java.lang=ALL-UNNAMED` to the arguments for the JVM that runs the tests. - -If you run tests with gradle, you can add the following to your `build.gradle.kts`: - -```kotlin -tasks.withType().configureEach { - jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED", "--add-opens=java.base/java.lang=ALL-UNNAMED") -} -``` -::: - -You can also use multiple values in this extension, through a map or list of pairs. - -```kotlin -test("foo") { - withEnvironment(mapOf("FooKey" to "BarValue", "BarKey" to "FooValue")) { - // Use FooKey and BarKey - } -} -``` - -These functions will add the keys and values if they're not currently present in the environment, and will override them if they are. Any keys untouched by the function will remain in the environment, and won't be messed with. - -Instead of extension functions, you can also use the provided Listeners to apply these functionalities in a bigger scope. There's an alternative for the Spec/Per test level, and an alternative for the Project Level. - -```kotlin -class MyTest : FreeSpec() { - override fun listeners() = listOf( - SystemEnvironmentTestListener( - environment = mapOf( - "foo" to "bar", - "bar" to null, // Useful for resetting environment variables - ), - mode = OverrideMode.SetOrOverride, - ) - ) - - init { - "MyTest" { - System.getenv("foo") shouldBe "bar" - System.getenv("bar") shouldBe null - } - } -} -``` - -```kotlin -class ProjectConfig : AbstractProjectConfig() { - override fun listeners(): List = listOf(SystemEnvironmentProjectListener("foo", "bar")) -} -``` - - ### System Property Extension -In the same fashion as the Environment Extensions, you can override the System Properties (`System.getProperties()`): +You can override the System Properties (`System.getProperties()`) by either using a listener at the spec level, +or by using the `withSystemProperty` function to wrap any arbitrary code. + +With the function: ```kotlin withSystemProperty("foo", "bar") { @@ -106,11 +41,11 @@ withSystemProperty("foo", "bar") { } ``` -And with similar Listeners: +And as an extension: ```kotlin class MyTest : FreeSpec() { - override fun listeners() = listOf(SystemPropertyListener("foo", "bar")) + override val extensions = listOf(SystemPropertyTestListener("foo", "bar")) init { "MyTest" { @@ -120,50 +55,6 @@ class MyTest : FreeSpec() { } ``` - - -### System Security Manager - -Similarly, with System Security Manager you can override the System Security Manager (`System.getSecurityManager()`) - -```kotlin -withSecurityManager(myManager) { - // Usage of security manager -} -``` - -And the Listeners: - -```kotlin -class MyTest : FreeSpec() { - override fun listeners() = listOf(SecurityManagerListener(myManager)) - - init { - // Use my security manager - } -} -``` - -### System Exit Extensions - -Sometimes you want to test that your code calls `System.exit`. For that you can use the `System Exit Listeners`. The Listener will throw an exception when the `System.exit` is called, allowing you to catch it and verify: - -```kotlin -class MyTest : FreeSpec() { - override fun listeners() = listOf(SpecSystemExitListener) - - init { - "Catch exception" { - val thrown: SystemExitException = shouldThrow { - System.exit(22) - } - - thrown.exitCode shouldBe 22 - } - } -} -``` - ### No-stdout / no-stderr listeners Maybe you want to guarantee that you didn't leave any debug messages around, or that you're always using a Logger in your logging. @@ -172,7 +63,7 @@ For that, Kotest provides you with `NoSystemOutListener` and `NoSystemErrListene ```kotlin // In Project or in Spec -override fun listeners() = listOf(NoSystemOutListener, NoSystemErrListener) +override val extensions = listOf(NoSystemOutListener, NoSystemErrListener) ``` ### Locale/Timezone listeners @@ -190,11 +81,11 @@ withDefaultTimeZone(TimeZone.getTimeZone(ZoneId.of("America/Sao_Paulo"))) { } ``` -And with the listeners +And as an extension: ```kotlin // In Project or in Spec -override fun listeners() = listOf( +override val extensions = listOf( LocaleTestListener(Locale.FRANCE), TimeZoneTestListener(TimeZone.getTimeZone(ZoneId.of("America/Sao_Paulo"))) ) diff --git a/documentation/versioned_docs/version-6.0/extensions/wiremock.md b/documentation/versioned_docs/version-6.0/extensions/wiremock.md index 21bc5f12180..f0c028e5031 100644 --- a/documentation/versioned_docs/version-6.0/extensions/wiremock.md +++ b/documentation/versioned_docs/version-6.0/extensions/wiremock.md @@ -37,7 +37,7 @@ For example: class SomeTest : FunSpec({ val customerServiceServer = WireMockServer(9000) - listener(WireMockListener(customerServiceServer, ListenerMode.PER_SPEC)) + extension(WireMockListener(customerServiceServer, ListenerMode.PER_SPEC)) test("let me get customer information") { customerServiceServer.stubFor( @@ -62,7 +62,7 @@ You can use `WireMockServer.perSpec(customerServiceServer)` to achieve same resu class SomeTest : FunSpec({ val customerServiceServer = WireMockServer(9000) - listener(WireMockListener(customerServiceServer, ListenerMode.PER_TEST)) + extension(WireMockListener(customerServiceServer, ListenerMode.PER_TEST)) test("let me get customer information") { customerServiceServer.stubFor( diff --git a/documentation/versioned_docs/version-6.0/framework/setup.mdx b/documentation/versioned_docs/version-6.0/framework/setup.mdx index dbfc932668e..07fc59d30c6 100644 --- a/documentation/versioned_docs/version-6.0/framework/setup.mdx +++ b/documentation/versioned_docs/version-6.0/framework/setup.mdx @@ -24,6 +24,7 @@ values={[ {label: 'Kotlin/WasmJS', value: 'WasmJS'}, {label: 'Kotlin/Native', value: 'Native'}, {label: 'Android', value: 'Android'}, +{label: 'Multiplatform', value: 'Multiplatform'}, ]}> @@ -95,7 +96,7 @@ Add the `kotest-framework-engine` dependency to your `commonTest` or `jsTest` so kotlin { js() sourceSets { - jsTest { + commonTest { dependencies { implementation("io.kotest:kotest-framework-engine:") } @@ -136,7 +137,7 @@ Add the `kotest-framework-engine` dependency to your `commonTest` or `wasmJsTest kotlin { wasmJs() sourceSets { - wasmJsTest { + commonTest { dependencies { implementation("io.kotest:kotest-framework-engine:") } @@ -178,7 +179,7 @@ Add the `kotest-framework-engine` dependency to your `commonTest`, `nativeTest` kotlin { linuxX64() // add any supported native target sourceSets { - nativeTest { + commonTest { dependencies { implementation("io.kotest:kotest-framework-engine:") } @@ -227,6 +228,13 @@ https://github.com/kotest/kotest-examples ::: + + + + + +To configure the test framework for multiplatform, combie the steps for JVM, JS and Native as detailed in the previous tabs. + @@ -235,7 +243,38 @@ A working multiplatform project with JVM, JS and native targets, and unit and da https://github.com/kotest/kotest-examples ::: -To configure the test framework for multiplatform, combie the steps for JVM, JS and Native as detailed in the previous tabs. +Add the [Kotest gradle plugin](https://plugins.gradle.org/plugin/io.kotest) and Google KSP plugin to to your build. + +For example: + +```kotlin +plugins { + id("io.kotest") version "" + id("com.google.devtools.ksp") version "-" +} +``` + +Add the `kotest-framework-engine` dependency to your `commonTest` source set: + +```kotlin +kotlin { + sourceSets { + commonTest { + dependencies { + implementation("io.kotest:kotest-framework-engine:") + } + } + } +} +``` + +Tests can be placed in either `commonTest` or a platform specific directory such as `jsTest` or `macosX64Test` etc. +Run your tests using the gradle `check` task, or a platform specific test task such as `macosX64Test` + +:::note +The JS, Wasm and native test engines are feature limited when compared to the JVM test engine. The major restriction is that annotation +based configuration will not work as Kotlin does not expose annotations at runtime to non-JVM platforms. +::: diff --git a/kotest-assertions/kotest-assertions-core/api/kotest-assertions-core.api b/kotest-assertions/kotest-assertions-core/api/kotest-assertions-core.api index 02cf1a956a1..4df39fcc889 100644 --- a/kotest-assertions/kotest-assertions-core/api/kotest-assertions-core.api +++ b/kotest-assertions/kotest-assertions-core/api/kotest-assertions-core.api @@ -1772,7 +1772,9 @@ public final class io/kotest/matchers/compose/AnyKt { public final class io/kotest/matchers/concurrent/ConcurrentKt { public static final fun shouldCompleteWithin (JLjava/util/concurrent/TimeUnit;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object; + public static final fun shouldCompleteWithin-VtjQ1oo (JLkotlin/jvm/functions/Function0;)Ljava/lang/Object; public static final fun shouldTimeout (JLjava/util/concurrent/TimeUnit;Lkotlin/jvm/functions/Function0;)V + public static final fun shouldTimeout-VtjQ1oo (JLkotlin/jvm/functions/Function0;)V } public final class io/kotest/matchers/concurrent/ThreadsKt { @@ -2955,14 +2957,13 @@ public final class io/kotest/matchers/resource/ByteArrayMatchersKt { } public final class io/kotest/matchers/resource/StringMatchersKt { - public static final fun matchResource (Ljava/lang/String;Lkotlin/jvm/functions/Function1;Z)Lio/kotest/matchers/Matcher; - public static final fun resourceAsString (Ljava/lang/String;)Ljava/lang/String; + public static final fun matchResource (Ljava/lang/String;Lkotlin/jvm/functions/Function1;ZZ)Lio/kotest/matchers/Matcher; public static final fun shouldMatchResource (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - public static final fun shouldMatchResource (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Z)Ljava/lang/String; - public static synthetic fun shouldMatchResource$default (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ZILjava/lang/Object;)Ljava/lang/String; + public static final fun shouldMatchResource (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ZZ)Ljava/lang/String; + public static synthetic fun shouldMatchResource$default (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ZZILjava/lang/Object;)Ljava/lang/String; public static final fun shouldNotMatchResource (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - public static final fun shouldNotMatchResource (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Z)Ljava/lang/String; - public static synthetic fun shouldNotMatchResource$default (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ZILjava/lang/Object;)Ljava/lang/String; + public static final fun shouldNotMatchResource (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ZZ)Ljava/lang/String; + public static synthetic fun shouldNotMatchResource$default (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ZZILjava/lang/Object;)Ljava/lang/String; } public final class io/kotest/matchers/result/FailureMatcher : io/kotest/matchers/Matcher { diff --git a/kotest-assertions/kotest-assertions-core/src/commonTest/kotlin/com/sksamuel/kotest/matchers/concurrent/suspension/ConcurrentTest.kt b/kotest-assertions/kotest-assertions-core/src/commonTest/kotlin/com/sksamuel/kotest/matchers/concurrent/suspension/ConcurrentTest.kt index 314e561423c..6f0f3aec857 100644 --- a/kotest-assertions/kotest-assertions-core/src/commonTest/kotlin/com/sksamuel/kotest/matchers/concurrent/suspension/ConcurrentTest.kt +++ b/kotest-assertions/kotest-assertions-core/src/commonTest/kotlin/com/sksamuel/kotest/matchers/concurrent/suspension/ConcurrentTest.kt @@ -2,11 +2,13 @@ package com.sksamuel.kotest.matchers.concurrent.suspension import io.kotest.assertions.shouldFail import io.kotest.assertions.throwables.shouldNotThrowAny +import io.kotest.assertions.throwables.shouldThrowAny import io.kotest.common.testTimeSource import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.concurrent.suspension.shouldCompleteBetween import io.kotest.matchers.concurrent.suspension.shouldCompleteWithin import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain import kotlinx.coroutines.delay import kotlin.time.Duration.Companion.seconds import kotlin.time.measureTime @@ -40,6 +42,14 @@ class ConcurrentTest : FunSpec({ testDuration shouldBe 1.seconds } + test("shouldCompleteWithin should not swallow threads #4892") { + shouldThrowAny { + shouldCompleteWithin(5.seconds) { + 1 shouldBe 2 + } + }.message.shouldContain("""expected:<2> but was:<1>""") + } + test("shouldCompleteBetween - should not fail when operation completes in given time range") { val testDuration = testTimeSource().measureTime { shouldNotThrowAny { diff --git a/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/concurrent/concurrent.kt b/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/concurrent/concurrent.kt index 85c76d72327..6f1466c1756 100644 --- a/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/concurrent/concurrent.kt +++ b/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/concurrent/concurrent.kt @@ -5,15 +5,27 @@ import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicReference import kotlin.concurrent.thread +import kotlin.time.Duration + +fun shouldCompleteWithin(duration: Duration, thunk: () -> A): A { + return shouldCompleteWithin(duration.inWholeMilliseconds, TimeUnit.MILLISECONDS, thunk) +} fun shouldCompleteWithin(timeout: Long, unit: TimeUnit, thunk: () -> A): A { val ref = AtomicReference(null) + val throwableRef = AtomicReference(null) + val latch = CountDownLatch(1) val t = thread { - val a = thunk() - ref.set(a) - latch.countDown() + try { + val a = thunk() + ref.set(a) + } catch (t: Throwable) { + throwableRef.set(t) + } finally { + latch.countDown() + } } if (!latch.await(timeout, unit)) { @@ -21,9 +33,14 @@ fun shouldCompleteWithin(timeout: Long, unit: TimeUnit, thunk: () -> A): A { AssertionErrorBuilder.fail("Test should have completed within $timeout/$unit") } + throwableRef.get()?.let { throw it } return ref.get() } +fun shouldTimeout(duration: Duration, thunk: () -> A) { + return shouldTimeout(duration.inWholeMilliseconds, TimeUnit.MILLISECONDS, thunk) +} + fun shouldTimeout(timeout: Long, unit: TimeUnit, thunk: () -> A) { val latch = CountDownLatch(1) diff --git a/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/equality/reflection.kt b/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/equality/reflection.kt index a7eb457fb08..5e040354a0a 100644 --- a/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/equality/reflection.kt +++ b/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/equality/reflection.kt @@ -462,7 +462,7 @@ internal fun comparisonToUse( (expected is List<*> && actual is List<*>) -> FieldComparison.LIST (expected is Map<*, *> && actual is Map<*, *>) -> FieldComparison.MAP (expected is Set<*> && actual is Set<*>) -> FieldComparison.SET - (expected is Array<*> && actual is Array<*>) -> FieldComparison.ARRAY + (isArray(expected) && isArray(actual)) -> FieldComparison.ARRAY typeIsJavaOrKotlinBuiltIn(expected) || typeIsJavaOrKotlinBuiltIn(actual) -> FieldComparison.DEFAULT useDefaultEqualForFields.contains(expected::class.java.canonicalName) || useDefaultEqualForFields.contains(actual::class.java.canonicalName) -> FieldComparison.DEFAULT @@ -471,6 +471,21 @@ internal fun comparisonToUse( else -> FieldComparison.RECURSIVE } +internal fun isArray(value: Any?) = when (value) { + null -> false + is Array<*> -> true + value::class.java.isArray -> true + is ByteArray -> true + is ShortArray -> true + is IntArray -> true + is LongArray -> true + is FloatArray -> true + is DoubleArray -> true + is CharArray -> true + is BooleanArray -> true + else -> false +} + internal fun isEnum(value: Any?) = when (value) { null -> false is Enum<*> -> true diff --git a/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/resource/byteArrayMatchers.kt b/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/resource/byteArrayMatchers.kt index b71ab8fe467..567c3a9e013 100644 --- a/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/resource/byteArrayMatchers.kt +++ b/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/resource/byteArrayMatchers.kt @@ -16,7 +16,7 @@ import kotlin.io.path.writeBytes infix fun ByteArray.shouldMatchResource( path: String ): ByteArray { - this should matchResource(path, ::be) + this should matchResource(resourcePath = path, matcherProvider = ::be) return this } @@ -26,7 +26,7 @@ infix fun ByteArray.shouldMatchResource( infix fun ByteArray.shouldNotMatchResource( path: String ): ByteArray { - this shouldNot matchResource(path, ::be) + this shouldNot matchResource(resourcePath = path, matcherProvider = ::be) return this } diff --git a/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/resource/stringMatchers.kt b/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/resource/stringMatchers.kt index 4783aacf40b..2834f671a59 100644 --- a/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/resource/stringMatchers.kt +++ b/kotest-assertions/kotest-assertions-core/src/jvmMain/kotlin/io/kotest/matchers/resource/stringMatchers.kt @@ -1,6 +1,7 @@ package io.kotest.matchers.resource -import io.kotest.matchers.ComparableMatcherResult +import io.kotest.assertions.print.StringPrint +import io.kotest.matchers.ComparisonMatcherResult import io.kotest.matchers.Matcher import io.kotest.matchers.MatcherResult import io.kotest.matchers.be @@ -20,7 +21,7 @@ import kotlin.io.path.writeText infix fun String.shouldMatchResource( path: String ): String { - this should matchResource(path, ::be, ignoreLineSeparators = true) + this should matchResource(path, ::be, ignoreLineSeparators = true, trim = false) return this } @@ -32,19 +33,28 @@ infix fun String.shouldMatchResource( infix fun String.shouldNotMatchResource( path: String ): String { - this shouldNot matchResource(path, ::be, ignoreLineSeparators = true) + this shouldNot matchResource(path, ::be, ignoreLineSeparators = true, trim = false) return this } /** * Will match if the given String and the resource value matches using matcher provided by [matcherProvider] + * + * @param ignoreLineSeparators if true, will ignore differences in "\r", "\n" and "\r\n", so it is not dependent on the system line separator. + * @param trim if true, will trim the expected and actual values before comparing them. */ fun String.shouldMatchResource( - path: String, - matcherProvider: (String) -> Matcher, - ignoreLineSeparators: Boolean = true + path: String, + matcherProvider: (String) -> Matcher = ::be, + ignoreLineSeparators: Boolean = true, + trim: Boolean = false, ): String { - this should matchResource(path, matcherProvider, ignoreLineSeparators) + this should matchResource( + resourcePath = path, + matcherProvider = matcherProvider, + ignoreLineSeparators = ignoreLineSeparators, + trim = trim, + ) return this } @@ -52,57 +62,64 @@ fun String.shouldMatchResource( * Will match if the given String and the resource value **not** matches using matcher provided by [matcherProvider] */ fun String.shouldNotMatchResource( - path: String, - matcherProvider: (String) -> Matcher, - ignoreLineSeparators: Boolean = true + path: String, + matcherProvider: (String) -> Matcher = ::be, + ignoreLineSeparators: Boolean = true, + trim: Boolean = false, ): String { - this shouldNot matchResource(path, matcherProvider, ignoreLineSeparators) + this shouldNot matchResource( + resourcePath = path, + matcherProvider = matcherProvider, + ignoreLineSeparators = ignoreLineSeparators, + trim = trim + ) return this } fun matchResource( - resourcePath: String, - matcherProvider: (String) -> Matcher, - ignoreLineSeparators: Boolean + resourcePath: String, + matcherProvider: (String) -> Matcher, + ignoreLineSeparators: Boolean, + trim: Boolean, ) = object : Matcher { override fun test(value: String): MatcherResult { - val resource = getResource(resourcePath) - val resourceValue = resource.readText() + val expectedUrl = getResource(resourcePath) + val expected = expectedUrl.readText() - val normalizedValue = if (ignoreLineSeparators) value.toLF() else value - val normalizedResourceValue = if (ignoreLineSeparators) resourceValue.toLF() else resourceValue + val normalizedActual = if (ignoreLineSeparators) value.toLF() else value + val normalizedExpected = if (ignoreLineSeparators) expected.toLF() else expected - return matcherProvider(normalizedResourceValue).test(normalizedValue).let { - ComparableMatcherResult( - it.passed(), - { - val actualFilePath = normalizedValue.writeToActualValueFile(resource) + val trimmedActual = if (trim) normalizedActual.trim() else normalizedActual + val trimmedExpected = if (trim) normalizedExpected.trim() else normalizedExpected - """${it.failureMessage()} + return matcherProvider(trimmedExpected).test(trimmedActual).let { + ComparisonMatcherResult( + passed = it.passed(), + actual = StringPrint.printUnquoted(trimmedActual), + expected = StringPrint.printUnquoted(trimmedExpected), + failureMessageFn = { -expected to match resource, but they differed -Expected : $resourcePath -Actual : $actualFilePath + val actualFilePath = normalizedActual.writeToActualValueFile(expectedUrl) -""" + """${it.failureMessage()} + + expected to match resource, but they differed + Expected : $resourcePath + Actual : $actualFilePath""" }, - { + negatedFailureMessageFn = { """${it.negatedFailureMessage()} -expected not to match resource, but they match -Expected : $resourcePath - -""" + expected not to match resource, but they match + Expected : $resourcePath""" }, - normalizedValue, - normalizedResourceValue, ) } } } -fun resourceAsString(path: String) = getResource(path).readText() +internal fun resourceAsString(path: String) = getResource(path).readText() internal fun getResource(path: String): URL = object {}.javaClass.getResource(path) ?: error("Failed to get resource at $path") diff --git a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/ShouldBeBooleanTest.kt b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/ShouldBeBooleanTest.kt new file mode 100644 index 00000000000..f3631ce2191 --- /dev/null +++ b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/ShouldBeBooleanTest.kt @@ -0,0 +1,17 @@ +package com.sksamuel.kotest.matchers + +import io.kotest.assertions.throwables.shouldThrowAny +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.throwable.shouldHaveMessage + +class ShouldBeBooleanTest : FunSpec() { + init { + test("a shouldBe true should have comparison") { + val a = false + shouldThrowAny { + a shouldBe true + }.shouldHaveMessage("expected: but was:") + } + } +} diff --git a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/equality/IsArrayTest.kt b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/equality/IsArrayTest.kt new file mode 100644 index 00000000000..c1364dab461 --- /dev/null +++ b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/equality/IsArrayTest.kt @@ -0,0 +1,27 @@ +package com.sksamuel.kotest.matchers.equality + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.equality.isArray +import io.kotest.matchers.shouldBe + +class IsArrayTest: StringSpec() { + init { + "isArray true" { + isArray(intArrayOf(1, 2, 3)) shouldBe true + isArray(arrayOf(1, 2, 3)) shouldBe true + isArray(BooleanArray(1)) shouldBe true + isArray(CharArray(1)) shouldBe true + isArray(ByteArray(1)) shouldBe true + isArray(IntArray(1)) shouldBe true + isArray(LongArray(1)) shouldBe true + isArray(FloatArray(1)) shouldBe true + isArray(DoubleArray(1)) shouldBe true + } + "isArray false" { + isArray(listOf(1, 2, 3)) shouldBe false + isArray("hello") shouldBe false + isArray(123) shouldBe false + isArray(null) shouldBe false + } + } +} diff --git a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/equality/ReflectionKtTest.kt b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/equality/ReflectionKtTest.kt index 503503ce147..77ee6940c77 100644 --- a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/equality/ReflectionKtTest.kt +++ b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/equality/ReflectionKtTest.kt @@ -303,6 +303,90 @@ class ReflectionKtTest : FunSpec() { EnumWrapper(EnumWithProperties.ONE).shouldBeEqualToComparingFields(EnumWrapper(EnumWithProperties.TWO)) }.message.shouldContain("expected: but was:") } + + test("shouldBeEqualToComparingFields handles ByteArray") { + class Test( + val test: kotlin.ByteArray + ) + Test(ByteArray(1)) shouldBeEqualToComparingFields Test(ByteArray(1)) + val actual = ByteArray(1) + actual[0] = 1 + shouldFail { + Test(ByteArray(1)) shouldBeEqualToComparingFields Test(actual) + }.message shouldContain "expected:<[1]> but was:<[0]>" + } + + test("shouldBeEqualToComparingFields handles CharArray") { + class Test( + val test: kotlin.CharArray + ) + Test(CharArray(1)) shouldBeEqualToComparingFields Test(CharArray(1)) + val actual = CharArray(1) + actual[0] = '1' + shouldFail { + Test(CharArray(1)) shouldBeEqualToComparingFields Test(actual) + }.message shouldContain "expected:<['1']> but was:<['\u0000']>" + } + + test("shouldBeEqualToComparingFields handles ShortArray") { + class Test( + val test: kotlin.ShortArray + ) + Test(ShortArray(1)) shouldBeEqualToComparingFields Test(ShortArray(1)) + val actual = ShortArray(1) + actual[0] = 1 + shouldFail { + Test(ShortArray(1)) shouldBeEqualToComparingFields Test(actual) + }.message shouldContain "expected:<[1]> but was:<[0]>" + } + + test("shouldBeEqualToComparingFields handles IntArray") { + class Test( + val test: kotlin.IntArray + ) + Test(IntArray(1)) shouldBeEqualToComparingFields Test(IntArray(1)) + val actual = IntArray(1) + actual[0] = 1 + shouldFail { + Test(IntArray(1)) shouldBeEqualToComparingFields Test(actual) + }.message shouldContain "expected:<[1]> but was:<[0]>" + } + + test("shouldBeEqualToComparingFields handles LongArray") { + class Test( + val test: kotlin.LongArray + ) + Test(LongArray(1)) shouldBeEqualToComparingFields Test(LongArray(1)) + val actual = LongArray(1) + actual[0] = 1L + shouldFail { + Test(LongArray(1)) shouldBeEqualToComparingFields Test(actual) + }.message shouldContain "expected:<[1L]> but was:<[0L]>" + } + + test("shouldBeEqualToComparingFields handles FloatArray") { + class Test( + val test: kotlin.FloatArray + ) + Test(FloatArray(1)) shouldBeEqualToComparingFields Test(FloatArray(1)) + val actual = FloatArray(1) + actual[0] = 1.0F + shouldFail { + Test(FloatArray(1)) shouldBeEqualToComparingFields Test(actual) + }.message shouldContain "expected:<[1.0f]> but was:<[0.0f]>" + } + + test("shouldBeEqualToComparingFields handles DoubleArray") { + class Test( + val test: kotlin.DoubleArray + ) + Test(DoubleArray(1)) shouldBeEqualToComparingFields Test(DoubleArray(1)) + val actual = DoubleArray(1) + actual[0] = 1.0 + shouldFail { + Test(DoubleArray(1)) shouldBeEqualToComparingFields Test(actual) + }.message shouldContain "expected:<[1.0]> but was:<[0.0]>" + } } data class KeyValuePair( diff --git a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/resource/ByteArrayResourceMatchersTest.kt b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/resource/ByteArrayResourceMatchersTest.kt index 3355911043d..c0200a2d060 100644 --- a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/resource/ByteArrayResourceMatchersTest.kt +++ b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/resource/ByteArrayResourceMatchersTest.kt @@ -1,9 +1,10 @@ package com.sksamuel.kotest.matchers.resource import io.kotest.assertions.AssertionErrorBuilder +import io.kotest.assertions.print.StringPrint import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.ShouldSpec -import io.kotest.matchers.ComparableMatcherResult +import io.kotest.matchers.ComparisonMatcherResult import io.kotest.matchers.Matcher import io.kotest.matchers.MatcherResult import io.kotest.matchers.file.shouldExist @@ -57,6 +58,16 @@ class ByteArrayResourceMatchersTest : ShouldSpec({ actualValueFile.readBytes() shouldBe givenValue } + should("include diff") { + val givenValue = byteArrayOf(1, 2) + + val errorMessage = shouldThrow { + givenValue shouldMatchResource "/resourceMatchersTest/expected/binary42.bin" + }.message ?: AssertionErrorBuilder.fail("Cannot get error message") + + errorMessage shouldContain "expected:<[4, 2]> but was:<[1, 2]" + } + } context("shouldMatchResource with custom matcher") { @@ -122,12 +133,12 @@ class ByteArrayResourceMatchersTest : ShouldSpec({ private fun lastBytesMatch(bytes: ByteArray) = object : Matcher { override fun test(value: ByteArray): MatcherResult { val last = value.last() - return ComparableMatcherResult( - last == bytes.last(), - { "expected to match resource, but they differed" }, - { "expected not to match resource, but they match" }, - last.toString(), - bytes.last().toString() + return ComparisonMatcherResult( + passed = last == bytes.last(), + actual = StringPrint.printUnquoted(last.toString()), + expected = StringPrint.printUnquoted(bytes.last().toString()), + failureMessageFn = { "expected to match resource, but they differed" }, + negatedFailureMessageFn = { "expected not to match resource, but they match" }, ) } } diff --git a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/resource/StringResourceMatchersTest.kt b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/resource/StringResourceMatchersTest.kt index 1dec8116b73..23b160b9264 100644 --- a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/resource/StringResourceMatchersTest.kt +++ b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/matchers/resource/StringResourceMatchersTest.kt @@ -57,6 +57,22 @@ class StringResourceMatchersTest : ShouldSpec({ actualValueFile.readText() shouldBe givenValue } + should("include diff") { + val givenValue = "not a test resource" + + val errorMessage = shouldThrow { + givenValue shouldMatchResource "/resourceMatchersTest/expected/testResource.txt" + }.message ?: AssertionErrorBuilder.fail("Cannot get error message") + + errorMessage shouldContain """expected: but was:""" + } + + should("support trim") { + val givenValue = " test\nresource\n " + givenValue.shouldMatchResource("/resourceMatchersTest/expected/testResource.txt", trim = true) + } } context("shouldMatchResource with custom matcher") { diff --git a/kotest-assertions/kotest-assertions-json/src/commonMain/kotlin/io/kotest/assertions/json/jsonMatchers.kt b/kotest-assertions/kotest-assertions-json/src/commonMain/kotlin/io/kotest/assertions/json/jsonMatchers.kt index 4d1a7f6da85..64d577ddba4 100644 --- a/kotest-assertions/kotest-assertions-json/src/commonMain/kotlin/io/kotest/assertions/json/jsonMatchers.kt +++ b/kotest-assertions/kotest-assertions-json/src/commonMain/kotlin/io/kotest/assertions/json/jsonMatchers.kt @@ -1,6 +1,7 @@ package io.kotest.assertions.json -import io.kotest.matchers.ComparableMatcherResult +import io.kotest.assertions.print.StringPrint +import io.kotest.matchers.ComparisonMatcherResult import io.kotest.matchers.Matcher import io.kotest.matchers.MatcherResult import io.kotest.matchers.should @@ -20,7 +21,7 @@ fun matchJson(@Language("json") expected: String?) = object : Matcher { override fun test(value: String?): MatcherResult { val actualJson = try { value?.let(pretty::parseToJsonElement) - } catch (ex: Exception) { + } catch (_: Exception) { return MatcherResult( false, { "expected: actual json to be valid json: $value" }, @@ -30,7 +31,7 @@ fun matchJson(@Language("json") expected: String?) = object : Matcher { val expectedJson = try { expected?.let(pretty::parseToJsonElement) - } catch (ex: Exception) { + } catch (_: Exception) { return MatcherResult( false, { "expected: expected json to be valid json: $expected" }, @@ -38,12 +39,12 @@ fun matchJson(@Language("json") expected: String?) = object : Matcher { ) } - return ComparableMatcherResult( - actualJson == expectedJson, + return ComparisonMatcherResult( + passed = actualJson == expectedJson, + actual = StringPrint.printUnquoted(actualJson.toString()), + expected = StringPrint.printUnquoted(expectedJson.toString()), { "expected json to match, but they differed\n" }, { "expected not to match with: $expectedJson but match: $actualJson" }, - actualJson.toString(), - expectedJson.toString() ) } } @@ -57,7 +58,7 @@ fun beValidJson() = object : Matcher { { "expected: actual json to be valid json: $value" }, { "expected: actual json to be invalid json: $value" } ) - } catch (ex: Exception) { + } catch (_: Exception) { MatcherResult( false, { "expected: actual json to be valid json: $value" }, @@ -72,7 +73,7 @@ fun beJsonType(kClass: KClass<*>) = object : Matcher { override fun test(value: String?): MatcherResult { val element = try { value?.let(pretty::parseToJsonElement) - } catch (ex: Exception) { + } catch (_: Exception) { return MatcherResult( false, { "expected: actual json to be valid json: $value" }, diff --git a/kotest-assertions/kotest-assertions-json/src/commonMain/kotlin/io/kotest/assertions/json/matchers.kt b/kotest-assertions/kotest-assertions-json/src/commonMain/kotlin/io/kotest/assertions/json/matchers.kt index dc41227d15e..d43b59b3fd3 100644 --- a/kotest-assertions/kotest-assertions-json/src/commonMain/kotlin/io/kotest/assertions/json/matchers.kt +++ b/kotest-assertions/kotest-assertions-json/src/commonMain/kotlin/io/kotest/assertions/json/matchers.kt @@ -1,7 +1,8 @@ package io.kotest.assertions.json import io.kotest.assertions.json.comparisons.compare -import io.kotest.matchers.ComparableMatcherResult +import io.kotest.assertions.print.StringPrint +import io.kotest.matchers.ComparisonMatcherResult import io.kotest.matchers.Matcher import io.kotest.matchers.MatcherResult import io.kotest.matchers.should @@ -53,12 +54,12 @@ private fun equalJsonTree( options, )?.asString() - ComparableMatcherResult( - error == null, - { "$error\n" }, - { "Expected values to not match" }, - value.raw, - expected.raw, + ComparisonMatcherResult( + passed = error == null, + actual = StringPrint.printUnquoted(value.raw), + expected = StringPrint.printUnquoted(expected.raw), + failureMessageFn = { "$error\n" }, + negatedFailureMessageFn = { "Expected values to not match" }, ) } diff --git a/kotest-assertions/kotest-assertions-json/src/jvmMain/kotlin/io/kotest/assertions/json/keyvalues.kt b/kotest-assertions/kotest-assertions-json/src/jvmMain/kotlin/io/kotest/assertions/json/keyvalues.kt index d35783667ab..591878b6250 100644 --- a/kotest-assertions/kotest-assertions-json/src/jvmMain/kotlin/io/kotest/assertions/json/keyvalues.kt +++ b/kotest-assertions/kotest-assertions-json/src/jvmMain/kotlin/io/kotest/assertions/json/keyvalues.kt @@ -27,7 +27,14 @@ inline fun String.shouldNotContainJsonKeyValue(@Language("JSONPath") this shouldNot containJsonKeyValue(path, value) inline fun containJsonKeyValue(@Language("JSONPath") path: String, t: T) = - containJsonKeyValue(path, t, t?.let { it::class.java } ?: Nothing::class.java) + containJsonKeyValue(path, t, clazz(t)) + +inline fun clazz(t: T?) = t?.let { + when { + t is Collection<*> -> List::class.java + t is Map<*, *> -> Map::class.java + else -> it::class.java +} } ?: Nothing::class.java @KotestInternal fun > containJsonKeyValue(path: String, t: T, tClass: C) = object : Matcher { diff --git a/kotest-assertions/kotest-assertions-json/src/jvmMain/kotlin/io/kotest/assertions/json/resources.kt b/kotest-assertions/kotest-assertions-json/src/jvmMain/kotlin/io/kotest/assertions/json/resources.kt index 723f9aaa12c..2b80c4195e9 100644 --- a/kotest-assertions/kotest-assertions-json/src/jvmMain/kotlin/io/kotest/assertions/json/resources.kt +++ b/kotest-assertions/kotest-assertions-json/src/jvmMain/kotlin/io/kotest/assertions/json/resources.kt @@ -1,6 +1,7 @@ package io.kotest.assertions.json -import io.kotest.matchers.ComparableMatcherResult +import io.kotest.assertions.print.StringPrint +import io.kotest.matchers.ComparisonMatcherResult import io.kotest.matchers.Matcher import io.kotest.matchers.MatcherResult import io.kotest.matchers.should @@ -27,12 +28,12 @@ fun matchJsonResource(resource: String) = object : Matcher { pretty.parseToJsonElement(it.readText()) } ?: throw AssertionError("File should exist in resources: $resource") - return ComparableMatcherResult( - actualJson == expectedJson, - { "expected json to match, but they differed\n" }, - { "expected not to match with: $expectedJson but match: $actualJson" }, - actualJson.toString(), - expectedJson.toString(), + return ComparisonMatcherResult( + passed = actualJson == expectedJson, + actual = StringPrint.printUnquoted(actualJson.toString()), + expected = StringPrint.printUnquoted(expectedJson.toString()), + failureMessageFn = { "expected json to match, but they differed\n" }, + negatedFailureMessageFn = { "expected not to match with: $expectedJson but match: $actualJson" }, ) } } diff --git a/kotest-assertions/kotest-assertions-json/src/jvmTest/kotlin/com/sksamuel/kotest/tests/json/ClazzTest.kt b/kotest-assertions/kotest-assertions-json/src/jvmTest/kotlin/com/sksamuel/kotest/tests/json/ClazzTest.kt new file mode 100644 index 00000000000..99b303ff698 --- /dev/null +++ b/kotest-assertions/kotest-assertions-json/src/jvmTest/kotlin/com/sksamuel/kotest/tests/json/ClazzTest.kt @@ -0,0 +1,25 @@ +package com.sksamuel.kotest.tests.json + +import io.kotest.assertions.json.clazz +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import java.time.LocalDate + +class ClazzTest: StringSpec() { + init { + "uses generic List" { + clazz(listOf("a", "b", "c")) shouldBe List::class.java + } + "uses generic Map" { + clazz(mapOf("a" to 1, "b" to 2)) shouldBe Map::class.java + } + "uses specific class" { + clazz("hello") shouldBe String::class.java + clazz(LocalDate.of(2025, 8, 28)) shouldBe LocalDate::class.java + } + "handles null" { + clazz(null) shouldBe Nothing::class.java + } + } + +} diff --git a/kotest-assertions/kotest-assertions-json/src/jvmTest/kotlin/com/sksamuel/kotest/tests/json/ContainJsonKeyValueTest.kt b/kotest-assertions/kotest-assertions-json/src/jvmTest/kotlin/com/sksamuel/kotest/tests/json/ContainJsonKeyValueTest.kt index 2f7883129d2..59cac6048fd 100644 --- a/kotest-assertions/kotest-assertions-json/src/jvmTest/kotlin/com/sksamuel/kotest/tests/json/ContainJsonKeyValueTest.kt +++ b/kotest-assertions/kotest-assertions-json/src/jvmTest/kotlin/com/sksamuel/kotest/tests/json/ContainJsonKeyValueTest.kt @@ -124,4 +124,20 @@ class ContainJsonKeyValueTest : StringSpec({ """{ "value": 3.14 }""".shouldContainJsonKeyValue("$.value", 3.14f) """{ "value": null }""".shouldContainJsonKeyValue("$.value", null) } + + "should handle list as expected value" { + @Language("JSON") + val jsonString = """ + {"v": ["a", "b"]} + """.trimIndent() + jsonString.shouldContainJsonKeyValue("$.v", listOf("a", "b")) + } + + "should handle map as expected value" { + @Language("JSON") + val jsonString = """ + {"v": {"a": "b"}} + """.trimIndent() + jsonString.shouldContainJsonKeyValue("$.v", mapOf("a" to "b")) + } }) diff --git a/kotest-assertions/kotest-assertions-json/src/jvmTest/kotlin/com/sksamuel/kotest/tests/json/EqualTest.kt b/kotest-assertions/kotest-assertions-json/src/jvmTest/kotlin/com/sksamuel/kotest/tests/json/EqualTest.kt index 0156ff879ff..788ceb5bf7a 100644 --- a/kotest-assertions/kotest-assertions-json/src/jvmTest/kotlin/com/sksamuel/kotest/tests/json/EqualTest.kt +++ b/kotest-assertions/kotest-assertions-json/src/jvmTest/kotlin/com/sksamuel/kotest/tests/json/EqualTest.kt @@ -44,7 +44,7 @@ class EqualTest : FunSpec() { }.shouldHaveMessage("Expected values to not match") } - test("f:comparing strings in objects") { + test("comparing strings in objects") { checkAll(Arb.string(1..10, Codepoint.az())) { string -> val a = """ { "a" : "$string" } """ diff --git a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/matchers/Matcher.kt b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/matchers/Matcher.kt index 6eae834f397..ba116447e2c 100644 --- a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/matchers/Matcher.kt +++ b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/matchers/Matcher.kt @@ -124,7 +124,7 @@ data class ComparisonMatcherResult( override fun negatedFailureMessage(): String = negatedFailureMessageFn() } -@Deprecated("Use ValuesMatcherResult") +@Deprecated("Use ComparisonMatcherResult") interface ComparableMatcherResult : MatcherResult { fun actual(): String @@ -148,7 +148,7 @@ interface ComparableMatcherResult : MatcherResult { } } -@Deprecated("Use ValuesMatcherResult") +@Deprecated("Use ComparisonMatcherResult") interface EqualityMatcherResult : MatcherResult { fun actual(): Any? diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/AbstractContainerExtension.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/AbstractContainerExtension.kt index c094817284a..2a831485b7b 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/AbstractContainerExtension.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/AbstractContainerExtension.kt @@ -6,7 +6,7 @@ import io.kotest.core.listeners.AfterSpecListener import io.kotest.core.spec.Spec import org.testcontainers.containers.GenericContainer -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") abstract class AbstractContainerExtension>( private val container: T, private val mode: ContainerLifecycleMode = ContainerLifecycleMode.Project, diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ContainerExtension.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ContainerExtension.kt index 22a557cf331..26e015fc503 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ContainerExtension.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ContainerExtension.kt @@ -53,7 +53,7 @@ import org.testcontainers.containers.GenericContainer * @param afterShutdown a callback that is invoked only once, just after the container is stopped. * If the container is never started, this callback will not be invoked. */ -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") class ContainerExtension>( private val container: T, private val mode: ContainerLifecycleMode = ContainerLifecycleMode.Project, diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ContainerLifecycleMode.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ContainerLifecycleMode.kt index ddbe98c7334..3e8369df7fb 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ContainerLifecycleMode.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ContainerLifecycleMode.kt @@ -3,7 +3,7 @@ package io.kotest.extensions.testcontainers /** * Determines the lifetime of a test container installed in a Kotest extension. */ -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") enum class ContainerLifecycleMode { /** diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/DockerComposeContainerExtension.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/DockerComposeContainerExtension.kt index 3a09b69e3b2..acbaed4d1af 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/DockerComposeContainerExtension.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/DockerComposeContainerExtension.kt @@ -48,7 +48,7 @@ import java.io.File * @param afterShutdown a callback that is invoked only once, just after the container is stopped. * If the container is never started, this callback will not be invoked. */ -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") class DockerComposeContainerExtension>( private val container: T, private val mode: ContainerLifecycleMode = ContainerLifecycleMode.Project, diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/DockerComposeContainersExtension.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/DockerComposeContainersExtension.kt index ddc9e1a7ee9..10067b0cd49 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/DockerComposeContainersExtension.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/DockerComposeContainersExtension.kt @@ -15,7 +15,7 @@ import org.testcontainers.lifecycle.TestLifecycleAware import java.util.Optional import org.testcontainers.containers.DockerComposeContainer -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") class DockerComposeContainersExtension>( private val container: T, private val lifecycleMode: LifecycleMode = LifecycleMode.Spec, diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/Extensions.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/Extensions.kt index 5d9a3d70855..89f5d1971b1 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/Extensions.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/Extensions.kt @@ -3,32 +3,32 @@ package io.kotest.extensions.testcontainers import io.kotest.core.TestConfiguration import org.testcontainers.lifecycle.Startable -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") fun T.perTest(): StartablePerTestListener = StartablePerTestListener(this) -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") fun T.perSpec(): StartablePerSpecListener = StartablePerSpecListener(this) -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") fun T.perProject(): StartablePerProjectListener = StartablePerProjectListener(this) -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") fun T.perProject(containerName: String): StartablePerProjectListener = StartablePerProjectListener(this) -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") fun TestConfiguration.configurePerTest(startable: T): T { extension(StartablePerTestListener(startable)) return startable } -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") fun TestConfiguration.configurePerSpec(startable: T): T { extension(StartablePerSpecListener(startable)) return startable } -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") fun TestConfiguration.configurePerProject(startable: T, containerName: String): T { extension(StartablePerProjectListener(startable)) return startable diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/JdbcDatabaseContainerExtension.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/JdbcDatabaseContainerExtension.kt index cfe6a804168..69c822c7214 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/JdbcDatabaseContainerExtension.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/JdbcDatabaseContainerExtension.kt @@ -50,7 +50,7 @@ import org.testcontainers.containers.JdbcDatabaseContainer * @param afterShutdown a callback that is invoked only once, just after the container is stopped. * If the container is never started, this callback will not be invoked. */ -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") class JdbcDatabaseContainerExtension( private val container: JdbcDatabaseContainer<*>, private val mode: ContainerLifecycleMode = ContainerLifecycleMode.Project, diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/LifecycleMode.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/LifecycleMode.kt index b8b343f3c89..ef577df734d 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/LifecycleMode.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/LifecycleMode.kt @@ -1,6 +1,6 @@ package io.kotest.extensions.testcontainers -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") enum class LifecycleMode { Spec, EveryTest, Leaf, Root } diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ProjectContainerExtension.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ProjectContainerExtension.kt index 6d3a2b84827..6615c03d4ee 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ProjectContainerExtension.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/ProjectContainerExtension.kt @@ -53,7 +53,7 @@ import org.testcontainers.containers.GenericContainer * @param afterShutdown a callback that is invoked only once, just after the container is stopped. * If the container is never started, this callback will not be invoked. */ -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") class ProjectContainerExtension>( private val container: T, private val mode: ContainerLifecycleMode = ContainerLifecycleMode.Project, diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/SettableDataSource.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/SettableDataSource.kt index 055e7a38121..3b9fe4eb50b 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/SettableDataSource.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/SettableDataSource.kt @@ -6,7 +6,7 @@ import java.sql.Connection import java.util.logging.Logger import javax.sql.DataSource -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") class SettableDataSource(private var ds: HikariDataSource?) : DataSource { private fun getDs(): DataSource = ds ?: error("DataSource is not ready") diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerProjectListener.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerProjectListener.kt index cf2b3a66598..42bda05249b 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerProjectListener.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerProjectListener.kt @@ -19,7 +19,7 @@ import org.testcontainers.lifecycle.Startable * @see * [StartablePerTestListener] * */ -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") class StartablePerProjectListener(private val startable: T) : TestListener, ProjectListener { @Deprecated("The containerName arg is no longer used") diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerSpecListener.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerSpecListener.kt index 085ed990764..d1bf7ff766b 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerSpecListener.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerSpecListener.kt @@ -22,7 +22,7 @@ import org.testcontainers.lifecycle.TestLifecycleAware * @see * [StartablePerTestListener] * */ -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") class StartablePerSpecListener(private val startable: T) : TestListener { private val testLifecycleAwareListener = TestLifecycleAwareListener(startable) diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerTestListener.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerTestListener.kt index 4ed1eb4b8de..5f8f59a5d97 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerTestListener.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/StartablePerTestListener.kt @@ -19,7 +19,7 @@ import org.testcontainers.lifecycle.TestLifecycleAware * * @see[StartablePerSpecListener] * */ -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") class StartablePerTestListener(private val startable: T) : TestListener { private val testLifecycleAwareListener = TestLifecycleAwareListener(startable) diff --git a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/TestLifecycleAwareListener.kt b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/TestLifecycleAwareListener.kt index 9162dfe8199..a5287d1f31d 100644 --- a/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/TestLifecycleAwareListener.kt +++ b/kotest-extensions/kotest-extensions-testcontainers/src/jvmMain/kotlin/io/kotest/extensions/testcontainers/TestLifecycleAwareListener.kt @@ -9,7 +9,7 @@ import org.testcontainers.lifecycle.TestLifecycleAware import java.net.URLEncoder import java.util.Optional -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") class TestLifecycleAwareListener(startable: Startable) : TestListener { private val testLifecycleAware = startable as? TestLifecycleAware @@ -22,7 +22,7 @@ class TestLifecycleAwareListener(startable: Startable) : TestListener { } } -@Deprecated("Use TestContainerProjectExtension or TestContainerSpeccExtension instead") +@Deprecated("Use TestContainerProjectExtension or TestContainerSpecExtension instead") internal fun TestCase.toTestDescription() = object : TestDescription { override fun getFilesystemFriendlyName(): String { diff --git a/kotest-framework/kotest-framework-engine/src/commonMain/kotlin/io/kotest/engine/spec/execution/InstancePerLeafSpecExecutor.kt b/kotest-framework/kotest-framework-engine/src/commonMain/kotlin/io/kotest/engine/spec/execution/InstancePerLeafSpecExecutor.kt index 5a0e251c177..05ee9bbf572 100644 --- a/kotest-framework/kotest-framework-engine/src/commonMain/kotlin/io/kotest/engine/spec/execution/InstancePerLeafSpecExecutor.kt +++ b/kotest-framework/kotest-framework-engine/src/commonMain/kotlin/io/kotest/engine/spec/execution/InstancePerLeafSpecExecutor.kt @@ -25,8 +25,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.withContext -import kotlin.concurrent.atomics.AtomicBoolean -import kotlin.concurrent.atomics.ExperimentalAtomicApi import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext @@ -41,8 +39,6 @@ internal class InstancePerLeafSpecExecutor( private val extensions = SpecExtensions(context.specConfigResolver, context.projectConfigResolver) private val materializer = Materializer(context.specConfigResolver) private val results = TestResults() - @OptIn(ExperimentalAtomicApi::class) - private val seedUsed = AtomicBoolean(false) private val inflator = SpecRefInflator( registry = context.registry, @@ -103,7 +99,6 @@ internal class InstancePerLeafSpecExecutor( * It will locate the root that is the parent of the given [TestCase] and execute it in the new spec instance. */ private suspend fun executeInFreshSpec(testCase: TestCase, ref: SpecRef, specContext: SpecContext) { - require(testCase.type == TestType.Test) { "Only leaf tests should be executed in a fresh spec" } logger.log { "Enqueuing in a fresh spec ${testCase.descriptor}" } val spec = inflator.inflate(ref).getOrThrow() @@ -163,37 +158,43 @@ internal class InstancePerLeafSpecExecutor( override val coroutineContext: CoroutineContext, private val ref: SpecRef, ) : TestScope { + private var hasVisitedFirstNode = false private val logger = Logger(LeafLaunchingScope::class) - @OptIn(ExperimentalAtomicApi::class) override suspend fun registerTestCase(nested: NestedTest) { logger.log { Pair(testCase.name.name, "Discovered nested test '${nested.name.name}'") } val nestedTestCase = materializer.materialize(nested, testCase) - // we care about two scenarios: - // - if the target is null, we are in discovery mode and nested tests will be executed if a container, or queued up if a leaf - // - if the target is not null, we are trying to reach a specific test, and we will execute nested tests on the path to the target + if (target != null && !nestedTestCase.descriptor.isPrefixOf(target)) { + // Should execute the given test case described by target but traversing an irrelevant test case now + // Just return to abort from this logic + return + } + if (hasVisitedFirstNode) { + logger.log { Pair(testCase.name.name, "Executing in fresh spec") } + executeInFreshSpec(nestedTestCase, ref, specContext) + return + } + hasVisitedFirstNode = true + if (target == null) { logger.log { Pair(testCase.name.name, "Launching discovered test in discovery mode") } when (nestedTestCase.type) { TestType.Container -> { logger.log { Pair(testCase.name.name, "Executing CONTAINER type in existing spec") } - executeTest(nestedTestCase, null, specContext, ref) } + TestType.Test -> { - if (!seedUsed.compareAndExchange(expectedValue = false, newValue = true)) { - logger.log { Pair(testCase.name.name, "Executing TEST type in seed spec") } - executeTest(nestedTestCase, null, specContext, ref) - } else { - logger.log { Pair(testCase.name.name, "Executing TEST type in fresh spec") } - executeInFreshSpec(nestedTestCase, ref, specContext) - } + logger.log { Pair(testCase.name.name, "Executing TEST type in existing spec") } } } - return + executeTest(nestedTestCase, null, specContext, ref) + } else if (nestedTestCase.descriptor == target) { + logger.log { Pair(testCase.name.name, "Start discovering tests from children nodes") } + executeTest(nestedTestCase, null, specContext, ref) } else if (nestedTestCase.descriptor.isPrefixOf(target)) { - logger.log { Pair(testCase.name.name, "Executing prefix in existing spec") } + logger.log { Pair(testCase.name.name, "Proceed discovery phase to the next node of target") } executeTest(nestedTestCase, target, specContext, ref) } } diff --git a/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/config/ApplyTagInheritanceConfigTest.kt b/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/config/ApplyTagInheritanceConfigTest.kt deleted file mode 100644 index e93c22291da..00000000000 --- a/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/config/ApplyTagInheritanceConfigTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -//package com.sksamuel.kotest.engine.config -// -//import io.kotest.core.annotation.Isolate -//import io.kotest.core.config.AbstractProjectConfig -//import io.kotest.core.config.ProjectConfiguration -//import io.kotest.core.spec.style.FunSpec -//import io.kotest.engine.config.KotestEngineProperties -//import io.kotest.engine.config.applyConfigFromProjectConfig -//import io.kotest.engine.config.applyConfigFromSystemProperties -//import io.kotest.extensions.system.OverrideMode -//import io.kotest.extensions.system.withEnvironment -//import io.kotest.extensions.system.withSystemProperty -//import io.kotest.matchers.shouldBe -// -//private const val key = KotestEngineProperties.tagInheritance -// -//@Isolate -//class ApplyTagInheritanceConfigTest : FunSpec({ -// test("tag inheritance can come from sys props") { -// val c = object : AbstractProjectConfig() { -// override val tagInheritance = false -// } -// withEnvironment(key, "false", OverrideMode.SetOrOverride) { -// withSystemProperty(key, "true", OverrideMode.SetOrOverride) { -// applyConfigFromSystemProperties(config) -// } -// } -// -// config.tagInheritance shouldBe true -// } -// -// test("tag inheritance can come from env vars with dots in name") { -// val config = ProjectConfiguration() -// -// config.tagInheritance shouldBe false -// -// withEnvironment(key, "true", OverrideMode.SetOrOverride) { -// applyConfigFromSystemProperties(config) -// } -// -// config.tagInheritance shouldBe true -// } -// -// test("tag inheritance can come from env vars with underscores in name") { -// val config = ProjectConfiguration() -// -// config.tagInheritance shouldBe false -// -// withEnvironment(key.replace('.', '_'), "TRUE", OverrideMode.SetOrOverride) { -// applyConfigFromSystemProperties(config) -// } -// -// config.tagInheritance shouldBe true -// } -// -// test("Tag inheritance can come from AbstractProjectConfig") { -// val config = ProjectConfiguration() -// -// config.tagInheritance shouldBe false -// -// applyConfigFromProjectConfig(object : AbstractProjectConfig() { -// override val tagInheritance = true -// }, config) -// -// config.tagInheritance shouldBe true -// } -//}) diff --git a/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/spec/execmode/InstancePerLeafTest.kt b/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/spec/execmode/InstancePerLeafTest.kt index faa09b443a3..3305af517ae 100644 --- a/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/spec/execmode/InstancePerLeafTest.kt +++ b/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/spec/execmode/InstancePerLeafTest.kt @@ -10,8 +10,82 @@ class InstancePerLeafTest : DescribeSpec({ isolationMode = IsolationMode.InstancePerLeaf + beforeSpec { + trace = "" + } + + afterSpec { + trace shouldBe "d1_c1_i1_d1_c2_i2_" + } + + describe("d1") { + trace += "d1_" + + context("c1") { + trace += "c1_" + + it("i1") { + trace += "i1_" + } + } + + context("c2") { + trace += "c2_" + + it("i2") { + trace += "i2_" + } + } + } +}) + +class InstancePerLeafTest2 : DescribeSpec({ + + isolationMode = IsolationMode.InstancePerLeaf + + beforeSpec { + trace = "" + } + + afterSpec { + trace shouldBe "d1_c1_i1_d1_c1_i2_d1_c2_i3_" + } + + describe("d1") { + trace += "d1_" + + context("c1") { + trace += "c1_" + + it("i1") { + trace += "i1_" + } + + it("i2") { + trace += "i2_" + } + } + + context("c2") { + trace += "c2_" + + it("i3") { + trace += "i3_" + } + } + } +}) + +class InstancePerLeafTest3 : DescribeSpec({ + + isolationMode = IsolationMode.InstancePerLeaf + + beforeSpec { + trace = "" + } + afterSpec { - trace shouldBe "d1_c1_i1_c2_d1_c2_i2_" + trace shouldBe "d1_c1_i1_d2_c2_i2_" } describe("d1") { @@ -24,6 +98,10 @@ class InstancePerLeafTest : DescribeSpec({ trace += "i1_" } } + } + + describe("d2") { + trace += "d2_" context("c2") { trace += "c2_" diff --git a/kotest-framework/kotest-framework-plugin-gradle/src/main/kotlin/io/kotest/framework/gradle/KotestPlugin.kt b/kotest-framework/kotest-framework-plugin-gradle/src/main/kotlin/io/kotest/framework/gradle/KotestPlugin.kt index 1fb7335ecb2..30cc6637f5f 100644 --- a/kotest-framework/kotest-framework-plugin-gradle/src/main/kotlin/io/kotest/framework/gradle/KotestPlugin.kt +++ b/kotest-framework/kotest-framework-plugin-gradle/src/main/kotlin/io/kotest/framework/gradle/KotestPlugin.kt @@ -44,8 +44,15 @@ abstract class KotestPlugin : Plugin { internal const val TESTS_DIR_NAME = "test-results" internal const val JVM_KOTEST_NAME = "jvmKotest" internal const val JVM_TEST_NAME = "jvmTest" - internal const val JSTEST_NAME = "jsTest" + internal const val JS_TEST_TASK_NAME = "jsTest" + internal const val WASM_JS_TEST_TASK_NAME = "wasmJsTest" + internal const val KOTEST_TASK_NAME = "kotest" internal const val KSP_PLUGIN_ID = "com.google.devtools.ksp" + internal const val KSP_JS_SOURCESET = "kspJsTest" + internal const val KSP_WASM_JS_SOURCESET = "kspWasmJsTest" + internal const val TEST_TASK_NAME = "test" + internal const val ANDROID_UNIT_TEST_SUFFIX = "UnitTest" + internal const val KOTEST_INCLUDE_PROPERTY = "kotest.include" private val unsupportedTargets = listOf("metadata") } @@ -53,7 +60,7 @@ abstract class KotestPlugin : Plugin { override fun apply(project: Project) { - project.tasks.register("kotest") { + project.tasks.register(KOTEST_TASK_NAME) { group = JavaBasePlugin.VERIFICATION_GROUP description = TASK_DESCRIPTION } @@ -80,10 +87,10 @@ abstract class KotestPlugin : Plugin { private fun handleKotlinJvm(project: Project) { project.plugins.withType { - val existing = project.tasks.findByName("test") + val existing = project.tasks.findByName(TEST_TASK_NAME) when (existing) { - null -> println("> No test task found in project ${project.name} - no Kotest task will be added") - is Test -> configureJvmTask("test", project, null) // no need for target name for standalone jvm + null -> project.logger.info("> No test task found in project ${project.name} - no Kotest task will be added") + is Test -> configureJvmTask(TEST_TASK_NAME, project, null) // no need for target name for standalone jvm } } } @@ -119,14 +126,14 @@ abstract class KotestPlugin : Plugin { // we can execute check or test tasks with -Pkotest.include and this will then be // passed to the kotest runtime as an environment variable to filter specs and tests - project.findProperty("kotest.include")?.let { include.set(it.toString()) } + project.findProperty(KOTEST_INCLUDE_PROPERTY)?.let { include.set(it.toString()) } // these are the JVM compile tasks that produce the classes we want to test inputs.files(project.tasks.withType().map { it.outputs.files }) } - project.tasks.getByName("kotest") { - println("> Configuring kotest task for $JVM_KOTEST_NAME") + project.tasks.getByName(KOTEST_TASK_NAME) { + project.logger.info("Configuring kotest task for $sourceSetName") dependsOn(jvmKotest) } } @@ -162,7 +169,7 @@ abstract class KotestPlugin : Plugin { private fun handleMultiplatformJvm(target: KotlinTarget) { val existing = target.project.tasks.findByName(JVM_TEST_NAME) when (existing) { - null -> println("> No $JVM_TEST_NAME task found in project ${target.project.name} - no $JVM_KOTEST_NAME task will be added") + null -> target.project.logger.info("> No $JVM_TEST_NAME task found in project ${target.project.name} - no $JVM_KOTEST_NAME task will be added") is KotlinJvmTest -> configureJvmTask(JVM_TEST_NAME, target.project, "jvm") } } @@ -173,7 +180,7 @@ abstract class KotestPlugin : Plugin { // sometimes a native target might not exist, because either tests are not supported (eg android native) // or the target is not buildable on the current host (eg ios target on a linux host) - null -> println("> Skipping tests for ${target.name} because no task $nativeTaskName found") + null -> target.project.logger.info("> Skipping tests for ${target.name} because no task $nativeTaskName found") is KotlinNativeTest -> { @@ -190,7 +197,7 @@ abstract class KotestPlugin : Plugin { // we can execute check or test tasks with -Pkotest.include and this will then be // passed to the kotest runtime as an environment variable to filter specs and tests - val include = target.project.findProperty("kotest.include") + val include = target.project.findProperty(KOTEST_INCLUDE_PROPERTY) existing.doFirst { @@ -223,8 +230,8 @@ abstract class KotestPlugin : Plugin { // do it for every different native target (there could be many!) wireKsp(target.project, kspConfigurationName(target)) - target.project.tasks.getByName("kotest") { - println("> Configuring kotest task for $nativeTaskName") + target.project.tasks.getByName(KOTEST_TASK_NAME) { + target.project.logger.info("> Configuring kotest task for $nativeTaskName") dependsOn(existing) } } @@ -239,10 +246,10 @@ abstract class KotestPlugin : Plugin { // the ksp plugin will create a configuration named kspWasmJsTest that contains // the symbol processors used by the test configuration. We want to wire in // the kotest symbol processor to this configuration so the user doesn't have to manually - wireKsp(target.project, "kspWasmJsTest") - target.project.tasks.getByName("kotest") { - println("> Configuring kotest task for wasmJsTest") - dependsOn(target.project.tasks["wasmJsTest"]) + wireKsp(target.project, KSP_WASM_JS_SOURCESET) + target.project.tasks.getByName(KOTEST_TASK_NAME) { + target.project.logger.info("> Configuring kotest task for $KSP_WASM_JS_SOURCESET") + dependsOn(target.project.tasks[WASM_JS_TEST_TASK_NAME]) } } @@ -273,10 +280,10 @@ abstract class KotestPlugin : Plugin { // the ksp plugin will create a configuration named kspJsTest that contains // the symbol processors used by the test configuration. We want to wire in // the kotest symbol processor to this configuration so the user doesn't have to manually - wireKsp(target.project, "kspJsTest") - target.project.tasks.getByName("kotest") { - println("> Configuring kotest task for $JSTEST_NAME") - dependsOn(target.project.tasks[JSTEST_NAME]) + wireKsp(target.project, KSP_JS_SOURCESET) + target.project.tasks.getByName(KOTEST_TASK_NAME) { + target.project.logger.info("> Configuring kotest task for $JS_TEST_TASK_NAME") + dependsOn(target.project.tasks[JS_TEST_TASK_NAME]) } } } @@ -311,7 +318,7 @@ abstract class KotestPlugin : Plugin { // Kotest only supports unit tests, not instrumentation tests, so we can filter to // compilations that ends with UnitTest. In a standard android project these would be debugUnitTest // and releaseUnitTest, but if someone has custom build types then there could be more. - compilations.matching { it.name.endsWith("UnitTest") }.configureEach { + compilations.matching { it.name.endsWith(ANDROID_UNIT_TEST_SUFFIX) }.configureEach { val compilation = this val runtimeDependencyConfigurationName = compilation.runtimeDependencyConfigurationName @@ -363,7 +370,7 @@ abstract class KotestPlugin : Plugin { // we can execute check or test tasks with -Pkotest.include and this will then be // passed to the kotest runtime as an environment variable to filter specs and tests - project.findProperty("kotest.include")?.let { include.set(it.toString()) } + project.findProperty(KOTEST_INCLUDE_PROPERTY)?.let { include.set(it.toString()) } // we depend on the standard android test task to ensure compilation has happened dependsOn(androidTestTaskName(compilation)) @@ -371,8 +378,8 @@ abstract class KotestPlugin : Plugin { } // this means this kotest task will be run when the user runs "gradle kotest" - project.tasks.getByName("kotest") { - println("> Configuring kotest task for $kotestTaskName") + project.tasks.getByName(KOTEST_TASK_NAME) { + project.logger.info("> Configuring kotest task for $kotestTaskName") dependsOn(task) } } @@ -382,8 +389,8 @@ abstract class KotestPlugin : Plugin { * Returns "release" or "debug" depending on the current build type, etc. */ private fun androidBuildType(compilation: KotlinCompilation<*>): String { - require(compilation.name.endsWith("UnitTest")) { "Only unit tests are supported" } - return compilation.name.removeSuffix("UnitTest").lowercase() + require(compilation.name.endsWith(ANDROID_UNIT_TEST_SUFFIX)) { "Only unit tests are supported" } + return compilation.name.removeSuffix(ANDROID_UNIT_TEST_SUFFIX).lowercase() } /** diff --git a/kotest-tests/kotest-tests-config-project/src/jvmTest/kotlin/com/sksamuel/kotest/config/classname/DefaultFqnConfigClassTest.kt b/kotest-tests/kotest-tests-config-project/src/jvmTest/kotlin/com/sksamuel/kotest/config/classname/DefaultFqnConfigClassTest.kt index bc146abf4f7..e99d6c601ed 100644 --- a/kotest-tests/kotest-tests-config-project/src/jvmTest/kotlin/com/sksamuel/kotest/config/classname/DefaultFqnConfigClassTest.kt +++ b/kotest-tests/kotest-tests-config-project/src/jvmTest/kotlin/com/sksamuel/kotest/config/classname/DefaultFqnConfigClassTest.kt @@ -16,7 +16,7 @@ class DefaultFqnConfigClassTest : FunSpec() { .withListener(collector) .withClasses(BarTest::class) .launch() - collector.result("bar")?.errorOrNull?.message shouldBe "Test 'bar' did not complete within 2ms" + collector.result("bar")?.errorOrNull?.message shouldBe "Test 'bar' did not complete within 25ms" } } } diff --git a/kotest-tests/kotest-tests-config-project/src/jvmTest/kotlin/io/kotest/provided/ProjectConfig.kt b/kotest-tests/kotest-tests-config-project/src/jvmTest/kotlin/io/kotest/provided/ProjectConfig.kt index 2a63c2da450..63acc6a761d 100644 --- a/kotest-tests/kotest-tests-config-project/src/jvmTest/kotlin/io/kotest/provided/ProjectConfig.kt +++ b/kotest-tests/kotest-tests-config-project/src/jvmTest/kotlin/io/kotest/provided/ProjectConfig.kt @@ -4,5 +4,5 @@ import io.kotest.core.config.AbstractProjectConfig import kotlin.time.Duration.Companion.milliseconds class ProjectConfig : AbstractProjectConfig() { - override val invocationTimeout = 2.milliseconds + override val invocationTimeout = 25.milliseconds }