Skip to content

Commit

Permalink
WIP Refactor throw handler
Browse files Browse the repository at this point in the history
  • Loading branch information
BenWoodworth committed Oct 26, 2023
1 parent cd75529 commit a533b52
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 76 deletions.
18 changes: 6 additions & 12 deletions src/commonMain/kotlin/Parameterize.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.benwoodworth.parameterize

import com.benwoodworth.parameterize.DefaultParameterizeContext.parameterizeConfiguration
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmInline
Expand Down Expand Up @@ -73,7 +74,7 @@ import kotlin.reflect.KProperty
//context(ParameterizeContext) // TODO
public fun ParameterizeContext.parameterize(
onFailure: ParameterizeConfiguration.OnFailureScope.(failure: Throwable) -> Unit = parameterizeConfiguration.onFailure,
onComplete: ParameterizeConfiguration.OnCompleteScope.() -> Unit = {}/*parameterizeConfiguration.onComplete*/,
onComplete: ParameterizeConfiguration.OnCompleteScope.() -> Unit = parameterizeConfiguration.onComplete,
block: ParameterizeScope.() -> Unit
) {
contract {
Expand All @@ -90,18 +91,11 @@ public fun ParameterizeContext.parameterize(
} catch (exception: ParameterizeException) {
throw exception
} catch (failure: Throwable) {
val onFailureScope = ParameterizeConfiguration.OnFailureScope(state)
onFailureScope.onFailure(failure)

if (onFailureScope.recordFailure) {

}

if (onFailureScope.breakEarly) break
state.handleFailure(onFailure, failure)
}
}

ParameterizeConfiguration.OnCompleteScope().onComplete()
state.handleComplete(onComplete)
}

/**
Expand All @@ -110,8 +104,8 @@ public fun ParameterizeContext.parameterize(
* @see parameterize
*/
public fun parameterize(
onFailure: ParameterizeConfiguration.OnFailureScope.(failure: Throwable) -> Unit = {}/*parameterizeConfiguration.onFailure*/,
onComplete: ParameterizeConfiguration.OnCompleteScope.() -> Unit = {}/*parameterizeConfiguration.onFailure*/,
onFailure: ParameterizeConfiguration.OnFailureScope.(failure: Throwable) -> Unit = parameterizeConfiguration.onFailure,
onComplete: ParameterizeConfiguration.OnCompleteScope.() -> Unit = parameterizeConfiguration.onComplete,
block: ParameterizeScope.() -> Unit
) {
contract {
Expand Down
31 changes: 16 additions & 15 deletions src/commonMain/kotlin/ParameterizeConfiguration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class ParameterizeConfiguration private constructor(
* ```
* onFailure = { failure ->
* // Only record the first 10 failures
* recordFailure = failureCount < 10
* recordFailure = failureCount <= 10
* }
* ```
*
Expand All @@ -56,7 +56,7 @@ public class ParameterizeConfiguration private constructor(
*/
public var onFailure: OnFailureScope.(failure: Throwable) -> Unit = from?.onFailure
?: {
// recordFailure = failureCount < 10 // TODO spec
recordFailure = failureCount <= 10 // TODO spec
}

/**
Expand All @@ -71,7 +71,7 @@ public class ParameterizeConfiguration private constructor(
*/
public var onComplete: OnCompleteScope.() -> Unit = from?.onComplete
?: {
// if (failureCount > 0) throw ParameterizeFailedError() // TODO spec
if (failureCount > 0) throw ParameterizeFailedError() // TODO spec
}

internal fun build(): ParameterizeConfiguration = ParameterizeConfiguration(
Expand All @@ -82,10 +82,11 @@ public class ParameterizeConfiguration private constructor(

public class OnFailureScope internal constructor(
private val state: ParameterizeState,
) {
public val iterationCount: Long = 0 // TODO
public val failureCount: Long = 0 // TODO
public val iterationCount: Long,

/** The number of failures that have occurred, including this failure. */
public val failureCount: Long
) {
public val parameterArguments: List<ParameterArgument<*>> by lazy {
state.getUsedParameterArguments()
}
Expand All @@ -107,19 +108,19 @@ public class ParameterizeConfiguration private constructor(

}

public class OnCompleteScope internal constructor() {
public val iterationCount: Long = 0 // TODO
public val failureCount: Long = 0 // TODO
public class OnCompleteScope internal constructor(
private val state: ParameterizeState,
public val iterationCount: Long,
public val failureCount: Long,

/**
* True if a [throwHandler][ParameterizeConfiguration.Builder.onFailure] result was [Break][ParameterizeThrowHandlerResult.Break]. TODO
*/
public val coveredAllCases: Boolean = false
public val coveredAllCases: Boolean,

public val recordedFailures: List<ParameterizeFailure> = emptyList()


public operator fun ParameterizeFailedError.Companion.invoke(): ParameterizeFailedError = TODO()
// ParameterizeFailedError(parameterArguments, cause)
public val recordedFailures: List<ParameterizeFailure>
) {
public operator fun ParameterizeFailedError.Companion.invoke(): ParameterizeFailedError =
ParameterizeFailedError(recordedFailures, iterationCount, failureCount)
}
}
6 changes: 4 additions & 2 deletions src/commonMain/kotlin/ParameterizeFailedError.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ public class ParameterizeFailedError internal constructor(
clearStackTrace()
}

private val parameterArguments = recordedFailures.first().parameterArguments
private val parameterArguments = recordedFailures.firstOrNull()?.parameterArguments

override val message: String = when (parameterArguments?.size) {
null -> "No recorded failures"

override val message: String = when (parameterArguments.size) {
0 -> "Failed with no arguments"

1 -> parameterArguments.single().let { (parameter, argument) ->
Expand Down
27 changes: 26 additions & 1 deletion src/commonMain/kotlin/ParameterizeFailure.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,30 @@ public class ParameterizeFailure internal constructor(
public val failure: Throwable,
public val parameterArguments: List<ParameterArgument<*>>
) {
override fun toString(): String = TODO()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false

other as ParameterizeFailure

if (failure != other.failure) return false
if (parameterArguments != other.parameterArguments) return false

return true
}

override fun hashCode(): Int {
var result = failure.hashCode()
result = 31 * result + parameterArguments.hashCode()
return result
}

override fun toString(): String =
"ParameterizeFailure(" +
"failure=$failure" +
", parameterArguments=$parameterArguments" +
")"
}

// <ParameterizeFailure(failure=java.lang.Throwable: failure, property parameterArguments (Kotlin reflection is not available)=[parameter = argument])>
// but was: <ParameterizeFailure(failure=kotlin.Throwable: failure, parameterArguments=[parameter = argument])>
43 changes: 35 additions & 8 deletions src/commonMain/kotlin/ParameterizeState.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.benwoodworth.parameterize

import com.benwoodworth.parameterize.ParameterizeConfiguration.OnCompleteScope
import com.benwoodworth.parameterize.ParameterizeConfiguration.OnFailureScope
import kotlin.reflect.KProperty

internal class ParameterizeState {
Expand All @@ -16,18 +18,25 @@ internal class ParameterizeState {
private var parameterCount = 0
private var parameterCountAfterAllUsed = 0

private var isFirstIteration: Boolean = true
private var iterationCount = 0L
private var failureCount = 0L
private val recordedFailures = mutableListOf<ParameterizeFailure>()

private var breakEarly = false
private var coveredAllCases = false

/**
* Starts the next iteration, or returns `false` if there isn't one.
*/
fun startNextIteration(): Boolean =
if (isFirstIteration) {
isFirstIteration = false
true
} else {
nextArgumentPermutationOrFalse()
}
fun startNextIteration(): Boolean {
val hasNextIteration = iterationCount == 0L || nextArgumentPermutationOrFalse()
coveredAllCases = !hasNextIteration

val shouldContinue = hasNextIteration && !breakEarly
if (shouldContinue) iterationCount++

return shouldContinue
}

fun <T> declareParameter(property: KProperty<T>, arguments: Iterable<T>): ParameterDelegate<Nothing> {
parameterBeingUsed.let {
Expand Down Expand Up @@ -124,4 +133,22 @@ internal class ParameterizeState {
parameters.take(parameterCount)
.filter { it.hasBeenUsed }
.mapNotNull { it.getParameterArgumentOrNull() }

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

val result = OnFailureScope(this, iterationCount, failureCount)
.apply { onFailure(failure) }

breakEarly = result.breakEarly

if (result.recordFailure) {
recordedFailures += ParameterizeFailure(failure, result.parameterArguments)
}
}

fun handleComplete(onComplete: OnCompleteScope.() -> Unit) {
OnCompleteScope(this, iterationCount, failureCount, coveredAllCases, recordedFailures)
.apply { onComplete() }
}
}
54 changes: 35 additions & 19 deletions src/commonTest/kotlin/ParameterizeConfigurationOnCompleteSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,29 @@ class ParameterizeConfigurationOnCompleteSpec {
}
}

@Test
fun iteration_count_should_be_correct_with_break() {
var expectedIterationCount = 0L

parameterize(
onFailure = {
breakEarly = true
},
onComplete = {
assertEquals(expectedIterationCount, iterationCount)
}
) {
val iteration by parameter(0..100)
useParameter(iteration)

expectedIterationCount++

if (iteration == 50) {
fail()
}
}
}

@Test
fun failure_count_should_be_correct() {
var expectedFailureCount = 0L
Expand Down Expand Up @@ -164,8 +187,10 @@ class ParameterizeConfigurationOnCompleteSpec {
var lastIteration = -1
parameterize(
onFailure = { failure ->
recordFailure = lastIteration % 3 == 0
if (recordFailure) expectedRecordedFailures += ParameterizeFailure(failure, parameterArguments)
if (lastIteration % 3 == 0) {
recordFailure = true
expectedRecordedFailures += ParameterizeFailure(failure, parameterArguments)
}
},
onComplete = {
assertEquals(expectedRecordedFailures, recordedFailures)
Expand All @@ -182,34 +207,25 @@ class ParameterizeConfigurationOnCompleteSpec {
}

@Test
fun error_constructor_should_provide_correct_values() {
val expectedRecordedFailures = mutableListOf<ParameterizeFailure>()
val expectedIterationCount = -1L
val expectedFailureCount = -1L

val actualError: ParameterizeFailedError
var lastIteration = -1

fun error_constructor_should_build_error_with_correct_values() {
parameterize(
onFailure = { failure ->
recordFailure = lastIteration % 3 == 0
if (recordFailure) expectedRecordedFailures += ParameterizeFailure(failure, parameterArguments)
onFailure = {
recordFailure = iterationCount % 3 == 0L
},
onComplete = {
actualError = ParameterizeFailedError()
val error = ParameterizeFailedError()

assertEquals(recordedFailures, error.recordedFailures, error::recordedFailures.name)
assertEquals(iterationCount, error.iterationCount, error::iterationCount.name)
assertEquals(failureCount, error.failureCount, error::failureCount.name)
}
) {
val iterations = 0..100
val iteration by parameter(iterations)
lastIteration = iteration

if (iteration % 2 == 0 || iteration % 3 == 0) {
fail("$iteration")
}
}

assertEquals(expectedRecordedFailures, actualError.recordedFailures, actualError::recordedFailures.name)
assertEquals(expectedIterationCount, actualError.iterationCount, actualError::iterationCount.name)
assertEquals(expectedFailureCount, actualError.failureCount, actualError::failureCount.name)
}
}
42 changes: 41 additions & 1 deletion src/commonTest/kotlin/ParameterizeConfigurationOnFailureSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.fail

class ParameterizeConfigurationOnFailureSpec {
class ParameterizeConfigurationOnFailureSpec : ParameterizeContext {
override val parameterizeConfiguration = ParameterizeConfiguration {
onComplete = {} // Don't throw because of the tested failures
}

@Test
fun should_be_called_once_per_failure() {
val failureIterations = listOf(1, 3, 4, 7, 9, 10)
Expand Down Expand Up @@ -86,6 +90,42 @@ class ParameterizeConfigurationOnFailureSpec {
}
}

@Test
fun iteration_count_should_be_correct() {
var expectedIterationCount = 0L

parameterize(
onFailure = {
assertEquals(expectedIterationCount, iterationCount)
}
) {
val iteration by parameter(0..100)
useParameter(iteration)

expectedIterationCount++
fail(iteration.toString())
}
}

@Test
fun failure_count_should_be_correct() {
var expectedFailureCount = 0L

parameterize(
onFailure = {
assertEquals(expectedFailureCount, failureCount)
}
) {
val iteration by parameter(0..100)
useParameter(iteration)

if (iteration % 2 == 0 || iteration % 7 == 0) {
expectedFailureCount++
fail("iteration $iteration")
}
}
}

@Test
fun parameter_arguments_should_be_those_from_the_last_iteration() {
val lastParameterArguments = mutableListOf<Pair<String, *>>()
Expand Down
Loading

0 comments on commit a533b52

Please sign in to comment.