Skip to content

Commit 4d17c91

Browse files
committed
ListContainingNull.kt: re-write
1 parent 257b15a commit 4d17c91

File tree

1 file changed

+82
-30
lines changed
  • firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary

1 file changed

+82
-30
lines changed

firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/ListContainingNull.kt

Lines changed: 82 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import io.kotest.property.Exhaustive
2121
import io.kotest.property.Gen
2222
import io.kotest.property.RandomSource
2323
import io.kotest.property.Sample
24+
import io.kotest.property.arbitrary.bind
2425
import io.kotest.property.arbitrary.enum
26+
import io.kotest.property.arbitrary.filterNot
2527
import io.kotest.property.arbitrary.next
2628
import io.kotest.property.asSample
2729
import kotlin.random.nextInt
@@ -71,44 +73,94 @@ private class ListContainingNullArb<T>(
7173
is Exhaustive<T> -> gen.toArb()
7274
}
7375

74-
private val edgeCaseArb: Arb<EdgeCase> = Arb.enum()
76+
private val edgeCaseArb: Arb<EdgeCase> = Arb.edgeCase()
7577

76-
override fun edgecase(rs: RandomSource): List<T?> =
77-
when (edgeCaseArb.next(rs)) {
78-
EdgeCase.ALL_NULLS -> List(randomListSize(rs)) { null }
79-
EdgeCase.MIN_SIZE -> generateListWithAtLeastOneNullElement(rs, size.first)
80-
EdgeCase.MIN_SIZE_ALL_NULLS -> List(size.first) { null }
81-
EdgeCase.MAX_SIZE -> generateListWithAtLeastOneNullElement(rs, size.last)
82-
EdgeCase.MAX_SIZE_ALL_NULLS -> List(size.last) { null }
78+
override fun sample(rs: RandomSource): Sample<List<T?>> =
79+
sample(rs, listSize = randomListSize(rs)).asSample()
80+
81+
private fun sample(rs: RandomSource, listSize: Int): List<T?> {
82+
require(listSize > 0) { "invalid listSize: $listSize (must be greater than zero)" }
83+
val guaranteedNullIndex = randomGuaranteedNullIndex(rs, listSize)
84+
return List(listSize) { index ->
85+
if (index == guaranteedNullIndex || rs.random.nextDouble() < nullProbability) {
86+
null
87+
} else {
88+
arb.next(rs)
89+
}
8390
}
91+
}
8492

85-
override fun sample(rs: RandomSource): Sample<List<T?>> =
86-
generateListWithAtLeastOneNullElement(rs).asSample()
87-
88-
private fun generateListWithAtLeastOneNullElement(
89-
rs: RandomSource,
90-
listSize: Int = randomListSize(rs)
91-
): List<T?> =
92-
buildList(listSize) {
93-
repeat(listSize) {
94-
if (rs.random.nextDouble() < nullProbability) {
95-
add(null)
96-
} else {
97-
add(arb.next(rs))
98-
}
93+
override fun edgecase(rs: RandomSource): List<T?> {
94+
val (listSizeCategory, nullPositions) = edgeCaseArb.next(rs)
95+
96+
val listSize =
97+
when (listSizeCategory) {
98+
EdgeCase.ListSizeCategory.MIN -> size.first
99+
EdgeCase.ListSizeCategory.MAX -> size.last
100+
EdgeCase.ListSizeCategory.RANDOM -> randomListSize(rs)
99101
}
100-
if (!contains(null)) {
101-
set(rs.random.nextInt(size), null)
102+
103+
val guaranteedNullIndex =
104+
when (nullPositions) {
105+
EdgeCase.NullPositions.RANDOM -> randomGuaranteedNullIndex(rs, listSize)
106+
EdgeCase.NullPositions.FIRST,
107+
EdgeCase.NullPositions.LAST,
108+
EdgeCase.NullPositions.FIRST_AND_LAST,
109+
EdgeCase.NullPositions.ALL -> -1
102110
}
111+
112+
val firstIndex = 0
113+
val lastIndex = listSize - 1
114+
115+
return List(listSize) {
116+
val isNull =
117+
when (nullPositions) {
118+
EdgeCase.NullPositions.FIRST -> it == firstIndex
119+
EdgeCase.NullPositions.LAST -> it == lastIndex
120+
EdgeCase.NullPositions.FIRST_AND_LAST -> it == firstIndex || it == lastIndex
121+
EdgeCase.NullPositions.ALL -> true
122+
EdgeCase.NullPositions.RANDOM -> it == guaranteedNullIndex
123+
}
124+
if (isNull) null else arb.next(rs)
103125
}
126+
}
104127

105128
private fun randomListSize(rs: RandomSource): Int = rs.random.nextInt(size)
106129

107-
private enum class EdgeCase {
108-
ALL_NULLS,
109-
MIN_SIZE,
110-
MIN_SIZE_ALL_NULLS,
111-
MAX_SIZE,
112-
MAX_SIZE_ALL_NULLS,
130+
private fun randomGuaranteedNullIndex(rs: RandomSource, listSize: Int): Int =
131+
rs.random.nextInt(listSize)
132+
133+
private data class EdgeCase(
134+
val listSizeCategory: ListSizeCategory,
135+
val nullPositions: NullPositions
136+
) {
137+
138+
enum class ListSizeCategory {
139+
MIN,
140+
MAX,
141+
RANDOM,
142+
}
143+
144+
enum class NullPositions {
145+
FIRST,
146+
LAST,
147+
FIRST_AND_LAST,
148+
ALL,
149+
RANDOM,
150+
}
151+
}
152+
153+
private companion object {
154+
fun Arb.Companion.edgeCase(
155+
listSizeCategory: Arb<EdgeCase.ListSizeCategory> = Arb.enum(),
156+
nullPositions: Arb<EdgeCase.NullPositions> = Arb.enum()
157+
): Arb<EdgeCase> =
158+
Arb.bind(listSizeCategory, nullPositions) { listSizeCategoryValue, nullPositionsValue ->
159+
EdgeCase(listSizeCategoryValue, nullPositionsValue)
160+
}
161+
.filterNot {
162+
it.listSizeCategory == EdgeCase.ListSizeCategory.RANDOM &&
163+
it.nullPositions == EdgeCase.NullPositions.RANDOM
164+
}
113165
}
114166
}

0 commit comments

Comments
 (0)