Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Arb.list failing within edge cases in case of null values #3982

Merged
merged 3 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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