@@ -21,7 +21,9 @@ import io.kotest.property.Exhaustive
21
21
import io.kotest.property.Gen
22
22
import io.kotest.property.RandomSource
23
23
import io.kotest.property.Sample
24
+ import io.kotest.property.arbitrary.bind
24
25
import io.kotest.property.arbitrary.enum
26
+ import io.kotest.property.arbitrary.filterNot
25
27
import io.kotest.property.arbitrary.next
26
28
import io.kotest.property.asSample
27
29
import kotlin.random.nextInt
@@ -71,44 +73,94 @@ private class ListContainingNullArb<T>(
71
73
is Exhaustive <T > -> gen.toArb()
72
74
}
73
75
74
- private val edgeCaseArb: Arb <EdgeCase > = Arb .enum ()
76
+ private val edgeCaseArb: Arb <EdgeCase > = Arb .edgeCase ()
75
77
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
+ }
83
90
}
91
+ }
84
92
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)
99
101
}
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
102
110
}
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)
103
125
}
126
+ }
104
127
105
128
private fun randomListSize (rs : RandomSource ): Int = rs.random.nextInt(size)
106
129
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
+ }
113
165
}
114
166
}
0 commit comments