Skip to content

Commit

Permalink
Updated eventually config (#2027)
Browse files Browse the repository at this point in the history
* Updated eventually config

* Updated eventually config
  • Loading branch information
sksamuel committed Jan 31, 2021
1 parent 8486e12 commit 25ae5c7
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 26 deletions.
@@ -1,3 +1,5 @@
@file:Suppress("BlockingMethodInNonBlockingContext")

package com.sksamuel.kotest.assertions.timing

import io.kotest.assertions.assertSoftly
Expand Down Expand Up @@ -170,7 +172,7 @@ class EventuallyTest : WordSpec() {
5.seconds,
250.milliseconds.fixed(),
predicate = { t == "xxxxxxxxxxx" },
listener = { _ -> latch.countDown() },
listener = { latch.countDown() },
) {
t += "x"
t
Expand Down Expand Up @@ -204,10 +206,10 @@ class EventuallyTest : WordSpec() {
}

"eventually has a shareable configuration" {
val slow = EventuallyConfig<Int>(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
Expand All @@ -220,7 +222,7 @@ class EventuallyTest : WordSpec() {
5
}

eventually(fast) {
eventually(fast, predicate = { i == 1 }) {
i++
}

Expand Down
@@ -1,7 +1,5 @@
package io.kotest.assertions

typealias SuspendingPredicate<T> = suspend (T) -> Boolean

typealias SuspendingProducer<T> = suspend () -> T


Expand Down
@@ -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
Expand All @@ -19,14 +21,21 @@ suspend fun <T : Any> eventually(
duration: Duration,
interval: Interval,
f: SuspendingProducer<T>
): T = eventually(EventuallyConfig(duration, interval), f)
): T = eventually(EventuallyConfig(duration, interval), f = f)

suspend fun <T> eventually(
duration: Duration,
interval: Interval,
predicate: EventuallyPredicate<T>,
f: SuspendingProducer<T>
): T = eventually(EventuallyConfig(duration = duration, interval), predicate = predicate, f = f)

suspend fun <T> eventually(
duration: Duration,
interval: Interval,
listener: EventuallyListener<T>,
f: SuspendingProducer<T>
): T = eventually(EventuallyConfig(duration = duration, interval, listener = listener), f)
): T = eventually(EventuallyConfig(duration = duration, interval), listener = listener, f = f)


@Deprecated(
Expand Down Expand Up @@ -61,19 +70,21 @@ suspend fun <T> eventually(duration: Duration, exceptionClass: KClass<out Throwa
suspend fun <T> eventually(
duration: Duration = Duration.INFINITE,
interval: Interval = 25.milliseconds.fixed(),
predicate: SuspendingPredicate<T> = { true },
predicate: EventuallyPredicate<T> = EventuallyPredicate { true },
listener: EventuallyListener<T> = EventuallyListener { },
retries: Int = Int.MAX_VALUE,
exceptionClass: KClass<out Throwable>? = null,
f: SuspendingProducer<T>
): 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 <T> eventually(
config: EventuallyConfig<T>,
config: EventuallyConfig,
predicate: EventuallyPredicate<T> = EventuallyPredicate { true },
listener: EventuallyListener<T> = EventuallyListener { },
f: SuspendingProducer<T>,
): T {
val start = TimeSource.Monotonic.markNow()
Expand All @@ -85,8 +96,8 @@ suspend fun <T> 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) {
Expand Down Expand Up @@ -121,28 +132,31 @@ suspend fun <T> eventually(
throw failure(message.toString())
}

data class EventuallyConfig<T>(
data class EventuallyConfig(
val duration: Duration = Duration.INFINITE,
val interval: Interval = 25.milliseconds.fixed(),
val predicate: SuspendingPredicate<T> = { true },
val listener: EventuallyListener<T> = EventuallyListener {},
val retries: Int = Int.MAX_VALUE,
val exceptionClass: KClass<out Throwable>? = null,
) {
init {
require(retries > 0) { "Retries should not be less than one" }
require(!duration.isNegative()) { "Duration cannot be negative" }
}
}

data class EventuallyState<T>(
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<T> {
fun test(result: T): Boolean
}

fun interface EventuallyListener<T> {
fun onEval(state: EventuallyState<T>)
}
@@ -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
Expand Down Expand Up @@ -42,7 +42,7 @@ suspend fun until(duration: Duration, interval: Interval = 1.seconds.fixed(), f:
"kotlin.time.seconds"
)
)
suspend fun <T> until(duration: Duration, predicate: SuspendingPredicate<T>, f: SuspendingProducer<T>): T =
suspend fun <T> until(duration: Duration, predicate: EventuallyPredicate<T>, f: SuspendingProducer<T>): T =
eventually(duration, 1.seconds.fixed(), predicate = predicate, f = f)

@Deprecated(
Expand All @@ -55,10 +55,9 @@ suspend fun <T> until(duration: Duration, predicate: SuspendingPredicate<T>, f:
suspend fun <T> until(
duration: Duration,
interval: Interval,
predicate: SuspendingPredicate<T>,
predicate: EventuallyPredicate<T>,
f: SuspendingProducer<T>
): T =
eventually(duration, interval, predicate = predicate, f = f)
): T = eventually(duration, interval, predicate = predicate, f = f)

@Deprecated(
"Use eventually", ReplaceWith(
Expand All @@ -69,7 +68,7 @@ suspend fun <T> until(
suspend fun <T> until(
duration: Duration,
interval: Interval,
predicate: SuspendingPredicate<T>,
predicate: EventuallyPredicate<T>,
listener: UntilListener<T>,
f: SuspendingProducer<T>
): T =
Expand Down

0 comments on commit 25ae5c7

Please sign in to comment.