Best Kotest code snippet using io.kotest.property.arbitrary.map.edgecase
builders.kt
Source:builders.kt
...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)...
BuilderTest.kt
Source:BuilderTest.kt
...16import 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 5...
bind.kt
Source:bind.kt
...432 val arbL = genL.toArb()433 val arbM = genM.toArb()434 val arbN = genN.toArb()435 return object : Arb<T>() {436 override fun edgecase(rs: RandomSource): T? {437 return bindFn(438 arbA.edgecase(rs) ?: arbA.next(rs),439 arbB.edgecase(rs) ?: arbB.next(rs),440 arbC.edgecase(rs) ?: arbC.next(rs),441 arbD.edgecase(rs) ?: arbD.next(rs),442 arbE.edgecase(rs) ?: arbE.next(rs),443 arbF.edgecase(rs) ?: arbF.next(rs),444 arbG.edgecase(rs) ?: arbG.next(rs),445 arbH.edgecase(rs) ?: arbH.next(rs),446 arbI.edgecase(rs) ?: arbI.next(rs),447 arbJ.edgecase(rs) ?: arbJ.next(rs),448 arbK.edgecase(rs) ?: arbK.next(rs),449 arbL.edgecase(rs) ?: arbL.next(rs),450 arbM.edgecase(rs) ?: arbM.next(rs),451 arbN.edgecase(rs) ?: arbN.next(rs),452 )453 }454 override fun sample(rs: RandomSource): Sample<T> {455 val (av, ar) = arbA.sample(rs)456 val (bv, br) = arbB.sample(rs)457 val (cv, cr) = arbC.sample(rs)458 val (dv, dr) = arbD.sample(rs)459 val (ev, er) = arbE.sample(rs)460 val (fv, fr) = arbF.sample(rs)461 val (gv, gr) = arbG.sample(rs)462 val (hv, hr) = arbH.sample(rs)463 val (iv, ir) = arbI.sample(rs)464 val (jv, jr) = arbJ.sample(rs)465 val (kv, kr) = arbK.sample(rs)...
map.kt
Source:map.kt
...7 * Returns a new [Arb] which takes its elements from the receiver and maps them using the supplied function.8 */9fun <A, B> Arb<A>.map(fn: (A) -> B): Arb<B> = trampoline { sampleA ->10 object : Arb<B>() {11 override fun edgecase(rs: RandomSource): B? = fn(sampleA.value)12 override fun sample(rs: RandomSource): Sample<B> {13 val value = fn(sampleA.value)14 val shrinks = sampleA.shrinks.map(fn)15 return Sample(value, shrinks)16 }17 }18}19/**20 * Returns a new [Arb] which takes its elements from the receiver and maps them using the supplied function.21 */22fun <A, B> Arb<A>.flatMap(fn: (A) -> Arb<B>): Arb<B> = trampoline { fn(it.value) }23/**24 * Returns a new [TrampolineArb] from the receiver [Arb] which composes the operations of [next] lambda25 * using a trampoline method. This allows [next] function to be executed without exhausting call stack.26 */27internal fun <A, B> Arb<A>.trampoline(next: (Sample<A>) -> Arb<B>): Arb<B> = when (this) {28 is TrampolineArb -> this.thunk(next)29 else -> TrampolineArb(this).thunk(next)30}31/**32 * The [TrampolineArb] is a special Arb that exchanges call stack with heap.33 * In a nutshell, this arb stores command chains to be applied to the original arb inside a list.34 * This technique is an imperative reduction of Free Monads. This eliminates the need of creating intermediate35 * Trampoline Monad and tail-recursive function on those which can be expensive.36 * This minimizes the amount of code and unnecessary object allocation during sample generation in the expense of typesafety.37 *38 * This is an internal implementation. Do not use this TrampolineArb as is and please do not expose this39 * to users outside of the library. For library maintainers, please use the [Arb.trampoline] extension function.40 * The extension function will provide some type-guardrails to workaround the loss of types within this Arb.41 */42@Suppress("UNCHECKED_CAST")43internal class TrampolineArb<A> private constructor(44 private val first: Arb<A>,45 private val commands: List<(Sample<Any>) -> Arb<Any>>46) : Arb<A>() {47 constructor(first: Arb<A>) : this(first, emptyList())48 fun <A, B> thunk(fn: (Sample<A>) -> Arb<B>): TrampolineArb<B> =49 TrampolineArb(50 first,51 commands + (fn as (Sample<Any>) -> Arb<Any>)52 ) as TrampolineArb<B>53 override fun edgecase(rs: RandomSource): A? =54 commands55 .fold(first as Arb<Any>) { currentArb, next ->56 val currentEdge = currentArb.edgecase(rs) ?: currentArb.sample(rs).value57 next(Sample(currentEdge))58 }59 .edgecase(rs) as A?60 override fun sample(rs: RandomSource): Sample<A> =61 commands62 .fold(first as Arb<Any>) { currentArb, next ->63 next(currentArb.sample(rs))64 }65 .sample(rs) as Sample<A>66}...
FilterTest.kt
Source:FilterTest.kt
...25 }26 test("should filter edge cases") {27 val arb = Arb.int(1..10).withEdgecases(1, 2, 3).filter { it % 2 == 0 }28 val edgeCases = arb29 .generate(RandomSource.seeded(1234L), EdgeConfig(edgecasesGenerationProbability = 1.0))30 .take(5)31 .map { it.value }32 .toList()33 edgeCases shouldContainExactly listOf(2, 2, 2, 2, 2)34 }35 test("should be stack safe") {36 val arb = object : Arb<Int>() {37 override fun edgecase(rs: RandomSource): Int? = null38 override fun sample(rs: RandomSource): Sample<Int> = Sample(rs.random.nextInt())39 }40 shouldNotThrow<StackOverflowError> {41 arb.filter { it % 2 == 0 }.take(1000000).toList()42 }43 }44 test("should apply filter to shrinks") {45 val arbEvenInts = Arb.int(-100..100).filter { it % 2 == 0 }46 val oddNumbers = (-100..100).filter { it % 2 != 0 }47 arbEvenInts.samples().take(100).forAll { sample ->48 sample.shrinks.value() shouldNotBeIn oddNumbers49 sample.shrinks.children.value.forAll {50 it.value() shouldNotBeIn oddNumbers51 }...
identifierGenerator.kt
Source:identifierGenerator.kt
...24import kotlinx.collections.immutable.PersistentList25import kotlinx.collections.immutable.toPersistentList26internal fun Arb.Companion.semVerIdentifier(minParts: Int = 1, maxParts: Int): Arb<Pair<PersistentList<Identifier>, String>> =27 object : Arb<Pair<PersistentList<Identifier>, String>>() {28 override fun edgecase(rs: RandomSource): Pair<PersistentList<Identifier>, String>? = null29 override fun sample(rs: RandomSource): Sample<Pair<PersistentList<Identifier>, String>> {30 // I don't really know what a sensible maxSize would be, even 100 feels a bit large for a semver identifier31 // the system should be able to handle identifiers of any size (as long as the final string isn't larger32 // than what is supported by the JVM)33 val stringArb = Arb.string(minSize = 1, maxSize = 100, codepoints = Codepoint.semVerIdentifier())34 val sequenceSize = rs.random.nextInt(minParts, maxParts + 1)35 val identifiers = stringArb.samples(rs)36 .take(sequenceSize)37 .map { it.value }38 .map { Identifier.of(it) }39 .toPersistentList()40 val identifierTextSequence = identifiers.joinToString(separator = ".")41 return Sample(identifiers to identifierTextSequence)42 }...
edgecases.kt
Source:edgecases.kt
...7 * Randomly chooses an [Arb] and then generates an edge case from that [Arb].8 * If the chosen arb has no edge cases, then another arb will be chosen.9 * If all [Arb]s have no edge cases, then returns null.10 */11tailrec fun <A> List<Arb<A>>.edgecase(rs: RandomSource): A? {12 if (this.isEmpty()) return null13 val shuffled = this.shuffled(rs.random)14 return when (val edge = shuffled.first().edgecase(rs)) {15 null -> this.drop(1).edgecase(rs)16 else -> edge17 }18}19/**20 * Collects the edge cases from this arb.21 * Will stop after the given number of iterations.22 * This function is mainly used for testing.23 */24fun <A> Arb<A>.edgecases(iterations: Int = 100, rs: RandomSource = RandomSource.default()): Set<A> =25 generate(rs, EdgeConfig(edgecasesGenerationProbability = 1.0))26 .take(iterations)27 .map { it.value }28 .toSet()29/**30 * Returns a new [Arb] with the supplied edge cases replacing any existing edge cases.31 */32fun <A> Arb<A>.withEdgecases(edgecases: List<A>): Arb<A> = arbitrary(edgecases) { this@withEdgecases.next(it) }33/**34 * Returns a new [Arb] with the supplied edge cases replacing any existing edge cases.35 */36fun <A> Arb<A>.withEdgecases(vararg edgecases: A): Arb<A> = this.withEdgecases(edgecases.toList())37fun <A> Arb<A>.removeEdgecases(): Arb<A> = this.withEdgecases(emptyList())38/**39 * Returns a new [Arb] with the edge cases from this arb transformed by the given function [f].40 */41fun <A> Arb<A>.modifyEdgecases(f: (A) -> A?): Arb<A> = object : Arb<A>() {42 override fun edgecase(rs: RandomSource): A? = this@modifyEdgecases.edgecase(rs)?.let(f)43 override fun sample(rs: RandomSource): Sample<A> = this@modifyEdgecases.sample(rs)44}...
arbs.kt
Source:arbs.kt
...24 **/25fun <A> Arb.Companion.lazy(f: () -> Arb<A>): Arb<A> {26 val arb by kotlin.lazy { f() }27 return object : Arb<A>() {28 override fun edgecase(rs: RandomSource): A? = arb.edgecase(rs)29 override fun sample(rs: RandomSource): Sample<A> = arb.sample(rs)30 }31}...
edgecase
Using AI Code Generation
1fun <T> Arb.Companion.edgecase(values: List<T>): Arb<T> =2if (values.isEmpty()) Arb.empty()3else Arb.from(values)4fun <T> Arb.Companion.edgecase(vararg values: T): Arb<T> =5if (values.isEmpty()) Arb.empty()6else Arb.from(values.toList())7fun <T> Arb.Companion.edgecase(values: Set<T>): Arb<T> =8if (values.isEmpty()) Arb.empty()9else Arb.from(values)10fun <K, V> Arb.Companion.edgecaseMap(keys: List<K>, values: List<V>): Arb<Map<K, V>> =11if (keys.isEmpty()) Arb.empty()12else Arb.from(EdgeCaseMapGenerator(keys, values))13fun <K, V> Arb.Companion.edgecaseMap(keys: Set<K>, values: List<V>): Arb<Map<K, V>> =14if (keys.isEmpty()) Arb.empty()15else Arb.from(EdgeCaseMapGenerator(keys.toList(), values))16fun <K, V> Arb.Companion.edgecaseMap(keys: List<K>, values: Set<V>): Arb<Map<K, V>> =17if (keys.isEmpty()) Arb.empty()18else Arb.from(EdgeCaseMapGenerator(keys, values.toList()))
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!!