@@ -4,9 +4,17 @@ import org.utbot.fuzzer.CartesianProduct
4
4
import org.utbot.fuzzer.Combinations
5
5
import org.junit.jupiter.api.Assertions.assertArrayEquals
6
6
import org.junit.jupiter.api.Assertions.assertEquals
7
+ import org.junit.jupiter.api.Assertions.assertFalse
7
8
import org.junit.jupiter.api.Assertions.assertThrows
9
+ import org.junit.jupiter.api.Assertions.assertTrue
8
10
import org.junit.jupiter.api.Test
11
+ import org.junit.jupiter.api.assertDoesNotThrow
12
+ import org.junit.jupiter.params.ParameterizedTest
13
+ import org.junit.jupiter.params.provider.ValueSource
14
+ import org.utbot.fuzzer.TooManyCombinationsException
15
+ import java.util.BitSet
9
16
import kotlin.math.pow
17
+ import kotlin.random.Random
10
18
11
19
class CombinationsTest {
12
20
@@ -55,11 +63,11 @@ class CombinationsTest {
55
63
val array = intArrayOf(10 , 10 , 10 )
56
64
val combinations = Combinations (* array)
57
65
combinations.forEachIndexed { i, c ->
58
- var actual = 0
66
+ var actual = 0L
59
67
for (pos in array.indices) {
60
68
actual + = c[pos] * (10.0 .pow(array.size - 1.0 - pos).toInt())
61
69
}
62
- assertEquals(i, actual)
70
+ assertEquals(i.toLong() , actual)
63
71
}
64
72
}
65
73
@@ -105,4 +113,161 @@ class CombinationsTest {
105
113
}
106
114
}
107
115
116
+ @ParameterizedTest(name = " testAllLongValues{arguments}" )
117
+ @ValueSource(ints = [1 , 100 , Int .MAX_VALUE ])
118
+ fun testAllLongValues (value : Int ) {
119
+ val combinations = Combinations (value, value, 2 )
120
+ assertEquals(2L * value * value, combinations.size)
121
+ val array = combinations[combinations.size - 1 ]
122
+ assertEquals(value - 1 , array[0 ])
123
+ assertEquals(value - 1 , array[1 ])
124
+ assertEquals(1 , array[2 ])
125
+ }
126
+
127
+ @Test
128
+ fun testCartesianFindsAllValues () {
129
+ val radix = 4
130
+ val product = createIntCartesianProduct(radix, 10 )
131
+ val total = product.estimatedSize
132
+ assertTrue(total < Int .MAX_VALUE ) { " This test should generate less than Int.MAX_VALUE values but has $total " }
133
+
134
+ val set = BitSet ((total / 64 ).toInt())
135
+ val updateSet: (List <String >) -> Unit = {
136
+ val value = it.joinToString(" " ).toLong(radix).toInt()
137
+ assertFalse(set[value])
138
+ set.set(value)
139
+ }
140
+ val realCount = product.onEach(updateSet).count()
141
+ assertEquals(total, realCount.toLong())
142
+
143
+ for (i in 0 until total) {
144
+ assertTrue(set[i.toInt()]) { " Values is not listed for index = $i " }
145
+ }
146
+ for (i in total until set.size()) {
147
+ assertFalse(set[i.toInt()])
148
+ }
149
+ }
150
+
151
+ /* *
152
+ * Creates all numbers from 0 to `radix^repeat`.
153
+ *
154
+ * For example:
155
+ *
156
+ * radix = 2, repeat = 2 -> {'0', '0'}, {'0', '1'}, {'1', '0'}, {'1', '1'}
157
+ * radix = 16, repeat = 1 -> {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}
158
+ */
159
+ private fun createIntCartesianProduct (radix : Int , repeat : Int ) =
160
+ CartesianProduct (
161
+ lists = (1 .. repeat).map {
162
+ Array (radix) { it.toString(radix) }.toList()
163
+ },
164
+ random = Random (0 )
165
+ ).apply {
166
+ assertEquals((1L .. repeat).fold(1L ) { acc, _ -> acc * radix }, estimatedSize)
167
+ }
168
+
169
+ @Test
170
+ fun testCanCreateCartesianProductWithSizeGreaterThanMaxInt () {
171
+ val product = createIntCartesianProduct(5 , 15 )
172
+ assertTrue(product.estimatedSize > Int .MAX_VALUE ) { " This test should generate more than Int.MAX_VALUE values but has ${product.estimatedSize} " }
173
+ assertDoesNotThrow {
174
+ product.first()
175
+ }
176
+ }
177
+
178
+ @Test
179
+ fun testIterationWithChunksIsCorrect () {
180
+ val expected = mutableListOf (
181
+ Triple (0L , 5 , 7L ),
182
+ Triple (5L , 5 , 2L ),
183
+ Triple (10L , 2 , 0L ),
184
+ )
185
+ CartesianProduct .forEachChunk(5 , 12 ) { start, chunk, remain ->
186
+ assertEquals(expected.removeFirst(), Triple (start, chunk, remain))
187
+ }
188
+ assertTrue(expected.isEmpty())
189
+ }
190
+
191
+ @Test
192
+ fun testIterationWithChunksIsCorrectWhenChunkIsIntMax () {
193
+ val total = 12
194
+ val expected = mutableListOf (
195
+ Triple (0L , total, 0L )
196
+ )
197
+ CartesianProduct .forEachChunk(Int .MAX_VALUE , total.toLong()) { start, chunk, remain ->
198
+ assertEquals(expected.removeFirst(), Triple (start, chunk, remain))
199
+ }
200
+ assertTrue(expected.isEmpty())
201
+ }
202
+
203
+ @ParameterizedTest(name = " testIterationWithChunksIsCorrectWhenChunkIs{arguments}" )
204
+ @ValueSource(ints = [1 , 2 , 3 , 4 , 6 , 12 ])
205
+ fun testIterationWithChunksIsCorrectWhenChunk (chunkSize : Int ) {
206
+ val total = 12
207
+ assertTrue(total % chunkSize == 0 ) { " Test requires values that are dividers of the total = $total , but it is not true for $chunkSize " }
208
+ val expected = (0 until total step chunkSize).map { it.toLong() }.map {
209
+ Triple (it, chunkSize, total - it - chunkSize)
210
+ }.toMutableList()
211
+ CartesianProduct .forEachChunk(chunkSize, total.toLong()) { start, chunk, remain ->
212
+ assertEquals(expected.removeFirst(), Triple (start, chunk, remain))
213
+ }
214
+ assertTrue(expected.isEmpty())
215
+ }
216
+
217
+ @ParameterizedTest(name = " testIterationsWithChunksThroughLongWithRemainingIs{arguments}" )
218
+ @ValueSource(longs = [1L , 200L , 307 , Int .MAX_VALUE - 1L , Int .MAX_VALUE .toLong()])
219
+ fun testIterationsWithChunksThroughLongTotal (remaining : Long ) {
220
+ val expected = mutableListOf (
221
+ Triple (0L , Int .MAX_VALUE , Int .MAX_VALUE + remaining),
222
+ Triple (Int .MAX_VALUE .toLong(), Int .MAX_VALUE , remaining),
223
+ Triple (Int .MAX_VALUE * 2L , remaining.toInt(), 0L ),
224
+ )
225
+ CartesianProduct .forEachChunk(Int .MAX_VALUE , Int .MAX_VALUE * 2L + remaining) { start, chunk, remain ->
226
+ assertEquals(expected.removeFirst(), Triple (start, chunk, remain))
227
+ }
228
+ assertTrue(expected.isEmpty())
229
+ }
230
+
231
+ @Test
232
+ fun testCartesianProductDoesNotThrowsExceptionBeforeOverflow () {
233
+ // We assume that a standard method has no more than 7 parameters.
234
+ // In this case every parameter can accept up to 511 values without Long overflow.
235
+ // CartesianProduct throws exception
236
+ val values = Array (511 ) { it }.toList()
237
+ val parameters = Array (7 ) { values }.toList()
238
+ assertDoesNotThrow {
239
+ CartesianProduct (parameters, Random (0 )).asSequence()
240
+ }
241
+ }
242
+
243
+ @Test
244
+ fun testCartesianProductThrowsExceptionOnOverflow () {
245
+ // We assume that a standard method has no more than 7 parameters.
246
+ // In this case every parameter can accept up to 511 values without Long overflow.
247
+ // CartesianProduct throws exception
248
+ val values = Array (512 ) { it }.toList()
249
+ val parameters = Array (7 ) { values }.toList()
250
+ assertThrows(TooManyCombinationsException ::class .java) {
251
+ CartesianProduct (parameters, Random (0 )).asSequence()
252
+ }
253
+ }
254
+
255
+ @ParameterizedTest(name = " testCombinationHasValue{arguments}" )
256
+ @ValueSource(ints = [1 , Int .MAX_VALUE ])
257
+ fun testCombinationHasValue (value : Int ) {
258
+ val combinations = Combinations (value)
259
+ assertEquals(value.toLong(), combinations.size)
260
+ assertEquals(value - 1 , combinations[value - 1L ][0 ])
261
+ }
262
+
263
+ @Test
264
+ fun testNoFailWhenMixedValues () {
265
+ val combinations = Combinations (2 , Int .MAX_VALUE )
266
+ assertEquals(2 * Int .MAX_VALUE .toLong(), combinations.size)
267
+ assertArrayEquals(intArrayOf(0 , 0 ), combinations[0L ])
268
+ assertArrayEquals(intArrayOf(0 , Int .MAX_VALUE - 1 ), combinations[Int .MAX_VALUE - 1L ])
269
+ assertArrayEquals(intArrayOf(1 , 0 ), combinations[Int .MAX_VALUE .toLong()])
270
+ assertArrayEquals(intArrayOf(1 , 1 ), combinations[Int .MAX_VALUE + 1L ])
271
+ assertArrayEquals(intArrayOf(1 , Int .MAX_VALUE - 1 ), combinations[Int .MAX_VALUE * 2L - 1 ])
272
+ }
108
273
}
0 commit comments