Best Kotest code snippet using io.kotest.property.arbitrary.arbs.Arb.single
builders.kt
Source:builders.kt
1package io.kotest.property.arbitrary2import io.kotest.property.Arb3import io.kotest.property.Classifier4import io.kotest.property.RandomSource5import io.kotest.property.Sample6import io.kotest.property.Shrinker7import io.kotest.property.sampleOf8import kotlin.coroutines.Continuation9import kotlin.coroutines.CoroutineContext10import kotlin.coroutines.EmptyCoroutineContext11import kotlin.coroutines.RestrictsSuspension12import kotlin.coroutines.intrinsics.startCoroutineUninterceptedOrReturn13import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn14import kotlin.coroutines.resume15/**16 * Creates a new [Arb] that performs no shrinking, has no edge cases and17 * generates values from the given function.18 */19fun <A> arbitrary(fn: suspend ArbitraryBuilderContext.(RandomSource) -> A): Arb<A> =20 arbitraryBuilder { rs -> fn(rs) }21/**22 * Creates a new [Arb] that performs shrinking using the supplied [Shrinker], has no edge cases and23 * generates values from the given function.24 */25fun <A> arbitrary(shrinker: Shrinker<A>, fn: suspend ArbitraryBuilderContext.(RandomSource) -> A): Arb<A> =26 arbitraryBuilder(shrinker) { rs -> fn(rs) }27/**28 * Creates a new [Arb] that classifies the generated values using the supplied [Classifier], has no edge cases and29 * generates values from the given function.30 */31fun <A> arbitrary(classifier: Classifier<A>, fn: suspend ArbitraryBuilderContext.(RandomSource) -> A): Arb<A> =32 arbitraryBuilder(null, classifier) { rs -> fn(rs) }33/**34 * Creates a new [Arb] that performs shrinking using the supplied [Shrinker],35 * classifies the generated values using the supplied [Classifier], has no edge cases and36 * generates values from the given function.37 */38fun <A> arbitrary(39 shrinker: Shrinker<A>,40 classifier: Classifier<A>,41 fn: suspend ArbitraryBuilderContext.(RandomSource) -> A42): Arb<A> =43 arbitraryBuilder(shrinker, classifier) { rs -> fn(rs) }44/**45 * Creates a new [Arb] that performs no shrinking, uses the given edge cases and46 * generates values from the given function.47 */48fun <A> arbitrary(edgecases: List<A>, fn: suspend ArbitraryBuilderContext.(RandomSource) -> A): Arb<A> =49 object : Arb<A>() {50 override fun edgecase(rs: RandomSource): A? = if (edgecases.isEmpty()) null else edgecases.random(rs.random)51 override fun sample(rs: RandomSource): Sample<A> = delegate.sample(rs)52 private val delegate = arbitraryBuilder { rs -> fn(rs) }53 }54/**55 * Creates a new [Arb] that performs shrinking using the supplied [Shrinker], uses the given edge cases and56 * generates values from the given function.57 */58fun <A> arbitrary(59 edgecases: List<A>,60 shrinker: Shrinker<A>,61 fn: suspend ArbitraryBuilderContext.(RandomSource) -> A62): Arb<A> = object : Arb<A>() {63 override fun edgecase(rs: RandomSource): A? = if (edgecases.isEmpty()) null else edgecases.random(rs.random)64 override fun sample(rs: RandomSource): Sample<A> = delegate.sample(rs)65 private val delegate = arbitraryBuilder(shrinker) { rs -> fn(rs) }66}67/**68 * Creates a new [Arb] that generates edge cases from the given [edgecaseFn] function69 * and generates samples from the given [sampleFn] function.70 */71fun <A> arbitrary(72 edgecaseFn: (RandomSource) -> A?,73 sampleFn: suspend ArbitraryBuilderContext.(RandomSource) -> A74): Arb<A> =75 object : Arb<A>() {76 override fun edgecase(rs: RandomSource): A? = edgecaseFn(rs)77 override fun sample(rs: RandomSource): Sample<A> = delegate.sample(rs)78 private val delegate: Arb<A> = arbitraryBuilder { rs -> sampleFn(rs) }79 }80/**81 * Creates a new [Arb] that generates edge cases from the given [edgecaseFn] function,82 * performs shrinking using the supplied [Shrinker], and generates samples from the given [sampleFn] function.83 */84fun <A> arbitrary(85 edgecaseFn: (RandomSource) -> A?,86 shrinker: Shrinker<A>,87 sampleFn: suspend ArbitraryBuilderContext.(RandomSource) -> A88): Arb<A> =89 object : Arb<A>() {90 override fun edgecase(rs: RandomSource): A? = edgecaseFn(rs)91 override fun sample(rs: RandomSource): Sample<A> = delegate.sample(rs)92 private val delegate: Arb<A> = arbitraryBuilder(shrinker) { rs -> sampleFn(rs) }93 }94/**95 * Creates a new [Arb] that performs no shrinking, has no edge cases and96 * generates values from the given function.97 */98suspend inline fun <A> generateArbitrary(99 crossinline fn: suspend GenerateArbitraryBuilderContext.(RandomSource) -> A100): Arb<A> = suspendArbitraryBuilder { rs -> fn(rs) }101/**102 * Creates a new [Arb] that performs shrinking using the supplied [Shrinker], has no edge cases and103 * generates values from the given function.104 */105suspend inline fun <A> generateArbitrary(106 shrinker: Shrinker<A>,107 crossinline fn: suspend GenerateArbitraryBuilderContext.(RandomSource) -> A108): Arb<A> = suspendArbitraryBuilder(shrinker, null) { rs -> fn(rs) }109/**110 * Creates a new [Arb] that classifies the generated values using the supplied [Classifier], has no edge cases and111 * generates values from the given function.112 */113suspend inline fun <A> generateArbitrary(114 classifier: Classifier<A>,115 crossinline fn: suspend GenerateArbitraryBuilderContext.(RandomSource) -> A116): Arb<A> = suspendArbitraryBuilder(null, classifier) { rs -> fn(rs) }117/**118 * Creates a new [Arb] that performs shrinking using the supplied [Shrinker],119 * classifies the generated values using the supplied [Classifier], has no edge cases and120 * generates values from the given function.121 */122suspend inline fun <A> generateArbitrary(123 shrinker: Shrinker<A>,124 classifier: Classifier<A>,125 crossinline fn: suspend GenerateArbitraryBuilderContext.(RandomSource) -> A126): Arb<A> = suspendArbitraryBuilder(shrinker, classifier) { rs -> fn(rs) }127/**128 * Creates a new [Arb] that performs no shrinking, uses the given edge cases and129 * generates values from the given function.130 */131suspend inline fun <A> generateArbitrary(132 edgecases: List<A>,133 crossinline fn: suspend GenerateArbitraryBuilderContext.(RandomSource) -> A134): Arb<A> = suspendArbitraryBuilder(null, null,135 if (edgecases.isEmpty()) null else { rs -> edgecases.random(rs.random) }136) { rs -> fn(rs) }137/**138 * Creates a new [Arb] that performs shrinking using the supplied [Shrinker], uses the given edge cases and139 * generates values from the given function.140 */141suspend inline fun <A> generateArbitrary(142 edgecases: List<A>,143 shrinker: Shrinker<A>,144 crossinline fn: suspend GenerateArbitraryBuilderContext.(RandomSource) -> A145): Arb<A> = suspendArbitraryBuilder(146 shrinker,147 null,148 if (edgecases.isEmpty()) null else { rs -> edgecases.random(rs.random) }149) { rs -> fn(rs) }150/**151 * Creates a new [Arb] that generates edge cases from the given [edgecaseFn] function152 * and generates samples from the given [sampleFn] function.153 */154suspend inline fun <A> generateArbitrary(155 crossinline edgecaseFn: (RandomSource) -> A?,156 crossinline sampleFn: suspend GenerateArbitraryBuilderContext.(RandomSource) -> A157): Arb<A> {158 val delegate: Arb<A> = suspendArbitraryBuilder { rs -> sampleFn(rs) }159 return object : Arb<A>() {160 override fun edgecase(rs: RandomSource): A? = edgecaseFn(rs)161 override fun sample(rs: RandomSource): Sample<A> = delegate.sample(rs)162 }163}164/**165 * Creates a new [Arb] that generates edge cases from the given [edgecaseFn] function,166 * performs shrinking using the supplied [Shrinker], and generates samples from the given [sampleFn] function.167 */168suspend inline fun <A> generateArbitrary(169 crossinline edgecaseFn: (RandomSource) -> A?,170 shrinker: Shrinker<A>,171 crossinline sampleFn: suspend GenerateArbitraryBuilderContext.(RandomSource) -> A172): Arb<A> {173 val delegate: Arb<A> = suspendArbitraryBuilder(shrinker) { rs -> sampleFn(rs) }174 return object : Arb<A>() {175 override fun edgecase(rs: RandomSource): A? = edgecaseFn(rs)176 override fun sample(rs: RandomSource): Sample<A> = delegate.sample(rs)177 }178}179/**180 * Creates a new [Arb] using [Continuation] using a stateless [builderFn].181 *182 * This function accepts an optional [shrinker], [classifier], and [edgecaseFn]. These parameters183 * will be passed to [ArbitraryBuilder].184 */185fun <A> arbitraryBuilder(186 shrinker: Shrinker<A>? = null,187 classifier: Classifier<A>? = null,188 edgecaseFn: EdgecaseFn<A>? = null,189 builderFn: suspend ArbitraryBuilderContext.(RandomSource) -> A190): Arb<A> = object : Arb<A>() {191 override fun edgecase(rs: RandomSource): A? = singleShotArb(SingleShotGenerationMode.Edgecase, rs).edgecase(rs)192 override fun sample(rs: RandomSource): Sample<A> = singleShotArb(SingleShotGenerationMode.Sample, rs).sample(rs)193 override val classifier: Classifier<out A>? = classifier194 /**195 * This function generates a new instance of a single shot arb.196 * DO NOT CACHE THE [Arb] returned by this function.197 *198 * This needs to be a function because at time of writing, Kotlin 1.5's [Continuation] is single shot.199 * With arbs, we ideally need multishot. To rerun [builderFn], we need to "reset" the continuation.200 *201 * The current way we do it is to recreate a fresh [SingleShotArbContinuation] instance that202 * will provide another single shot Arb. Hence the reason why this function is invoked203 * on every call to [sample] / [edgecase].204 */205 private fun singleShotArb(mode: SingleShotGenerationMode, rs: RandomSource): Arb<A> {206 val restrictedContinuation = SingleShotArbContinuation.Restricted(mode, rs) {207 /**208 * At the end of the suspension we got a generated value [A] as a comprehension result.209 * This value can either be a sample, or an edgecase.210 */211 val value: A = builderFn(rs)212 /**213 * Here we point A into an Arb<A> with the appropriate enrichments including214 * [Shrinker], [Classifier], and [EdgecaseFn]. When edgecase returns null, we pass the generated value215 * to the edgecase function so to make sure we retain all arbs' edgecases inside the comprehension.216 */217 ArbitraryBuilder({ value }, classifier, shrinker, { rs -> edgecaseFn?.invoke(rs) ?: value }).build()218 }219 return with(restrictedContinuation) {220 this@with.createSingleShotArb()221 }222 }223}224/**225 * Creates a new suspendable [Arb] using [Continuation] using a stateless [fn].226 *227 * This function accepts an optional [shrinker], [classifier], and [edgecaseFn]. These parameters228 * will be passed to [ArbitraryBuilder].229 */230suspend fun <A> suspendArbitraryBuilder(231 shrinker: Shrinker<A>? = null,232 classifier: Classifier<A>? = null,233 edgecaseFn: EdgecaseFn<A>? = null,234 fn: suspend GenerateArbitraryBuilderContext.(RandomSource) -> A235): Arb<A> = suspendCoroutineUninterceptedOrReturn { cont ->236 val arb = object : Arb<A>() {237 override fun edgecase(rs: RandomSource): A? = singleShotArb(SingleShotGenerationMode.Edgecase, rs).edgecase(rs)238 override fun sample(rs: RandomSource): Sample<A> = singleShotArb(SingleShotGenerationMode.Sample, rs).sample(rs)239 override val classifier: Classifier<out A>? = classifier240 /**241 * This function generates a new instance of a single shot arb.242 * DO NOT CACHE THE [Arb] returned by this function.243 *244 * This needs to be a function because at time of writing, Kotlin 1.5's [Continuation] is single shot.245 * With arbs, we ideally need multishot. To rerun [fn], we need to "reset" the continuation.246 *247 * The current way we do it is to recreate a fresh [SingleShotArbContinuation] instance that248 * will provide another single shot Arb. Hence the reason why this function is invoked249 * on every call to [sample] / [edgecase].250 */251 private fun singleShotArb(genMode: SingleShotGenerationMode, rs: RandomSource): Arb<A> {252 val suspendableContinuation = SingleShotArbContinuation.Suspendedable(genMode, rs, cont.context) {253 /**254 * At the end of the suspension we got a generated value [A] as a comprehension result.255 * This value can either be a sample, or an edgecase.256 */257 val value: A = fn(rs)258 /**259 * Here we point A into an Arb<A> with the appropriate enrichments including260 * [Shrinker], [Classifier], and [EdgecaseFn]. When edgecase returns null, we pass the generated value261 * to the edgecase function so to make sure we retain all arbs' edgecases inside the comprehension.262 */263 ArbitraryBuilder({ value }, classifier, shrinker, { rs -> edgecaseFn?.invoke(rs) ?: value }).build()264 }265 return with(suspendableContinuation) {266 this@with.createSingleShotArb()267 }268 }269 }270 cont.resume(arb)271}272typealias SampleFn<A> = (RandomSource) -> A273typealias EdgecaseFn<A> = (RandomSource) -> A?274class ArbitraryBuilder<A>(275 private val sampleFn: SampleFn<A>,276 private val classifier: Classifier<A>?,277 private val shrinker: Shrinker<A>?,278 private val edgecaseFn: EdgecaseFn<A>?,279) {280 companion object {281 fun <A> create(f: (RandomSource) -> A): ArbitraryBuilder<A> = ArbitraryBuilder(f, null, null, null)282 }283 fun withClassifier(classifier: Classifier<A>) = ArbitraryBuilder(sampleFn, classifier, shrinker, edgecaseFn)284 fun withShrinker(shrinker: Shrinker<A>) = ArbitraryBuilder(sampleFn, classifier, shrinker, edgecaseFn)285 fun withEdgecaseFn(edgecaseFn: EdgecaseFn<A>) = ArbitraryBuilder(sampleFn, classifier, shrinker, edgecaseFn)286 fun withEdgecases(edgecases: List<A>) = ArbitraryBuilder(sampleFn, classifier, shrinker) {287 if (edgecases.isEmpty()) null else edgecases.random(it.random)288 }289 fun build() = object : Arb<A>() {290 override val classifier: Classifier<out A>? = this@ArbitraryBuilder.classifier291 override fun edgecase(rs: RandomSource): A? = edgecaseFn?.invoke(rs)292 override fun sample(rs: RandomSource): Sample<A> {293 val sample = sampleFn(rs)294 return if (shrinker == null) Sample(sample) else sampleOf(sample, shrinker)295 }296 }297}298interface BaseArbitraryBuilderSyntax {299 /**300 * [bind] returns the generated value of an arb. This can either be a sample or an edgecase.301 */302 suspend fun <T> Arb<T>.bind(): T303}304@RestrictsSuspension305interface ArbitraryBuilderContext : BaseArbitraryBuilderSyntax306interface GenerateArbitraryBuilderContext : BaseArbitraryBuilderSyntax307enum class SingleShotGenerationMode { Edgecase, Sample }308sealed class SingleShotArbContinuation<F : BaseArbitraryBuilderSyntax, A>(309 override val context: CoroutineContext,310 private val generationMode: SingleShotGenerationMode,311 private val randomSource: RandomSource,312 private val fn: suspend F.() -> Arb<A>313) : Continuation<Arb<A>>, BaseArbitraryBuilderSyntax {314 class Restricted<A>(315 genMode: SingleShotGenerationMode,316 rs: RandomSource,317 fn: suspend ArbitraryBuilderContext.() -> Arb<A>318 ) : SingleShotArbContinuation<ArbitraryBuilderContext, A>(EmptyCoroutineContext, genMode, rs, fn),319 ArbitraryBuilderContext320 class Suspendedable<A>(321 genMode: SingleShotGenerationMode,322 rs: RandomSource,323 override val context: CoroutineContext,324 fn: suspend GenerateArbitraryBuilderContext.() -> Arb<A>325 ) : SingleShotArbContinuation<GenerateArbitraryBuilderContext, A>(context, genMode, rs, fn),326 GenerateArbitraryBuilderContext327 private lateinit var returnedArb: Arb<A>328 private var hasExecuted: Boolean = false329 override fun resumeWith(result: Result<Arb<A>>) {330 hasExecuted = true331 result.map { resultArb -> returnedArb = resultArb }.getOrThrow()332 }333 override suspend fun <T> Arb<T>.bind(): T = when (generationMode) {334 SingleShotGenerationMode.Edgecase -> this.edgecase(randomSource) ?: this.sample(randomSource).value335 SingleShotGenerationMode.Sample -> this.sample(randomSource).value336 }337 /**338 * It's important to understand that at the time of writing (Kotlin 1.5) [Continuation] is single shot,339 * i.e. it can only be resumed once. When it's possible to create multishot continuations in the future, we340 * might be able to simplify this further.341 *342 * The aforementioned limitation means the [Arb] that we construct through this mechanism can only be used343 * to generate exactly one value. Hence, to recycle and rerun the specified composed transformation,344 * we need to recreate the [SingleShotArbContinuation] instance and call [createSingleShotArb] again.345 */346 fun F.createSingleShotArb(): Arb<A> {347 require(!hasExecuted) { "continuation has already been executed, if you see this error please raise a bug report" }348 val result = fn.startCoroutineUninterceptedOrReturn(this@createSingleShotArb, this@SingleShotArbContinuation)349 @Suppress("UNCHECKED_CAST")350 returnedArb = result as Arb<A>351 return returnedArb352 }353}...
BuilderTest.kt
Source:BuilderTest.kt
1package com.sksamuel.kotest.property.arbitrary2import io.kotest.assertions.throwables.shouldNotThrowAny3import io.kotest.assertions.throwables.shouldThrow4import io.kotest.core.spec.style.FunSpec5import io.kotest.matchers.collections.shouldContainExactly6import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder7import io.kotest.matchers.ints.shouldBeBetween8import io.kotest.matchers.ints.shouldBeGreaterThan9import io.kotest.matchers.nulls.shouldBeNull10import io.kotest.matchers.shouldBe11import io.kotest.matchers.string.shouldHaveLengthBetween12import io.kotest.matchers.types.shouldBeSameInstanceAs13import io.kotest.property.Arb14import io.kotest.property.RandomSource15import io.kotest.property.arbitrary.Codepoint16import io.kotest.property.arbitrary.IntShrinker17import io.kotest.property.arbitrary.alphanumeric18import io.kotest.property.arbitrary.arbitrary19import io.kotest.property.arbitrary.constant20import io.kotest.property.arbitrary.edgecases21import io.kotest.property.arbitrary.flatMap22import io.kotest.property.arbitrary.generateArbitrary23import io.kotest.property.arbitrary.int24import io.kotest.property.arbitrary.map25import io.kotest.property.arbitrary.next26import io.kotest.property.arbitrary.numbers.IntClassifier27import io.kotest.property.arbitrary.single28import io.kotest.property.arbitrary.string29import io.kotest.property.arbitrary.take30import io.kotest.property.arbitrary.withEdgecases31import kotlinx.coroutines.withContext32import kotlin.coroutines.CoroutineContext33import kotlin.random.nextInt34class BuilderTest : FunSpec() {35 init {36 test("custom arb test") {37 arbitrary {38 it.random.nextInt(3..6)39 }.take(1000).toSet() shouldBe setOf(3, 4, 5, 6)40 }41 test("composition of arbs") {42 data class Person(val name: String, val age: Int)43 val personArb = arbitrary { rs ->44 val name = Arb.string(10..12).next(rs)45 val age = Arb.int(21, 150).next(rs)46 Person(name, age)47 }48 personArb.next().name.shouldHaveLengthBetween(10, 12)49 personArb.next().age.shouldBeBetween(21, 150)50 }51 context("arbitrary builder using restricted continuation") {52 test("should be stack safe") {53 val arb: Arb<Int> = arbitrary {54 (1..100000).map {55 Arb.int().bind()56 }.last()57 }58 val result = shouldNotThrowAny { arb.single(RandomSource.seeded(1234)) }59 result shouldBe -148693402360 }61 test("should be equivalent to chaining flatMaps") {62 val arbFlatMaps: Arb<String> =63 Arb.string(5, Codepoint.alphanumeric()).withEdgecases("edge1", "edge2").flatMap { first ->64 Arb.int(1..9).withEdgecases(5).flatMap { second ->65 Arb.int(101..109).withEdgecases(100 + second).map { third ->66 "$first $second $third"67 }68 }69 }70 val arb: Arb<String> = arbitrary {71 val first = Arb.string(5, Codepoint.alphanumeric()).withEdgecases("edge1", "edge2").bind()72 val second = Arb.int(1..9).withEdgecases(5).bind()73 val third = Arb.int(101..109).withEdgecases(100 + second).bind()74 "$first $second $third"75 }76 val flatMapsResult = arbFlatMaps.generate(RandomSource.seeded(12345L)).take(100).map { it.value }.toList()77 val builderResult = arb.generate(RandomSource.seeded(12345L)).take(100).map { it.value }.toList()78 // should be equivalent79 builderResult shouldContainExactly flatMapsResult80 }81 test("should bind edgecases") {82 val arb: Arb<String> = arbitrary {83 val first = Arb.string(5, Codepoint.alphanumeric()).withEdgecases("edge1", "edge2").bind()84 val second = Arb.int(1..9).withEdgecases(5).bind()85 val third = Arb.int(101..109).withEdgecases(100 + second, 109).bind()86 "$first $second $third"87 }88 arb.edgecases() shouldContainExactlyInAnyOrder setOf(89 "edge1 5 105",90 "edge2 5 105",91 "edge1 5 109",92 "edge2 5 109",93 )94 }95 test("should preserve edgecases of dependent arbs, even when intermideary arb(s) have no edgecases") {96 val arb: Arb<String> = arbitrary {97 val first = Arb.string(5, Codepoint.alphanumeric()).withEdgecases("edge1", "edge2").bind()98 val second = Arb.int(1..4).withEdgecases(emptyList()).bind()99 val third = Arb.int(101..109).withEdgecases(100 + second).bind()100 "$first $second $third"101 }102 arb.edgecases() shouldContainExactlyInAnyOrder setOf(103 "edge1 1 101",104 "edge1 2 102",105 "edge1 3 103",106 "edge1 4 104",107 "edge2 1 101",108 "edge2 2 102",109 "edge2 3 103",110 "edge2 4 104"111 )112 }113 test("should assign edgecases") {114 val edges = setOf("edge1", "edge2")115 val arb = arbitrary(edges.toList()) { "abcd" }116 arb.edgecases() shouldContainExactlyInAnyOrder edges117 }118 test("should assign edgecases and shrinker") {119 val shrinker = IntShrinker(1..5)120 val edges = setOf(1, 2)121 val arb = arbitrary(edges.toList(), shrinker) { 5 }122 arb.edgecases() shouldContainExactlyInAnyOrder edges123 arb.sample(RandomSource.seeded(1234L)).shrinks.children.value.map { it.value() } shouldBe shrinker.shrink(5)124 }125 test("should use shrinker when provided") {126 val shrinker = IntShrinker(1..5)127 val arb = arbitrary(shrinker) { 5 }128 arb.classifier.shouldBeNull()129 val shrinks = arb.sample(RandomSource.seeded(1234L)).shrinks130 shrinks.children.value.map { it.value() } shouldContainExactly shrinker.shrink(5)131 }132 test("should use classifier when provided") {133 val classifier = IntClassifier(1..5)134 val arb = arbitrary(classifier) { 5 }135 arb.classifier shouldBeSameInstanceAs classifier136 }137 test("should use classifier and shrinker when provided") {138 val shrinker = IntShrinker(1..5)139 val classifier = IntClassifier(1..5)140 val arb = arbitrary(shrinker, classifier) { 5 }141 arb.classifier shouldBeSameInstanceAs classifier142 val shrinks = arb.sample(RandomSource.seeded(1234L)).shrinks143 shrinks.children.value.map { it.value() } shouldContainExactly shrinker.shrink(5)144 }145 test("should use edgecase function when provided") {146 val arb = arbitrary({ 5 }) { 10 }147 arb.edgecases() shouldContainExactlyInAnyOrder setOf(5)148 }149 test("should use edgecase function and shrinker when provided") {150 val shrinker = IntShrinker(1..5)151 val arb = arbitrary({ 5 }, shrinker) { 10 }152 arb.edgecases() shouldContainExactlyInAnyOrder setOf(5)153 val shrinks = arb.sample(RandomSource.seeded(1234L)).shrinks154 shrinks.children.value.map { it.value() } shouldContainExactly shrinker.shrink(10)155 }156 test("should support .bind() syntax") {157 val arb = Arb.constant(5)158 val shrinker = IntShrinker(1..5)159 val classifier = IntClassifier(1..5)160 arbitrary { arb.bind() }.single() shouldBe 5161 arbitrary(shrinker) { arb.bind() }.single() shouldBe 5162 arbitrary(classifier) { arb.bind() }.single() shouldBe 5163 arbitrary(shrinker, classifier) { arb.bind() }.single() shouldBe 5164 arbitrary(listOf(5)) { arb.bind() }.single() shouldBe 5165 arbitrary({ 5 }) { arb.bind() }.single() shouldBe 5166 arbitrary({ 5 }, shrinker) { arb.bind() }.single() shouldBe 5167 }168 }169 context("suspend arbitrary builder with unrestricted continuation") {170 suspend fun combineAsString(vararg values: Any?): String = values.joinToString(" ")171 test("should build arb on the parent coroutine context") {172 val arb = withContext(Foo("hello")) {173 generateArbitrary {174 val hello = coroutineContext[Foo]?.value175 val world = arbitrary { "world" }.bind()176 val first = Arb.int(1..10).bind()177 val second = Arb.int(11..20).bind()178 combineAsString(hello, world, first, second)179 }180 }181 arb.generate(RandomSource.seeded(1234L)).take(4).toList().map { it.value } shouldContainExactly listOf(182 "hello world 2 20",183 "hello world 6 12",184 "hello world 7 19",185 "hello world 9 13"186 )187 }188 test("should bind edgecases") {189 val arb: Arb<String> = generateArbitrary {190 val first = Arb.string(5, Codepoint.alphanumeric()).withEdgecases("edge1", "edge2").bind()191 val second = Arb.int(1..9).withEdgecases(5).bind()192 val third = Arb.int(101..109).withEdgecases(100 + second, 109).bind()193 combineAsString(first, second, third)194 }195 arb.edgecases() shouldContainExactlyInAnyOrder setOf(196 "edge1 5 105",197 "edge2 5 105",198 "edge1 5 109",199 "edge2 5 109",200 )201 }202 test("should preserve edgecases of dependent arbs, even when intermideary arb(s) have no edgecases") {203 val arb: Arb<String> = generateArbitrary {204 val first = Arb.string(5, Codepoint.alphanumeric()).withEdgecases("edge1", "edge2").bind()205 val second = Arb.int(1..4).withEdgecases(emptyList()).bind()206 val third = Arb.int(101..109).withEdgecases(100 + second).bind()207 combineAsString(first, second, third)208 }209 arb.edgecases() shouldContainExactlyInAnyOrder setOf(210 "edge1 1 101",211 "edge1 2 102",212 "edge1 3 103",213 "edge1 4 104",214 "edge2 1 101",215 "edge2 2 102",216 "edge2 3 103",217 "edge2 4 104"218 )219 }220 test("should propagate exception") {221 val throwingArb = generateArbitrary {222 val number = Arb.int(1..4).withEdgecases(emptyList()).bind()223 // try to throw something inside the arb224 number shouldBeGreaterThan 5225 }226 val assertionError = shouldThrow<AssertionError> { execute(RandomSource.seeded(1234L), throwingArb) }227 assertionError.message shouldBe "4 should be > 5"228 }229 test("should assign edgecases") {230 val edges = setOf("edge1", "edge2")231 val arb = generateArbitrary(edges.toList()) { "abcd" }232 arb.edgecases() shouldContainExactlyInAnyOrder edges233 }234 test("should assign edgecases and shrinker") {235 val shrinker = IntShrinker(1..5)236 val edges = setOf(1, 2)237 val arb = generateArbitrary(edges.toList(), shrinker) { 5 }238 arb.edgecases() shouldContainExactlyInAnyOrder edges239 arb.sample(RandomSource.seeded(1234L)).shrinks.children.value.map { it.value() } shouldBe shrinker.shrink(5)240 }241 test("should use shrinker when provided") {242 val shrinker = IntShrinker(1..5)243 val arb = generateArbitrary(shrinker) { 5 }244 arb.classifier.shouldBeNull()245 val shrinks = arb.sample(RandomSource.seeded(1234L)).shrinks246 shrinks.children.value.map { it.value() } shouldContainExactly shrinker.shrink(5)247 }248 test("should use classifier when provided") {249 val classifier = IntClassifier(1..5)250 val arb = generateArbitrary(classifier) { 5 }251 arb.classifier shouldBeSameInstanceAs classifier252 }253 test("should use classifier and shrinker when provided") {254 val shrinker = IntShrinker(1..5)255 val classifier = IntClassifier(1..5)256 val arb = generateArbitrary(shrinker, classifier) { 5 }257 arb.classifier shouldBeSameInstanceAs classifier258 val shrinks = arb.sample(RandomSource.seeded(1234L)).shrinks259 shrinks.children.value.map { it.value() } shouldContainExactly shrinker.shrink(5)260 }261 test("should use edgecase function when provided") {262 val arb = generateArbitrary({ 5 }) { 10 }263 arb.edgecases() shouldContainExactlyInAnyOrder setOf(5)264 }265 test("should use edgecase function and shrinker when provided") {266 val shrinker = IntShrinker(1..5)267 val arb = generateArbitrary({ 5 }, shrinker) { 10 }268 arb.edgecases() shouldContainExactlyInAnyOrder setOf(5)269 val shrinks = arb.sample(RandomSource.seeded(1234L)).shrinks270 shrinks.children.value.map { it.value() } shouldContainExactly shrinker.shrink(10)271 }272 test("should support .bind() syntax") {273 val arb = Arb.constant(5)274 val shrinker = IntShrinker(1..5)275 val classifier = IntClassifier(1..5)276 generateArbitrary { arb.bind() }.single() shouldBe 5277 generateArbitrary(shrinker) { arb.bind() }.single() shouldBe 5278 generateArbitrary(classifier) { arb.bind() }.single() shouldBe 5279 generateArbitrary(shrinker, classifier) { arb.bind() }.single() shouldBe 5280 generateArbitrary(listOf(5)) { arb.bind() }.single() shouldBe 5281 generateArbitrary({ 5 }) { arb.bind() }.single() shouldBe 5282 generateArbitrary({ 5 }, shrinker) { arb.bind() }.single() shouldBe 5283 }284 }285 }286 private data class Foo(val value: String) : CoroutineContext.Element {287 companion object : CoroutineContext.Key<Foo>288 override val key: CoroutineContext.Key<*> = Foo289 }290 private suspend fun execute(rs: RandomSource, arb: Arb<*>): Unit {291 arb.generate(rs).take(1000).last()292 }293}...
Gen.kt
Source:Gen.kt
1package io.kotest.property2import io.kotest.property.arbitrary.of3/**4 * A [Gen] is responsible for providing values to be used in property testing. You can think of it as like5 * an input stream for values. Each arg will provide data for a specific type <A>.6 *7 * Gens can be created in two ways: with arbitrary (random) values from instances of [Arb] and8 * exhaustive values over a closed space from instances of [Exhaustive].9 *10 * Arbs generate random values across a given space. The values may be repeated, and some11 * values may never be generated at all. For example generating 1000 random integers between 0 and Int.MAX12 * will clearly not return all possible values, and some values may happen to be generated more than once.13 *14 * Exhaustives generate all values from a given space. This is useful when you want to ensure every15 * value in that space is used. For instance for enum values, it is usually more helpful to ensure each16 * enum is used, rather than picking randomly from the enums values.17 *18 * Both types of gens can be mixed and matched in property tests. For example,19 * you could test a function with 100 random positive integers (arbitrary) alongside every20 * even number from 0 to 200 (exhaustive).21 */22sealed class Gen<out A> {23 /**24 * Returns values from this generator as a lazily generated sequence.25 *26 * If this gen is an [Arb], then each value will either be a sample or an edge case. The bias27 * towards edge cases or samples is given by the value of [EdgeConfig.edgecasesGenerationProbability]28 * inside the [edgeConfig] parameter.29 *30 * If this gen is an [Exhaustive], then the returned values will iterate in turn, repeating31 * once exhausted as required.32 *33 */34 fun generate(35 rs: RandomSource,36 edgeConfig: EdgeConfig = EdgeConfig.default()37 ): Sequence<Sample<A>> =38 when (this) {39 is Arb -> {40 val samples = this.samples(rs).iterator()41 generateSequence {42 val isEdgeCase = rs.random.nextDouble(0.0, 1.0) < edgeConfig.edgecasesGenerationProbability43 if (isEdgeCase) {44 this.edgecase(rs)?.asSample() ?: samples.next()45 } else samples.next()46 }47 }48 is Exhaustive -> {49 check(this.values.isNotEmpty()) { "Exhaustive.values shouldn't be a empty list." }50 generateSequence { this.values.map { Sample(it) } }.flatten()51 }52 }53 /**54 * Returns an optional [Classifier] to label values.55 */56 open val classifier: Classifier<out A>? = null57 /**58 * The minimum iteration count required for this [Gen] to be invoked.59 * Requesting a property test with fewer than this will result in an exception.60 */61 fun minIterations(): Int = when (this) {62 is Exhaustive -> this.values.size63 else -> 164 }65}66/**67 * An [Arb] (short for arbitrary) is a generator of values in two categories: edge cases and samples.68 *69 * Edge cases are values that are a common source of bugs. For example, a function using ints is70 * more likely to fail for common edge cases like zero, minus 1, positive 1, [Int.MAX_VALUE] and [Int.MIN_VALUE]71 * rather than random values like 965489. Therefore, it is useful that we try to include such values72 * rather than relying entirely on random values which are unlikely to generate these.73 *74 * Not all arbitraries will utilize edge cases. For example, if you define an integer generator75 * using a subset of the number space - say from 100 to 200 - then no edge cases would be provided.76 *77 * Samples are chosen randomly from the sample space and are used to give a greater breadth to78 * the test cases. For example, in the case of a function using integers, these random values79 * could be from across the entire integer number line, or could be limited to a subset of ints80 * such as natural numbers or even numbers.81 *82 * In addition to values, arbs can optionally implement a [classify] function which classifies83 * the generated values with labels. These labels can then be used to display information on the84 * types of values generated.85 *86 * In order to use an [Arb] outside a property test, one must invoke the [take] method, passing in87 * the number of iterations required and optionally a [ShrinkingMode].88 */89abstract class Arb<out A> : Gen<A>() {90 /**91 * Returns a single edge case for this arbitrary. If this arb supports multiple edge cases,92 * then one should be chosen randomly each time this function is invoked.93 *94 * Can return null if this arb does not provide edge cases.95 */96 abstract fun edgecase(rs: RandomSource): A?97 /**98 * Returns a single random [Sample] from this [Arb] using the supplied random source.99 */100 abstract fun sample(rs: RandomSource): Sample<A>101 /**102 * Returns a sequence from values generated from this arb.103 * Edgecases will be ignored.104 */105 fun samples(rs: RandomSource = RandomSource.default()): Sequence<Sample<A>> {106 return generateSequence { sample(rs) }107 }108 companion object109}110/**111 * An exhaustive is a type of [Gen] which generates an exhaustive set of values from a defined range.112 *113 * An example of a exhaustive is the sequence of integers from 0 to 100.114 * Another example is all strings of two characters.115 *116 * A progression is useful when you want to generate an exhaustive set of values from a given117 * sample space, rather than random values from that space. For example, if you were testing a118 * function that used an enum, you might prefer to guarantee that every enum value is used, rather119 * than selecting randomly from amongst the enum values (with possible duplicates and gaps).120 *121 * Exhaustives do not shrink their values. There is no need to find a smaller failing case, because122 * the smaller values will themselves naturally be included in the tested values.123 *124 * An exhaustive is less suitable when you have a large sample space you need to select values from.125 */126abstract class Exhaustive<out A> : Gen<A>() {127 /**128 * Returns the values of this [Exhaustive].129 */130 abstract val values: List<A>131 /**132 * Converts this into an [Arb] where the generated values of the returned arb133 * are choosen randomly from the values provided by this exhausive.134 */135 fun toArb(): Arb<A> = Arb.of(values)136 companion object137}138fun interface Classifier<A> {139 fun classify(value: A): String?140}141/**142 * Contains a single generated value from a [Gen] and an [RTree] of lazily evaluated shrinks.143 */144data class Sample<out A>(val value: A, val shrinks: RTree<A> = RTree({ value }))145fun <A> A.asSample(): Sample<A> = Sample(this)146/**147 * Returns a [Sample] with shrinks by using the supplied [Shrinker] against the input value [a].148 */149fun <A> sampleOf(a: A, shrinker: Shrinker<A>) = Sample(a, shrinker.rtree(a))150data class EdgeConfig(151 val edgecasesGenerationProbability: Double = PropertyTesting.defaultEdgecasesGenerationProbability152) {153 companion object;154 init {155 check(edgecasesGenerationProbability in 0.0..1.0) {156 "provided edgecasesProbability $edgecasesGenerationProbability is not between 0.0 and 1.0"157 }158 }159}...
ChooseTest.kt
Source:ChooseTest.kt
1package com.sksamuel.kotest.property.arbitrary2import io.kotest.assertions.throwables.shouldNotThrow3import io.kotest.assertions.throwables.shouldThrow4import io.kotest.core.spec.style.FunSpec5import io.kotest.data.forAll6import io.kotest.data.row7import io.kotest.matchers.collections.shouldContainExactly8import io.kotest.matchers.doubles.plusOrMinus9import io.kotest.matchers.shouldBe10import io.kotest.property.Arb11import io.kotest.property.EdgeConfig12import io.kotest.property.RandomSource13import io.kotest.property.arbitrary.choose14import io.kotest.property.arbitrary.constant15import io.kotest.property.arbitrary.single16import io.kotest.property.arbitrary.withEdgecases17import io.kotest.property.random18class ChooseTest : FunSpec({19 test("Arb.choose should honour seed") {20 val seedListA =21 Arb.choose(1 to 'A', 3 to 'B', 4 to 'C', 5 to 'D').samples(684658365846L.random()).take(500).toList()22 .map { it.value }23 val seedListB =24 Arb.choose(1 to 'A', 3 to 'B', 4 to 'C', 5 to 'D').samples(684658365846L.random()).take(500).toList()25 .map { it.value }26 seedListA shouldBe seedListB27 }28 test("Arb.choose for values should generate expected values in correct ratios according to weights") {29 forAll(30 row(listOf(1 to 'A', 1 to 'B'), mapOf('A' to 0.5, 'B' to 0.5)),31 row(listOf(1 to 'A', 3 to 'B', 1 to 'C'), mapOf('A' to 0.2, 'B' to 0.6, 'C' to 0.2)),32 row(listOf(1 to 'A', 3 to 'C', 1 to 'C'), mapOf('A' to 0.2, 'C' to 0.8)),33 row(listOf(1 to 'A', 3 to 'B', 1 to 'C', 4 to 'D'), mapOf('A' to 0.11, 'B' to 0.33, 'C' to 0.11, 'D' to 0.44))34 ) { weightPairs, expectedRatiosMap ->35 val genCount = 10000036 val chooseGen = Arb.choose(weightPairs[0], weightPairs[1], *weightPairs.drop(2).toTypedArray())37 val actualCountsMap = (1..genCount).map { chooseGen.single() }.groupBy { it }.map { (k, v) -> k to v.count() }38 val actualRatiosMap = actualCountsMap.associate { (k, v) -> k to (v.toDouble() / genCount) }39 actualRatiosMap.keys shouldBe expectedRatiosMap.keys40 actualRatiosMap.forEach { (k, actualRatio) ->41 actualRatio shouldBe (expectedRatiosMap[k] as Double plusOrMinus 0.02)42 }43 }44 }45 test("Arb.choose should not accept negative weights") {46 shouldThrow<IllegalArgumentException> { Arb.choose(-1 to 'A', 1 to 'B') }47 }48 test("Arb.choose should not accept all zero weights") {49 shouldThrow<IllegalArgumentException> { Arb.choose(0 to 'A', 0 to 'B') }50 }51 test("Arb.choose should accept weights if at least one is non-zero") {52 shouldNotThrow<Exception> { Arb.choose(0 to 'A', 0 to 'B', 1 to 'C') }53 }54 test("Arb.choose(arbs) should generate expected values in correct ratios according to weights") {55 val arbA = Arb.constant('A')56 val arbB = Arb.constant('B')57 val arbC = Arb.constant('C')58 val arbD = Arb.constant('D')59 forAll(60 row(listOf(1 to arbA, 1 to arbB), mapOf('A' to 0.5, 'B' to 0.5)),61 row(listOf(1 to arbA, 3 to arbB, 1 to arbC), mapOf('A' to 0.2, 'B' to 0.6, 'C' to 0.2)),62 row(listOf(1 to arbA, 3 to arbC, 1 to arbC), mapOf('A' to 0.2, 'C' to 0.8)),63 row(64 listOf(1 to arbA, 3 to arbB, 1 to arbC, 4 to arbD),65 mapOf('A' to 0.11, 'B' to 0.33, 'C' to 0.11, 'D' to 0.44)66 )67 ) { weightPairs, expectedRatiosMap ->68 val genCount = 10000069 val chooseGen = Arb.choose(weightPairs[0], weightPairs[1], *weightPairs.drop(2).toTypedArray())70 val actualCountsMap = (1..genCount).map { chooseGen.single() }.groupBy { it }.map { (k, v) -> k to v.count() }71 val actualRatiosMap = actualCountsMap.associate { (k, v) -> k to (v.toDouble() / genCount) }72 actualRatiosMap.keys shouldBe expectedRatiosMap.keys73 actualRatiosMap.forEach { (k, actualRatio) ->74 actualRatio shouldBe (expectedRatiosMap[k] as Double plusOrMinus 0.02)75 }76 }77 }78 test("Arb.choose(arbs) should not accept all zero weights") {79 shouldThrow<IllegalArgumentException> { Arb.choose(0 to Arb.constant('A'), 0 to Arb.constant('B')) }80 }81 test("Arb.choose(arbs) should not accept negative weights") {82 shouldThrow<IllegalArgumentException> { Arb.choose(-1 to Arb.constant('A'), 1 to Arb.constant('B')) }83 }84 test("Arb.choose(arbs) should accept weights if at least one is non-zero") {85 shouldNotThrow<Exception> { Arb.choose(0 to Arb.constant('A'), 0 to Arb.constant('B'), 1 to Arb.constant('C')) }86 }87 test("Arb.choose(arbs) should collate edge cases") {88 val arb = Arb.choose(89 1 to Arb.constant('A').withEdgecases('a'),90 3 to Arb.constant('B').withEdgecases('b'),91 4 to Arb.constant('C').withEdgecases('c'),92 5 to Arb.constant('D').withEdgecases('d')93 )94 val edgeCases = arb95 .generate(RandomSource.seeded(1234L), EdgeConfig(edgecasesGenerationProbability = 1.0))96 .take(10)97 .map { it.value }98 .toList()99 edgeCases shouldContainExactly listOf(100 'c',101 'c',102 'd',103 'a',104 'b',105 'a',106 'd',107 'd',108 'a',109 'b'110 )111 }112})...
maps.kt
Source:maps.kt
1package io.kotest.property.arbitrary2import io.kotest.property.Arb3import io.kotest.property.Shrinker4/**5 * Returns an [Arb] where each generated value is a map, with the entries of the map6 * drawn from the given pair generating arb. The size of each7 * generated map is a random value between the specified min and max bounds.8 *9 * There are no edge cases.10 *11 * This arbitrary uses a [Shrinker] which will reduce the size of a failing map by12 * removing elements from the failed case until it is empty.13 *14 * @see MapShrinker15 *16 * @param arb the arbitrary to populate the map entries17 * @param minSize the desired minimum size of the generated map18 * @param maxSize the desired maximum size of the generated map19 * @param slippage when generating keys, we may have repeats if the underlying gen is random.20 * The slippage factor determines how many times we continue after retrieving a duplicate key.21 * The total acceptable number of misses is the slippage factor multiplied by the target set size.22 * If this value is not specified, then the default slippage value of 10 will be used.23 */24fun <K, V> Arb.Companion.map(25 arb: Arb<Pair<K, V>>,26 minSize: Int = 1,27 maxSize: Int = 100,28 slippage: Int = 1029): Arb<Map<K, V>> = arbitrary(MapShrinker(minSize)) { random ->30 val targetSize = random.random.nextInt(minSize, maxSize)31 val maxMisses = targetSize * slippage32 val map = mutableMapOf<K, V>()33 var iterations = 034 while (iterations < maxMisses && map.size < targetSize) {35 val initialSize = map.size36 val (key, value) = arb.single(random)37 map[key] = value38 if (map.size == initialSize) iterations++39 }40 require(map.size >= minSize) {41 "the minimum size requirement of $minSize could not be satisfied after $iterations consecutive samples"42 }43 map44}45/**46 * Returns an [Arb] where each generated value is a map, with the entries of the map47 * drawn by combining values from the key gen and value gen. The size of each48 * generated map is a random value between the specified min and max bounds.49 *50 * There are no edge cases.51 *52 * This arbitrary uses a [Shrinker] which will reduce the size of a failing map by53 * removing elements until they map is empty.54 *55 * @see MapShrinker56 *57 * @param keyArb the arbitrary to populate the keys58 * @param valueArb the arbitrary to populate the values59 * @param minSize the desired minimum size of the generated map60 * @param maxSize the desired maximum size of the generated map61 * @param slippage when generating keys, we may have repeats if the underlying gen is random.62 * The slippage factor determines how many times we continue after retrieving a duplicate key.63 * The total acceptable number of misses is the slippage factor multiplied by the target set size.64 * If this value is not specified, then the default slippage value of 10 will be used.65 */66fun <K, V> Arb.Companion.map(67 keyArb: Arb<K>,68 valueArb: Arb<V>,69 minSize: Int = 1,70 maxSize: Int = 100,71 slippage: Int = 1072): Arb<Map<K, V>> {73 require(minSize >= 0) { "minSize must be positive" }74 require(maxSize >= 0) { "maxSize must be positive" }75 return arbitrary(MapShrinker(minSize)) { random ->76 val targetSize = random.random.nextInt(minSize, maxSize)77 val maxMisses = targetSize * slippage78 val map = mutableMapOf<K, V>()79 var iterations = 080 while (iterations < maxMisses && map.size < targetSize) {81 val initialSize = map.size82 map[keyArb.single(random)] = valueArb.single(random)83 if (map.size == initialSize) iterations++84 }85 require(map.size >= minSize) {86 "the minimum size requirement of $minSize could not be satisfied after $iterations consecutive samples"87 }88 map89 }90}91class MapShrinker<K, V>(private val minSize: Int) : Shrinker<Map<K, V>> {92 override fun shrink(value: Map<K, V>): List<Map<K, V>> {93 val shrinks = when (value.size) {94 0 -> emptyList()95 1 -> listOf(emptyMap())96 else -> listOf(97 value.toList().take(value.size / 2).toMap(),98 value.toList().drop(1).toMap()99 )100 }101 return shrinks.filter { it.size >= minSize }102 }103}104/**105 * Returns an [Arb] that produces Pairs of K,V using the supplied arbs for K and V.106 * Edgecases will be derived from [k] and [v].107 */108fun <K, V> Arb.Companion.pair(k: Arb<K>, v: Arb<V>): Arb<Pair<K, V>> {109 val arbPairWithoutKeyEdges: Arb<Pair<K, V>> = Arb.bind(k.removeEdgecases(), v, ::Pair)110 val arbPairWithoutValueEdges: Arb<Pair<K, V>> = Arb.bind(k, v.removeEdgecases(), ::Pair)111 val arbPair: Arb<Pair<K, V>> = Arb.bind(k, v, ::Pair)112 return Arb.choice(arbPair, arbPairWithoutKeyEdges, arbPairWithoutValueEdges)113}...
MapsTest.kt
Source:MapsTest.kt
1package com.sksamuel.kotest.property.arbitrary2import io.kotest.assertions.throwables.shouldThrowWithMessage3import io.kotest.core.spec.style.FunSpec4import io.kotest.inspectors.forAll5import io.kotest.matchers.collections.shouldContainExactly6import io.kotest.matchers.ints.shouldBeInRange7import io.kotest.property.Arb8import io.kotest.property.RandomSource9import io.kotest.property.arbitrary.Codepoint10import io.kotest.property.arbitrary.alphanumeric11import io.kotest.property.arbitrary.edgecases12import io.kotest.property.arbitrary.int13import io.kotest.property.arbitrary.map14import io.kotest.property.arbitrary.pair15import io.kotest.property.arbitrary.single16import io.kotest.property.arbitrary.string17import io.kotest.property.arbitrary.take18import io.kotest.property.arbitrary.withEdgecases19class MapsTest : FunSpec({20 context("Arb.pair") {21 test("should generate a pair of values from given arb key / value") {22 val arbKey = Arb.int(1..10)23 val arbValue = Arb.string(1..10, Codepoint.alphanumeric())24 val arbPair: Arb<Pair<Int, String>> = Arb.pair(arbKey, arbValue)25 arbPair.take(5, RandomSource.seeded(1234L)).toList() shouldContainExactly listOf(26 Pair(6, "tI"),27 Pair(6, "i7"),28 Pair(9, "wmWkyqH"),29 Pair(7, "J"),30 Pair(7, "V")31 )32 }33 test("should generate edgecases from key and value arbs") {34 val arbKey = Arb.int(1..10).withEdgecases(5)35 val arbValue = Arb.string(1..10, Codepoint.alphanumeric()).withEdgecases("edge")36 val arbPair: Arb<Pair<Int, String>> = Arb.pair(arbKey, arbValue)37 arbPair.edgecases(rs = RandomSource.seeded(1234L)).take(5).toList() shouldContainExactly listOf(38 Pair(5, "edge"),39 Pair(5, "awmWkyqH8"),40 Pair(5, "V"),41 Pair(7, "edge"),42 Pair(5, "LOFBhzunuV")43 )44 }45 }46 context("Arb.map with individual key arb and value arb") {47 test("should generate map of a specified size") {48 val arbMap = Arb.map(Arb.int(1..10), Arb.string(1..10, Codepoint.alphanumeric()), minSize = 5, maxSize = 10)49 val maps = arbMap.take(1000, RandomSource.seeded(12345L)).toList()50 maps.forAll { it.size shouldBeInRange 5..10 }51 }52 test("should produce shrinks that adhere to minimum size") {53 val arbMap = Arb.map(Arb.int(1..10), Arb.string(1..10, Codepoint.alphanumeric()), minSize = 5, maxSize = 10)54 val maps = arbMap.samples(RandomSource.seeded(12345L)).take(100).toList()55 val shrinks = maps.flatMap { it.shrinks.children.value }56 shrinks.forAll { it.value().size shouldBeInRange 5..10 }57 }58 test("should throw when the cardinality of the key arbitrary does not satisfy the required minimum size") {59 val arbKey = Arb.int(1..3)60 val arbMap = Arb.map(arbKey, Arb.string(1..10), minSize = 5, maxSize = 10)61 shouldThrowWithMessage<IllegalArgumentException>(62 "the minimum size requirement of 5 could not be satisfied after 90 consecutive samples"63 ) {64 arbMap.single(RandomSource.seeded(1234L))65 }66 }67 }68 context("Arb.map with arb pair") {69 test("should generate map of a specified size") {70 val arbPair = Arb.pair(Arb.int(1..10), Arb.string(1..10, Codepoint.alphanumeric()))71 val arbMap = Arb.map(arbPair, minSize = 5, maxSize = 10)72 val maps = arbMap.take(100, RandomSource.seeded(12345L)).toList()73 maps.forAll { it.size shouldBeInRange 5..10 }74 }75 test("should produce shrinks that adhere to minimum size") {76 val arbPair = Arb.pair(Arb.int(1..10), Arb.string(1..10, Codepoint.alphanumeric()))77 val arbMap = Arb.map(arbPair, minSize = 5, maxSize = 10)78 val maps = arbMap.samples(RandomSource.seeded(12345L)).take(100).toList()79 val shrinks = maps.flatMap { it.shrinks.children.value }80 shrinks.forAll {81 it.value().size shouldBeInRange 5..1082 }83 }84 test("should throw when the cardinality of the key arbitrary does not satisfy the required minimum size") {85 val arbPair = Arb.pair(Arb.int(1..3), Arb.string(1..10, Codepoint.alphanumeric()))86 val arbMap = Arb.map(arbPair, minSize = 5, maxSize = 10)87 shouldThrowWithMessage<IllegalArgumentException>(88 "the minimum size requirement of 5 could not be satisfied after 90 consecutive samples"89 ) {90 arbMap.single(RandomSource.seeded(1234L))91 }92 }93 }94})...
Arb.single
Using AI Code Generation
1val arb = Arb.single(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)2val config = PropertyTestingConfig(iterations = 100, threads = 1)3forAll(arb, config) { n ->4println(n)5}6forAll { n: Int ->7println(n)8}9val config = PropertyTestingConfig(iterations = 100, threads = 1)10forAll(config) { n: Int ->11println(n)12}13val config = PropertyTestingConfig(iterations = 100, threads = 1)14forAll(Arb.int(), config) { n ->15println(n)16}17val config = PropertyTestingConfig(iterations = 100, threads = 1)18forAll(Gen.int(), config) { n ->19println(n)20}21val config = PropertyTestingConfig(iterations = 100, threads = 1)22forAll(Gen.int().edgecases(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), config) { n ->23println(n)24}25val config = PropertyTestingConfig(iterations = 100, threads = 1)26forAll(Gen.int().edgecases(1, 2, 3, 4, 5,
Arb.single
Using AI Code Generation
1val arb = Arb.single(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)2forAll(arb) {3 it.shouldBeIn(1..10)4}5val arb = Arb.create {6 when (it.random.nextInt(1, 11)) {7 }8}9forAll(arb) {10 it.shouldBeIn(1..10)11}12val arb = Arb.create {13 when (it.random.nextInt(1, 11)) {14 }15}16forAll(arb) {17 it.shouldBeIn(1..10)18}19val arb = Arb.create {20 when (it.random.nextInt(1, 11)) {21 }22}23forAll(arb) {24 it.shouldBeIn(1..10)25}26val arb = Arb.create {27 when (it.random.nextInt(1, 11)) {
Arb.single
Using AI Code Generation
1val arb = Arb.single ( 1 , 2 , 3 , 4 , 5 )2val arb = Arb.single ( "a" , "b" , "c" )3val arb = Arb.single ( 1.0 , 2.0 , 3.0 , 4.0 , 5.0 )4val arb = Arb.single ( "a" , "b" , "c" )5val arb = Arb.single ( 1.0 , 2.0 , 3.0 , 4.0 , 5.0 )6val arb = Arb.single ( "a" , "b" , "c" )7val arb = Arb.single ( 1.0 , 2.0 , 3.0 , 4.0 , 5.0 )8val arb = Arb.single ( "a" , "b" , "c" )9val arb = Arb.single ( 1.0 , 2.0 , 3.0 , 4.0 , 5.0 )10val arb = Arb.single ( "a" , "b" , "c" )11val arb = Arb.single ( 1.0 , 2.0 , 3.0 , 4.0 , 5.0 )12val arb = Arb.single ( "a" , "b" , "c" )
Arb.single
Using AI Code Generation
1val arb = Arb.single(1, 2, 3)2val prop = property(arb) { a -> a in 1..3 }3prop.check()4val arb = Arb.single(1, 2, 3)5val prop = property(arb) { a -> a in 1..3 }6prop.check()7val arb = Arb.single(1, 2, 3)8val prop = property(arb) { a -> a in 1..3 }9prop.check()10val arb = Arb.single(1, 2, 3)11val prop = property(arb) { a -> a in 1..3 }12prop.check()13val arb = Arb.single(1, 2, 3)14val prop = property(arb) { a -> a in 1..3 }15prop.check()16val arb = Arb.single(1, 2, 3)17val prop = property(arb) { a -> a in 1..3 }18prop.check()19val arb = Arb.single(1, 2, 3)20val prop = property(arb) { a -> a in 1..3 }21prop.check()22val arb = Arb.single(1, 2, 3)23val prop = property(arb) { a -> a in 1..3 }24prop.check()25val arb = Arb.single(1, 2, 3)26val prop = property(arb) { a -> a in 1..3 }27prop.check()28val arb = Arb.single(1, 2, 3)29val prop = property(arb)
Arb.single
Using AI Code Generation
1fun testProperty() {2 Arb.single(1..5).assertAll { it.shouldBeLessThan(6) }3}4fun testProperty() {5 Arb.single(1..5).assertAll { it.shouldBeLessThan(6) }6}7fun testProperty() {8 Arb.single(1..5).assertAll { it.shouldBeLessThan(6) }9}10fun testProperty() {11 Arb.single(1..5).assertAll { it.shouldBeLessThan(6) }12}13fun testProperty() {14 Arb.single(1..5).assertAll { it.shouldBeLessThan(6) }15}16fun testProperty() {17 Arb.single(1..5).assertAll { it.shouldBeLessThan(6) }18}19fun testProperty() {20 Arb.single(1..5).assertAll { it.shouldBeLessThan(6) }21}22fun testProperty() {23 Arb.single(1..5).assertAll { it.shouldBeLessThan(6) }24}25fun testProperty() {26 Arb.single(1..5).assertAll { it.shouldBeLessThan(6) }27}28fun testProperty() {29 Arb.single(1..5).assertAll { it.shouldBeLessThan(6) }30}31fun testProperty() {32 Arb.single(1..5).assertAll
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!