Skip to content

Commit

Permalink
Fix Arb.list failing within edge cases in case of null values (#3982)
Browse files Browse the repository at this point in the history
This shall fix the issue as described in
#3981.
Instead of just assuming that a null value means "there is no edge
case", this change adds explicitly checking that, effectively fixing the
problem.
  • Loading branch information
oliverblaha committed May 9, 2024
1 parent 0bf6ace commit 6243176
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 4 deletions.
Expand Up @@ -96,13 +96,17 @@ fun <A> Arb.Companion.list(gen: Gen<A>, range: IntRange = 0..100): Arb<List<A>>
edgecaseFn = { rs ->
val emptyList = emptyList<A>()
val singleList: List<A>? = when (gen) {
is Arb -> (gen.edgecase(rs) ?: gen.next(rs))?.let { listOf(it) }
is Exhaustive -> gen.values.firstOrNull()?.let { listOf(it) }
is Arb -> listOf(if (gen.edgecases().isNotEmpty()) gen.edgecases().random(rs.random) else gen.next(rs))
is Exhaustive -> gen.values.let { if (it.isEmpty()) null else listOf(it.first()) }
}
val repeatedList: List<A>? = when {
range.last < 2 -> null // too small for repeats
gen is Arb -> (gen.edgecase(rs) ?: gen.next(rs))?.let { a -> List(max(2, range.first)) { a } }
gen is Exhaustive -> gen.values.firstOrNull()?.let { a -> List(max(2, range.first)) { a } }
gen is Arb -> (if (gen.edgecases().isNotEmpty()) gen.edgecases().random(rs.random) else gen.next(rs))
.let { a -> List(max(2, range.first)) { a } }

gen is Exhaustive -> if (gen.values.isEmpty()) null else gen.values.first()
.let { a -> List(max(2, range.first)) { a } }

else -> null
}
listOfNotNull(emptyList, singleList, repeatedList).filter { it.size in range }.distinct().random(rs.random)
Expand Down
@@ -1,28 +1,36 @@
package com.sksamuel.kotest.property.arbitrary

import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.assertions.throwables.shouldThrowAny
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.inspectors.forAll
import io.kotest.matchers.collections.exist
import io.kotest.matchers.collections.shouldContain
import io.kotest.matchers.collections.shouldHaveAtLeastSize
import io.kotest.matchers.collections.shouldHaveAtMostSize
import io.kotest.matchers.collections.shouldNotBeEmpty
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNot
import io.kotest.property.Arb
import io.kotest.property.EdgeConfig
import io.kotest.property.Exhaustive
import io.kotest.property.PropTestConfig
import io.kotest.property.RandomSource
import io.kotest.property.arbitrary.constant
import io.kotest.property.arbitrary.double
import io.kotest.property.arbitrary.edgecases
import io.kotest.property.arbitrary.int
import io.kotest.property.arbitrary.list
import io.kotest.property.arbitrary.of
import io.kotest.property.arbitrary.orNull
import io.kotest.property.arbitrary.positiveInt
import io.kotest.property.arbitrary.set
import io.kotest.property.arbitrary.single
import io.kotest.property.arbitrary.take
import io.kotest.property.checkAll
import io.kotest.property.exhaustive.constant
import io.kotest.property.exhaustive.of
import io.kotest.property.forAll

class CollectionsTest : DescribeSpec({
Expand Down Expand Up @@ -91,6 +99,26 @@ class CollectionsTest : DescribeSpec({
it.shouldHaveAtMostSize(500)
}
}

it("support underlying generators returning null") {
checkAll(
Exhaustive.of(
Exhaustive.constant(null),
Arb.constant(null),
Arb.constant("").orNull(1.0),
Arb.constant(0).orNull(1.0)
),
Exhaustive.of(0..100, 1..10, 2..50),
) { gen, range ->
shouldNotThrowAny {
Arb.list(gen, range).checkAll(PropTestConfig(edgeConfig = EdgeConfig(0.5))) {
it.shouldHaveAtLeastSize(range.first)
it.shouldHaveAtMostSize(range.last)
it shouldNot exist { value -> value != null }
}
}
}
}
}

describe("Arb.set should") {
Expand Down

0 comments on commit 6243176

Please sign in to comment.