Skip to content

Commit

Permalink
WIP Extract configuration logic
Browse files Browse the repository at this point in the history
TODO
- Readme module.
- iterationFailures unused?
- remove check failure in next commit?
- check if PException was caught on next iteration
- configured shouldn't catch PException
- vanilla `parameterize` docs, and other configuration docs
- merge iterator -> state (vanilla & configured)

Improves performance of unconfigured parameterize, and greatly simplifies core logic
configuration may be dropped later
  • Loading branch information
BenWoodworth committed Oct 18, 2024
1 parent 229d033 commit b1e792e
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public inline fun parameterize(
val state = ConfiguredParameterizeState(configuration)

parameterize {
val scope = state.nextIteration() ?: return
val scope = state.nextIteration(this) ?: return

try {
scope.block()
} catch (failure: Throwable) {
state.handleFailure(failure)
state.handleThrow(failure)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import kotlin.coroutines.Continuation
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.intrinsics.createCoroutineUnintercepted
import kotlin.coroutines.resume
import kotlin.jvm.JvmInline
import kotlin.reflect.KProperty

@PublishedApi
Expand All @@ -32,6 +31,7 @@ internal class ConfiguredParameterizeState(
private var iterationCount = 0L
private var skipCount = 0L
private var failureCount = 0L
private var unhandledFailure: Throwable? = null
private val recordedFailures = mutableListOf<ParameterizeFailure>()

private var breakEarly = false
Expand All @@ -47,7 +47,7 @@ internal class ConfiguredParameterizeState(
return null
}

if (currentIterationScope != null) afterEach()
if (currentIterationScope != null) afterEach(false)

return ConfiguredParameterizeScope(parameterizeScope).also {
currentIterationScope = it
Expand All @@ -57,71 +57,66 @@ internal class ConfiguredParameterizeState(
}

@PublishedApi
internal fun handleFailure(failure: Throwable): Unit = when {
failure is ParameterizeControlFlow -> {
afterEach() // Since nextIteration() won't be called again to finalize the iteration
throw failure // TODO Could be blocked if afterEach throws
internal fun handleThrow(thrown: Throwable): Unit = when {
thrown is ParameterizeControlFlow -> {
// Counts as skip regardless of what kind of control flow, since either:
// - it's a continue this loop for this parameterize loop, meaning it *is* a proper skip;
// - or breaks out of this loop, meaning the count *is* inaccurate, but won't be used anyway.
skipCount++

throw thrown // TODO Could be blocked if afterEach throws
}

else -> {
afterEach() // Since the decorator should complete before onFailure is invoked

val result = handleFailure(configuration.onFailure, failure)
breakEarly = result.breakEarly
unhandledFailure = thrown
}
}

private fun beforeEach() {
decoratorCoroutine = DecoratorCoroutine(parameterizeState, configuration)
decoratorCoroutine = DecoratorCoroutine(this, configuration)
.also { it.beforeIteration() }
}

private fun afterEach() {
val currentIterationScope = checkNotNull(currentIterationScope) { "${::currentIterationScope.name} was null" }
private fun afterEach(isLastIteration: Boolean) {
val decoratorCoroutine = checkNotNull(decoratorCoroutine) { "${::decoratorCoroutine.name} was null" }

currentIterationScope.iterationCompleted = true
decoratorCoroutine.afterIteration()

this.currentIterationScope = null
this.decoratorCoroutine = null
}

fun handleContinue() {
skipCount++
unhandledFailure?.let { failure ->
unhandledFailure = null
handleFailure(failure)
}
}

@JvmInline
value class HandleFailureResult(val breakEarly: Boolean)

fun handleFailure(onFailure: OnFailureScope.(Throwable) -> Unit, failure: Throwable): HandleFailureResult {
private fun handleFailure(failure: Throwable) {
failureCount++

val scope = OnFailureScope(
val onFailureScope = OnFailureScope(
state = this,
iterationCount,
failureCount,
)

with(scope) {
onFailure(failure)
configuration.onFailure(onFailureScope, failure)

if (recordFailure) {
recordedFailures += ParameterizeFailure(failure, parameters)
}

return HandleFailureResult(breakEarly)
if (onFailureScope.recordFailure) {
recordedFailures += ParameterizeFailure(failure, onFailureScope.parameters)
}

breakEarly = onFailureScope.breakEarly
}

private fun handleComplete() {
afterEach()
fun handleComplete() {
afterEach(true)

val scope = OnCompleteScope(
iterationCount,
skipCount,
failureCount,
completedEarly = hasNextArgumentCombination,
completedEarly = false, // TODO hasNextArgumentCombination,
recordedFailures,
)

Expand All @@ -146,7 +141,7 @@ private class ConfiguredParameterizeScope(
* two separate parts, without needing to wrap the (inlined) [parameterize] block.
*/
private class DecoratorCoroutine(
private val parameterizeState: ParameterizeState,
private val parameterizeState: ConfiguredParameterizeState,
private val configuration: ParameterizeConfiguration
) {
private val scope = DecoratorScope(parameterizeState)
Expand All @@ -155,7 +150,7 @@ private class DecoratorCoroutine(
private var completed = false

private val iteration: suspend DecoratorScope.() -> Unit = {
parameterizeState.checkState(continueAfterIteration == null) {
check(continueAfterIteration == null) {
"Decorator must invoke the iteration function exactly once, but was invoked twice"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public class ParameterizeConfiguration internal constructor(
/** @see Builder.decorator */
@RestrictsSuspension
public class DecoratorScope internal constructor(
private val parameterizeState: ParameterizeState
private val parameterizeState: ConfiguredParameterizeState
) {
private var initializedIsLastIteration = false

Expand Down
11 changes: 3 additions & 8 deletions parameterize-core/src/commonMain/kotlin/ParameterizeState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,13 @@ internal class ParameterizeState {
*/
private var lastParameterWithNextArgument: ParameterState? = null

private var iterationCount = 0L
private var skipCount = 0L
private var failureCount = 0L
private var startedFirstIteration = true

val hasNextArgumentCombination: Boolean
get() = lastParameterWithNextArgument != null || iterationCount == 0L

val isFirstIteration: Boolean
get() = iterationCount == 1L
get() = lastParameterWithNextArgument != null || !startedFirstIteration

fun startNextIteration() {
iterationCount++
startedFirstIteration = true
parameterCount = 0

parameterToIterate = lastParameterWithNextArgument
Expand Down

0 comments on commit b1e792e

Please sign in to comment.