From 93635df9fde0955c7fd89288a904d44f4d17697d Mon Sep 17 00:00:00 2001 From: sksamuel Date: Sun, 31 Jan 2021 08:52:09 -0600 Subject: [PATCH 1/2] Updated eventually config --- .../assertions/timing/EventuallyTest.kt | 10 ++-- .../assertions/NondeterministicHelpers.kt | 2 - .../io/kotest/assertions/timing/eventually.kt | 47 +++++++++++++------ .../io/kotest/assertions/until/until.kt | 11 ++--- 4 files changed, 44 insertions(+), 26 deletions(-) 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 814ad4580eb..d39628a791f 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 @@ -1,3 +1,5 @@ +@file:Suppress("BlockingMethodInNonBlockingContext") + package com.sksamuel.kotest.assertions.timing import io.kotest.assertions.assertSoftly @@ -170,7 +172,7 @@ class EventuallyTest : WordSpec() { 5.seconds, 250.milliseconds.fixed(), predicate = { t == "xxxxxxxxxxx" }, - listener = { _ -> latch.countDown() }, + listener = { latch.countDown() }, ) { t += "x" t @@ -204,10 +206,10 @@ class EventuallyTest : WordSpec() { } "eventually has a shareable configuration" { - val slow = EventuallyConfig(duration = 5.seconds) + val slow = EventuallyConfig(duration = 5.seconds) var i = 0 - val fast = slow.copy(retries = 1, predicate = { i == 1 }) + val fast = slow.copy(retries = 1) assertSoftly { slow.retries shouldBe Int.MAX_VALUE @@ -220,7 +222,7 @@ class EventuallyTest : WordSpec() { 5 } - eventually(fast) { + eventually(fast, predicate = { i == 1 }) { i++ } diff --git a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/NondeterministicHelpers.kt b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/NondeterministicHelpers.kt index d6692ca774d..025aab2fcdb 100644 --- a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/NondeterministicHelpers.kt +++ b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/NondeterministicHelpers.kt @@ -1,7 +1,5 @@ package io.kotest.assertions -typealias SuspendingPredicate = suspend (T) -> Boolean - typealias SuspendingProducer = suspend () -> T 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 2af688c9540..19179253bc0 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 @@ -1,13 +1,15 @@ package io.kotest.assertions.timing -import io.kotest.assertions.SuspendingPredicate import io.kotest.assertions.SuspendingProducer import io.kotest.assertions.failure import io.kotest.assertions.until.Interval import io.kotest.assertions.until.fixed import kotlinx.coroutines.delay import kotlin.reflect.KClass -import kotlin.time.* +import kotlin.time.Duration +import kotlin.time.TimeMark +import kotlin.time.TimeSource +import kotlin.time.milliseconds /** * Runs a function until it doesn't throw as long as the specified duration hasn't passed @@ -16,17 +18,29 @@ suspend fun eventually(duration: Duration, f: SuspendingProducer): T = eventually(EventuallyConfig(duration = duration, exceptionClass = Throwable::class), f = f) suspend fun eventually( + duration: Duration, + f: SuspendingProducer +): T = eventually(EventuallyConfig(duration), f = f) + +suspend fun eventually( + duration: Duration, + interval: Interval, + f: SuspendingProducer +): T = eventually(EventuallyConfig(duration, interval), f = f) + +suspend fun eventually( duration: Duration, interval: Interval, + predicate: EventuallyPredicate, f: SuspendingProducer -): T = eventually(EventuallyConfig(duration, interval), f) +): T = eventually(EventuallyConfig(duration = duration, interval), predicate = predicate, f = f) suspend fun eventually( duration: Duration, interval: Interval, listener: EventuallyListener, f: SuspendingProducer -): T = eventually(EventuallyConfig(duration = duration, interval, listener = listener), f) +): T = eventually(EventuallyConfig(duration = duration, interval), listener = listener, f = f) @Deprecated( @@ -61,19 +75,21 @@ suspend fun eventually(duration: Duration, exceptionClass: KClass eventually( duration: Duration = Duration.INFINITE, interval: Interval = 25.milliseconds.fixed(), - predicate: SuspendingPredicate = { true }, + predicate: EventuallyPredicate = EventuallyPredicate { true }, listener: EventuallyListener = EventuallyListener { }, retries: Int = Int.MAX_VALUE, exceptionClass: KClass? = null, f: SuspendingProducer -): T = eventually(EventuallyConfig(duration, interval, predicate, listener, retries, exceptionClass), f) +): T = eventually(EventuallyConfig(duration, interval, retries, exceptionClass), predicate, listener, 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. */ suspend fun eventually( - config: EventuallyConfig, + config: EventuallyConfig, + predicate: EventuallyPredicate = EventuallyPredicate { true }, + listener: EventuallyListener = EventuallyListener { }, f: SuspendingProducer, ): T { val start = TimeSource.Monotonic.markNow() @@ -85,8 +101,8 @@ suspend fun eventually( while (end.hasNotPassedNow() && times < config.retries) { try { val result = f() - config.listener.onEval(EventuallyState(result, start, end, times, firstError, lastError)) - if (config.predicate(result)) { + listener.onEval(EventuallyState(result, start, end, times, firstError, lastError)) + if (predicate.test(result)) { return result } } catch (e: Throwable) { @@ -121,16 +137,15 @@ suspend fun eventually( throw failure(message.toString()) } -data class EventuallyConfig( +data class EventuallyConfig( val duration: Duration = Duration.INFINITE, val interval: Interval = 25.milliseconds.fixed(), - val predicate: SuspendingPredicate = { true }, - val listener: EventuallyListener = EventuallyListener {}, val retries: Int = Int.MAX_VALUE, val exceptionClass: KClass? = null, ) { init { require(retries > 0) { "Retries should not be less than one" } + require(!duration.isNegative()) { "Duration cannot be negative" } } } @@ -138,11 +153,15 @@ data class EventuallyState( val result: T, val start: TimeMark, val end: TimeMark, - val times: Int, + val iteration: Int, val firstError: Throwable?, - val lastError: Throwable?, + val thisError: Throwable?, ) +fun interface EventuallyPredicate { + fun test(result: T): Boolean +} + fun interface EventuallyListener { fun onEval(state: EventuallyState) } 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 35ffe232a6f..8630ec7712d 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 @@ -1,7 +1,7 @@ package io.kotest.assertions.until -import io.kotest.assertions.SuspendingPredicate import io.kotest.assertions.SuspendingProducer +import io.kotest.assertions.timing.EventuallyPredicate import io.kotest.assertions.timing.eventually import kotlin.time.Duration import kotlin.time.seconds @@ -42,7 +42,7 @@ suspend fun until(duration: Duration, interval: Interval = 1.seconds.fixed(), f: "kotlin.time.seconds" ) ) -suspend fun until(duration: Duration, predicate: SuspendingPredicate, f: SuspendingProducer): T = +suspend fun until(duration: Duration, predicate: EventuallyPredicate, f: SuspendingProducer): T = eventually(duration, 1.seconds.fixed(), predicate = predicate, f = f) @Deprecated( @@ -55,10 +55,9 @@ suspend fun until(duration: Duration, predicate: SuspendingPredicate, f: suspend fun until( duration: Duration, interval: Interval, - predicate: SuspendingPredicate, + predicate: EventuallyPredicate, f: SuspendingProducer -): T = - eventually(duration, interval, predicate = predicate, f = f) +): T = eventually(duration, interval, predicate = predicate, f = f) @Deprecated( "Use eventually", ReplaceWith( @@ -69,7 +68,7 @@ suspend fun until( suspend fun until( duration: Duration, interval: Interval, - predicate: SuspendingPredicate, + predicate: EventuallyPredicate, listener: UntilListener, f: SuspendingProducer ): T = From 599042d63c925ff1d482a115102e9e5fb12c7b6e Mon Sep 17 00:00:00 2001 From: sksamuel Date: Sun, 31 Jan 2021 09:03:31 -0600 Subject: [PATCH 2/2] Updated eventually config --- .../kotlin/io/kotest/assertions/timing/eventually.kt | 5 ----- 1 file changed, 5 deletions(-) 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 19179253bc0..14a52290964 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 @@ -17,11 +17,6 @@ import kotlin.time.milliseconds suspend fun eventually(duration: Duration, f: SuspendingProducer): T = eventually(EventuallyConfig(duration = duration, exceptionClass = Throwable::class), f = f) -suspend fun eventually( - duration: Duration, - f: SuspendingProducer -): T = eventually(EventuallyConfig(duration), f = f) - suspend fun eventually( duration: Duration, interval: Interval,