From cdf605ca7cbe799ba782dd98063202b51c05566e Mon Sep 17 00:00:00 2001 From: Rafael Pili Date: Sat, 31 Jul 2021 14:16:28 -0300 Subject: [PATCH] feat: add distinct operator and tests --- .../kwik/generator/api/GeneratorOperators.kt | 26 +++++++++++++++++++ .../kwik/generator/api/DistinctTest.kt | 25 ++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 generator/api/src/commonTest/kotlin/com/github/jcornaz/kwik/generator/api/DistinctTest.kt diff --git a/generator/api/src/commonMain/kotlin/com/github/jcornaz/kwik/generator/api/GeneratorOperators.kt b/generator/api/src/commonMain/kotlin/com/github/jcornaz/kwik/generator/api/GeneratorOperators.kt index 4425f9c3..60fba7ba 100644 --- a/generator/api/src/commonMain/kotlin/com/github/jcornaz/kwik/generator/api/GeneratorOperators.kt +++ b/generator/api/src/commonMain/kotlin/com/github/jcornaz/kwik/generator/api/GeneratorOperators.kt @@ -3,6 +3,7 @@ package com.github.jcornaz.kwik.generator.api import kotlin.random.Random private const val DEFAULT_SAMPLE_PROBABILITY = 0.2 +private const val MAX_DISTINCT_TRIES = 100 /** * Returns a generator containing the results of applying the given transform function to each element emitted by @@ -40,6 +41,31 @@ public fun Generator.filter(predicate: (T) -> Boolean): Generator = public fun Generator.andThen(transform: (T) -> Generator): Generator = Generator { transform(generate(it)).generate(it) } +/** + * Returns a generator that emits items of the upstream generator only once (based on result of `hashCode` and `equals` method) + * + * Be sure to use a generator function that can generate a big set of values + * Be sure to not overuse it has it may slow down your tests + */ +public fun Generator.distinct(): Generator { + var generatedValues = mutableListOf() + return Generator { + var retries = 0 + val genSize = generatedValues.size + generatedValues.add(generate(it)) + generatedValues = generatedValues.toMutableSet().toMutableList() + while (genSize == generatedValues.size) { + if (retries > MAX_DISTINCT_TRIES) { + throw Exception("Number of retries exceeded maximum. Distinct cannot be applied to this resource") + } + generatedValues.add(generate(it)) + generatedValues = generatedValues.toMutableSet().toMutableList() + retries++ + } + generatedValues.last() + } +} + /** * @Deprecated Use `andThen` operator instead */ diff --git a/generator/api/src/commonTest/kotlin/com/github/jcornaz/kwik/generator/api/DistinctTest.kt b/generator/api/src/commonTest/kotlin/com/github/jcornaz/kwik/generator/api/DistinctTest.kt new file mode 100644 index 00000000..183ebb08 --- /dev/null +++ b/generator/api/src/commonTest/kotlin/com/github/jcornaz/kwik/generator/api/DistinctTest.kt @@ -0,0 +1,25 @@ +package com.github.jcornaz.kwik.generator.api + +import com.github.jcornaz.kwik.generator.test.AbstractGeneratorTest +import kotlin.math.absoluteValue +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertTrue + +class DistinctTest : AbstractGeneratorTest() { + override val generator: Generator = Generator { it: Random -> it.nextInt() }.map { it.toString() } + + companion object { + const val COLLISION_TAKEN_VALUES = 2000 + } + + @Test + fun applyDistinction() { + val gen: Generator = Generator { it.nextInt().absoluteValue / 2000 } + + // sequence known to produce collisions + val result = gen.distinct().randomSequence(1).take(COLLISION_TAKEN_VALUES) + assertTrue { COLLISION_TAKEN_VALUES == result.toList().size } + + } +} \ No newline at end of file