From 25ae5c7142e4aa60a2ca067c680cb3d19f853a84 Mon Sep 17 00:00:00 2001 From: Sam Sam Date: Sun, 31 Jan 2021 17:05:19 -0600 Subject: [PATCH] Updated eventually config (#2027) * Updated eventually config * Updated eventually config --- .../assertions/timing/EventuallyTest.kt | 10 +++-- .../assertions/NondeterministicHelpers.kt | 2 - .../io/kotest/assertions/timing/eventually.kt | 42 ++++++++++++------- .../io/kotest/assertions/until/until.kt | 11 +++-- 4 files changed, 39 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..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 @@ -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 @@ -19,14 +21,21 @@ suspend fun eventually( duration: Duration, interval: Interval, f: SuspendingProducer -): T = eventually(EventuallyConfig(duration, interval), f) +): T = eventually(EventuallyConfig(duration, interval), f = f) + +suspend fun eventually( + duration: Duration, + interval: Interval, + predicate: EventuallyPredicate, + f: SuspendingProducer +): 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 +70,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 +96,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 +132,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 +148,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 =