diff --git a/kotest-assertions/kotest-assertions-core/build.gradle.kts b/kotest-assertions/kotest-assertions-core/build.gradle.kts index fa8fbaf8ae0..2d206140db2 100644 --- a/kotest-assertions/kotest-assertions-core/build.gradle.kts +++ b/kotest-assertions/kotest-assertions-core/build.gradle.kts @@ -38,14 +38,6 @@ kotlin { iosArm32() } - targets.all { - compilations.all { - kotlinOptions { - freeCompilerArgs = freeCompilerArgs + listOf("-Xopt-in=kotlin.RequiresOptIn", "-Xopt-in=kotlin.time.ExperimentalTime") - } - } - } - sourceSets { val commonMain by getting { @@ -75,6 +67,11 @@ kotlin { implementation(Libs.Apache.commonslang) } } + + all { + languageSettings.useExperimentalAnnotation("kotlin.time.ExperimentalTime") + languageSettings.useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference") + } } } diff --git a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/timing/ContinuallyTest.kt b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/timing/ContinuallyTest.kt index ad3851c6bc2..8a2b7dd3c60 100644 --- a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/timing/ContinuallyTest.kt +++ b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/timing/ContinuallyTest.kt @@ -11,7 +11,6 @@ import kotlin.time.hours import kotlin.time.milliseconds import kotlin.time.seconds -@OptIn(ExperimentalTime::class) class ContinuallyTest : WordSpec() { init { diff --git a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/timing/EventuallyTest.kt b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/timing/EventuallyTest.kt index ba9c2977e83..814ad4580eb 100644 --- a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/timing/EventuallyTest.kt +++ b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/timing/EventuallyTest.kt @@ -20,7 +20,6 @@ import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlin.time.* -@OptIn(ExperimentalTime::class) class EventuallyTest : WordSpec() { init { @@ -167,8 +166,12 @@ class EventuallyTest : WordSpec() { "eventually with T predicate, interval, and listener" { var t = "" val latch = CountDownLatch(5) - val result = eventually(5.seconds, 250.milliseconds.fixed(), - listener = { _, _ -> latch.countDown() }, predicate = { t == "xxxxxxxxxxx" }) { + val result = eventually( + 5.seconds, + 250.milliseconds.fixed(), + predicate = { t == "xxxxxxxxxxx" }, + listener = { _ -> latch.countDown() }, + ) { t += "x" t } @@ -187,8 +190,12 @@ class EventuallyTest : WordSpec() { "support fibonacci intervals" { var t = "" val latch = CountDownLatch(5) - val result = eventually(10.seconds, 200.milliseconds.fibonacci(), - listener = { _, _ -> latch.countDown() }, predicate = { t == "xxxxxx" }) { + val result = eventually( + duration = 10.seconds, + interval = 200.milliseconds.fibonacci(), + predicate = { t == "xxxxxx" }, + listener = { latch.countDown() }, + ) { t += "x" t } @@ -197,8 +204,10 @@ class EventuallyTest : WordSpec() { } "eventually has a shareable configuration" { - val slow = EventuallyConfig(duration = 5.seconds) - val fast = slow.copy(retries = 1) + val slow = EventuallyConfig(duration = 5.seconds) + + var i = 0 + val fast = slow.copy(retries = 1, predicate = { i == 1 }) assertSoftly { slow.retries shouldBe Int.MAX_VALUE @@ -211,8 +220,7 @@ class EventuallyTest : WordSpec() { 5 } - var i = 0 - eventually(fast, predicate = { i == 1 }) { + eventually(fast) { i++ } diff --git a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/until/UntilTest.kt b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/until/UntilTest.kt index b074caa98b2..f35a3d666b7 100644 --- a/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/until/UntilTest.kt +++ b/kotest-assertions/kotest-assertions-core/src/jvmTest/kotlin/com/sksamuel/kotest/assertions/until/UntilTest.kt @@ -1,7 +1,6 @@ package com.sksamuel.kotest.assertions.until import io.kotest.assertions.throwables.shouldThrow -import io.kotest.assertions.timing.eventually import io.kotest.assertions.until.fibonacci import io.kotest.assertions.until.fixed import io.kotest.assertions.until.until @@ -10,11 +9,9 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit -import kotlin.time.ExperimentalTime import kotlin.time.milliseconds import kotlin.time.seconds -@OptIn(ExperimentalTime::class) class UntilTest : FunSpec({ test("until with boolean predicate") { diff --git a/kotest-assertions/kotest-assertions-shared/build.gradle.kts b/kotest-assertions/kotest-assertions-shared/build.gradle.kts index 25f6909d36a..fc64cf0d9b9 100644 --- a/kotest-assertions/kotest-assertions-shared/build.gradle.kts +++ b/kotest-assertions/kotest-assertions-shared/build.gradle.kts @@ -98,6 +98,11 @@ kotlin { val tvosMain by getting { dependsOn(desktopMain) } + + all { + languageSettings.useExperimentalAnnotation("kotlin.time.ExperimentalTime") + languageSettings.useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference") + } } } diff --git a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/Retry.kt b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/Retry.kt index f210f24f007..58652f8e091 100644 --- a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/Retry.kt +++ b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/Retry.kt @@ -4,16 +4,15 @@ import io.kotest.mpp.bestName import kotlin.reflect.KClass import kotlin.time.Duration import kotlin.time.DurationUnit -import kotlin.time.ExperimentalTime import kotlin.time.TimeSource import kotlin.time.seconds import kotlinx.coroutines.delay /** * Retry [f] until it's a success or [maxRetry]/[timeout] is reached - * + * * This will treat any Exception as a failure, along with [AssertionError]. - * + * * Retry delay might increase exponentially if you choose a [multiplier] value. For example, if you want to configure * 5 [maxRetry], with an initial [delay] of 1s between requests, the delay between requests will increase when you * choose 2 as your [multiplier]: @@ -21,11 +20,10 @@ import kotlinx.coroutines.delay * 2 - Failed (wait 2s before retrying) * 3 - Failed (wait 4s before retrying) * .. - * + * * If either timeout or max retries is reached, the execution will be aborted and an exception will be thrown. - * + * * */ -@OptIn(ExperimentalTime::class) suspend fun retry( maxRetry: Int, timeout: Duration, @@ -51,7 +49,6 @@ suspend fun retry( * If either timeout or max retries is reached, the execution will be aborted and an exception will be thrown. * * */ -@OptIn(ExperimentalTime::class) suspend fun retry( maxRetry: Int, timeout: Duration, diff --git a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/timing/continually.kt b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/timing/continually.kt index 24ad0b050e9..13b10061d13 100644 --- a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/timing/continually.kt +++ b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/timing/continually.kt @@ -7,7 +7,6 @@ import io.kotest.assertions.until.fixed import kotlinx.coroutines.delay import kotlin.time.* -@OptIn(ExperimentalTime::class) data class ContinuallyState(val start: TimeMark, val end: TimeMark, val times: Int) fun interface ContinuallyListener { @@ -18,7 +17,6 @@ fun interface ContinuallyListener { } } -@OptIn(ExperimentalTime::class) data class Continually ( val duration: Duration = Duration.INFINITE, val interval: Interval = 25.milliseconds.fixed(), @@ -50,13 +48,11 @@ data class Continually ( } } -@OptIn(ExperimentalTime::class) @Deprecated("Use continually with an interval, using Duration based poll is deprecated", ReplaceWith("continually(duration, poll.fixed(), f = f)", "io.kotest.assertions.until.fixed") ) suspend fun continually(duration: Duration, poll: Duration, f: suspend () -> T) = continually(duration, poll.fixed(), f = f) -@OptIn(ExperimentalTime::class) suspend fun continually(duration: Duration, interval: Interval = 10.milliseconds.fixed(), f: suspend () -> T) = Continually(duration, interval).invoke(f) diff --git a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/timing/eventually.kt b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/timing/eventually.kt index e37733ec8ed..2af688c9540 100644 --- a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/timing/eventually.kt +++ b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/timing/eventually.kt @@ -12,34 +12,40 @@ import kotlin.time.* /** * Runs a function until it doesn't throw as long as the specified duration hasn't passed */ -@OptIn(ExperimentalTime::class) suspend fun eventually(duration: Duration, f: SuspendingProducer): T = eventually(EventuallyConfig(duration = duration, exceptionClass = Throwable::class), f = f) -/** - * Runs a function until it doesn't throw and the result satisfies the predicate as long as the specified duration hasn't passed - */ -@OptIn(ExperimentalTime::class) -suspend fun eventually(duration: Duration, predicate: SuspendingPredicate, f: SuspendingProducer): T = - eventually(EventuallyConfig(duration = duration, exceptionClass = Throwable::class), predicate, f) +suspend fun eventually( + duration: Duration, + interval: Interval, + f: SuspendingProducer +): T = eventually(EventuallyConfig(duration, interval), f) + +suspend fun eventually( + duration: Duration, + interval: Interval, + listener: EventuallyListener, + f: SuspendingProducer +): T = eventually(EventuallyConfig(duration = duration, interval, listener = listener), f) + -@OptIn(ExperimentalTime::class) -@Deprecated(""" +@Deprecated( + """ Use eventually with an interval, using Duration based poll is deprecated. To convert an existing duration to an interval you can Duration.fixed(), Duration.exponential(), or Duration.fibonacci(). """, ReplaceWith( "eventually(duration, interval = poll.fixed(), f = f)", "io.kotest.assertions.until.fixed" - )) + ) +) suspend fun eventually(duration: Duration, poll: Duration, f: SuspendingProducer): T = eventually(EventuallyConfig(duration = duration, interval = poll.fixed(), exceptionClass = Throwable::class), f = f) /** * Runs a function until it doesn't throw the specified exception as long as the specified duration hasn't passed */ -@OptIn(ExperimentalTime::class) -suspend fun eventually(duration: Duration, exceptionClass: KClass, f: SuspendingProducer): T = +suspend fun eventually(duration: Duration, exceptionClass: KClass, f: SuspendingProducer): T = eventually(EventuallyConfig(duration = duration, exceptionClass = exceptionClass), f = f) /** @@ -52,25 +58,22 @@ suspend fun eventually(duration: Duration, exceptionClass: KC * [eventually] will delay the specified [interval] between iterations, defaults to 25 [milliseconds] * [eventually] will pass the resulting value and state (see [EventuallyState]) into the optional [listener] */ -@OptIn(ExperimentalTime::class) suspend fun eventually( duration: Duration = Duration.INFINITE, interval: Interval = 25.milliseconds.fixed(), - listener: EventuallyListener = EventuallyListener.noop, - retries: Int = Int.MAX_VALUE, - exceptionClass: KClass? = Throwable::class, predicate: SuspendingPredicate = { true }, + listener: EventuallyListener = EventuallyListener { }, + retries: Int = Int.MAX_VALUE, + exceptionClass: KClass? = null, f: SuspendingProducer -): T = eventually(EventuallyConfig(duration, interval, listener, retries, exceptionClass), predicate, f) +): T = eventually(EventuallyConfig(duration, interval, predicate, listener, retries, exceptionClass), f) /** * Runs a function until it doesn't throw and the result satisfies the predicate as long as the specified duration hasn't passed * and uses [EventuallyConfig] to control the duration, interval, listener, retries, and exceptionClass. */ -@OptIn(ExperimentalTime::class) -suspend fun eventually( - config: EventuallyConfig, - predicate: SuspendingPredicate = { true }, +suspend fun eventually( + config: EventuallyConfig, f: SuspendingProducer, ): T { val start = TimeSource.Monotonic.markNow() @@ -82,8 +85,8 @@ suspend fun eventually( while (end.hasNotPassedNow() && times < config.retries) { try { val result = f() - config.listener.onEval(result, EventuallyState(start, end, times, firstError, lastError)) - if (predicate(result)) { + config.listener.onEval(EventuallyState(result, start, end, times, firstError, lastError)) + if (config.predicate(result)) { return result } } catch (e: Throwable) { @@ -118,28 +121,28 @@ suspend fun eventually( throw failure(message.toString()) } -@OptIn(ExperimentalTime::class) -data class EventuallyConfig ( +data class EventuallyConfig( val duration: Duration = Duration.INFINITE, val interval: Interval = 25.milliseconds.fixed(), - val listener: EventuallyListener = EventuallyListener.noop, + val predicate: SuspendingPredicate = { true }, + val listener: EventuallyListener = EventuallyListener {}, val retries: Int = Int.MAX_VALUE, - val exceptionClass: KClass? = null, + val exceptionClass: KClass? = null, ) { init { require(retries > 0) { "Retries should not be less than one" } } } -@OptIn(ExperimentalTime::class) -data class EventuallyState ( - val start: TimeMark, val end: TimeMark, val times: Int, val firstError: Throwable?, val lastError: Throwable?, +data class EventuallyState( + val result: T, + val start: TimeMark, + val end: TimeMark, + val times: Int, + val firstError: Throwable?, + val lastError: Throwable?, ) -fun interface EventuallyListener { - fun onEval(t: T, state: EventuallyState) - - companion object { - val noop = EventuallyListener { _, _ -> } - } +fun interface EventuallyListener { + fun onEval(state: EventuallyState) } diff --git a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/ExponentialInterval.kt b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/ExponentialInterval.kt index 4a089daec08..9a3d6d0a830 100644 --- a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/ExponentialInterval.kt +++ b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/ExponentialInterval.kt @@ -2,10 +2,8 @@ package io.kotest.assertions.until import kotlin.math.pow import kotlin.time.Duration -import kotlin.time.ExperimentalTime import kotlin.time.milliseconds -@OptIn(ExperimentalTime::class) class ExponentialInterval(private val base: Duration) : Interval { override fun toString() = "ExponentialInterval(${::base.name}=$base)" @@ -15,5 +13,4 @@ class ExponentialInterval(private val base: Duration) : Interval { } } -@OptIn(ExperimentalTime::class) fun Duration.exponential() = ExponentialInterval(this) diff --git a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/FibonacciInterval.kt b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/FibonacciInterval.kt index f774401fcb0..0e10f332527 100644 --- a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/FibonacciInterval.kt +++ b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/FibonacciInterval.kt @@ -1,7 +1,6 @@ package io.kotest.assertions.until import kotlin.time.Duration -import kotlin.time.ExperimentalTime import kotlin.time.milliseconds /** @@ -14,7 +13,6 @@ import kotlin.time.milliseconds * @param offset Added to the count, so if the offset is 4, then the first value will be the 4th fib number. * @param base The duration that is multiplied by the fibonacci value */ -@OptIn(ExperimentalTime::class) class FibonacciInterval(private val base: Duration, private val offset: Int) : Interval { init { @@ -30,7 +28,6 @@ class FibonacciInterval(private val base: Duration, private val offset: Int) : I } } -@OptIn(ExperimentalTime::class) fun Duration.fibonacci() = FibonacciInterval(this, 0) fun fibonacci(n: Int): Int { diff --git a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/FixedInterval.kt b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/FixedInterval.kt index 2af09b7d035..71c987d79f9 100644 --- a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/FixedInterval.kt +++ b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/FixedInterval.kt @@ -1,12 +1,10 @@ package io.kotest.assertions.until import kotlin.time.Duration -import kotlin.time.ExperimentalTime /** * Generates a fixed (linear) poll interval based on the supplied duration */ -@OptIn(ExperimentalTime::class) class FixedInterval(private val duration: Duration) : Interval { override fun toString() = "FixedInterval(${::duration.name}=$duration)" @@ -15,5 +13,4 @@ class FixedInterval(private val duration: Duration) : Interval { } } -@OptIn(ExperimentalTime::class) -fun Duration.fixed() = FixedInterval(this) +fun Duration.fixed(): FixedInterval = FixedInterval(this) diff --git a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/Interval.kt b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/Interval.kt index 9309b382e73..9bf27804bb8 100644 --- a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/Interval.kt +++ b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/Interval.kt @@ -1,12 +1,10 @@ package io.kotest.assertions.until import kotlin.time.Duration -import kotlin.time.ExperimentalTime /** * A [Interval] determines how often Kotest will invoke the predicate function for an [until] block. */ -@OptIn(ExperimentalTime::class) interface Interval { /** diff --git a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/until.kt b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/until.kt index 5c72fcac16c..35ffe232a6f 100644 --- a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/until.kt +++ b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/until/until.kt @@ -4,7 +4,6 @@ import io.kotest.assertions.SuspendingPredicate import io.kotest.assertions.SuspendingProducer import io.kotest.assertions.timing.eventually import kotlin.time.Duration -import kotlin.time.ExperimentalTime import kotlin.time.seconds @Deprecated("Use NondeterministicListener") @@ -25,7 +24,6 @@ fun untilListener(f: (T) -> Unit) = object : UntilListener { } } -@OptIn(ExperimentalTime::class) @Deprecated( "Use eventually or Eventually.invoke", ReplaceWith( @@ -36,7 +34,6 @@ fun untilListener(f: (T) -> Unit) = object : UntilListener { suspend fun until(duration: Duration, interval: Interval = 1.seconds.fixed(), f: suspend () -> Boolean) = eventually(duration, interval, f = f) -@OptIn(ExperimentalTime::class) @Deprecated( "Use eventually or Eventually.invoke", ReplaceWith( @@ -48,7 +45,6 @@ suspend fun until(duration: Duration, interval: Interval = 1.seconds.fixed(), f: suspend fun until(duration: Duration, predicate: SuspendingPredicate, f: SuspendingProducer): T = eventually(duration, 1.seconds.fixed(), predicate = predicate, f = f) -@OptIn(ExperimentalTime::class) @Deprecated( "Use eventually or Eventually.invoke", ReplaceWith( @@ -64,7 +60,6 @@ suspend fun until( ): T = eventually(duration, interval, predicate = predicate, f = f) -@OptIn(ExperimentalTime::class) @Deprecated( "Use eventually", ReplaceWith( "eventually(duration, interval, listener = { it, _ -> listener.onEval(it) }, predicate = predicate, f = f)", @@ -78,4 +73,4 @@ suspend fun until( listener: UntilListener, f: SuspendingProducer ): T = - eventually(duration, interval, listener = { it, _ -> listener.onEval(it) }, predicate = predicate, f = f) + eventually(duration, interval, listener = { listener.onEval(it.result) }, predicate = predicate, f = f)