
commonMain.io.kotest.framework.concurrency.continually.kt Maven / Gradle / Ivy
package io.kotest.framework.concurrency
import io.kotest.assertions.failure
import io.kotest.common.ExperimentalKotest
import io.kotest.mpp.timeInMillis
import kotlinx.coroutines.delay
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@OptIn(ExperimentalKotest::class)
typealias ContinuallyListener = (ContinuallyState) -> Unit
@ExperimentalKotest
data class ContinuallyConfig(
val duration: Long = defaultDuration,
val interval: Interval = defaultInterval,
val initialDelay: Long = defaultDelay,
val listener: ContinuallyListener? = null,
)
@ExperimentalKotest
class ContinuallyBuilder {
var duration: Long = defaultDuration
var interval: Interval = defaultInterval
var initialDelay: Long = defaultDelay
var listener: ContinuallyListener? = null
fun toConfig() = ContinuallyConfig(
duration = duration, interval = interval, initialDelay = initialDelay, listener = listener
)
constructor()
constructor(config: ContinuallyConfig) {
duration = config.duration
interval = config.interval
initialDelay = config.initialDelay
listener = config.listener
}
}
@ExperimentalKotest
data class ContinuallyState(val result: T, val start: Long, val end: Long, val times: Int)
@ExperimentalKotest
private suspend fun ContinuallyConfig.invoke(f: suspend () -> T): T? {
delay(initialDelay)
val start = timeInMillis()
val end = start + duration
var times = 0
var result: T? = null
while (timeInMillis() < end) {
try {
result = f()
listener?.invoke(ContinuallyState(result, start, end, times))
} catch (e: AssertionError) {
// if this is the first time the check was executed then just rethrow the underlying error
if (times == 0)
throw e
// if not the first attempt then include how many times/for how long the test passed
throw failure(
"Test failed after ${start}ms; expected to pass for ${duration}ms; attempted $times times\nUnderlying failure was: ${e.message}",
e
)
}
delay(interval.next(++times))
}
return result
}
// region continually
@ExperimentalKotest
suspend fun continually(
config: ContinuallyConfig, configure: ContinuallyBuilder.() -> Unit, @BuilderInference test: suspend () -> T
): T? {
val builder = ContinuallyBuilder(config).apply(configure)
return builder.toConfig().invoke(test)
}
@ExperimentalKotest
suspend fun continually(
configure: ContinuallyBuilder.() -> Unit, @BuilderInference test: suspend () -> T
): T? {
val builder = ContinuallyBuilder().apply(configure)
return builder.toConfig().invoke(test)
}
@ExperimentalTime
@ExperimentalKotest
suspend fun continually(duration: Duration, test: suspend () -> T): T? =
continually(duration.inWholeMilliseconds, test)
@ExperimentalKotest
suspend fun continually(duration: Long, test: suspend () -> T): T? = continually({ this.duration = duration }, test)
// endregion
© 2015 - 2025 Weber Informatics LLC | Privacy Policy