Skip to content

Commit 953bb48

Browse files
committed
Fix hanging test and more accurate missing data processing
1 parent 9134330 commit 953bb48

File tree

7 files changed

+70
-61
lines changed

7 files changed

+70
-61
lines changed

utbot-fuzzers/src/test/kotlin/org/utbot/fuzzing/JavaFuzzingTest.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.utbot.fuzzing
22

33
import kotlinx.coroutines.runBlocking
4+
import kotlinx.coroutines.withTimeout
45
import org.junit.jupiter.api.Assertions.*
56
import org.junit.jupiter.api.Test
67
import org.utbot.framework.plugin.api.MethodId
@@ -242,7 +243,9 @@ class JavaFuzzingTest {
242243
private fun <T> runBlockingWithContext(block: suspend () -> T) : T {
243244
return withUtContext(UtContext(this::class.java.classLoader)) {
244245
runBlocking {
245-
block()
246+
withTimeout(10000) {
247+
block()
248+
}
246249
}
247250
}
248251
}

utbot-fuzzing/src/main/kotlin/org/utbot/fuzzing/Api.kt

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package org.utbot.fuzzing
44
import kotlinx.coroutines.yield
55
import mu.KotlinLogging
66
import org.utbot.fuzzing.seeds.KnownValue
7-
import org.utbot.fuzzing.utils.Multiset
7+
import org.utbot.fuzzing.utils.MissedSeed
88
import org.utbot.fuzzing.utils.chooseOne
99
import org.utbot.fuzzing.utils.flipCoin
1010
import org.utbot.fuzzing.utils.transformIfNotEmpty
@@ -56,11 +56,7 @@ interface Fuzzing<TYPE, RESULT, DESCRIPTION : Description<TYPE>, FEEDBACK : Feed
5656
* @param statistic statistic about fuzzing generation like elapsed time or number of the runs.
5757
* @param configuration current used configuration; it can be changes for tuning fuzzing.
5858
*/
59-
fun update(description: DESCRIPTION, statistic: Statistic<TYPE>, configuration: Configuration) {
60-
if (!configuration.generateEmptyListForMissedTypes && statistic.missedTypes.isNotEmpty()) {
61-
error("No seed candidates generated for types:\n\t${statistic.missedTypes.joinToString("\n\t")}")
62-
}
63-
}
59+
suspend fun update(description: DESCRIPTION, statistic: Statistic<TYPE, RESULT>, configuration: Configuration) {}
6460
}
6561

6662
/**
@@ -270,8 +266,8 @@ suspend fun <T, R, D : Description<T>, F : Feedback<T, R>> Fuzzing<T, R, D, F>.f
270266
class StatImpl(
271267
override var totalRuns: Long = 0,
272268
val startTime: Long = System.nanoTime(),
273-
override var missedTypes: Multiset<T> = Multiset(),
274-
) : Statistic<T> {
269+
override var missedTypes: MissedSeed<T, R> = MissedSeed(),
270+
) : Statistic<T, R> {
275271
override val elapsedTime: Long
276272
get() = System.nanoTime() - startTime
277273
}
@@ -292,17 +288,10 @@ suspend fun <T, R, D : Description<T>, F : Feedback<T, R>> Fuzzing<T, R, D, F>.f
292288
run breaking@ {
293289
sequence {
294290
while (description.parameters.isNotEmpty()) {
295-
fuzzing.update(description, userStatistic, configuration)
296291
if (dynamicallyGenerated.isNotEmpty()) {
297292
yield(dynamicallyGenerated.removeFirst())
298293
} else {
299-
val fuzzOne = try {
300-
fuzzOne()
301-
} catch (nsv: NoSeedValueException) {
302-
@Suppress("UNCHECKED_CAST")
303-
userStatistic.missedTypes.add(nsv.type as T)
304-
continue
305-
}
294+
val fuzzOne = fuzzOne()
306295
// fuzz one value, seems to be bad, when have only a few and simple values
307296
yield(fuzzOne)
308297

@@ -320,6 +309,9 @@ suspend fun <T, R, D : Description<T>, F : Feedback<T, R>> Fuzzing<T, R, D, F>.f
320309
}
321310
}.forEach execution@ { values ->
322311
yield()
312+
fuzzing.update(description, userStatistic.apply {
313+
totalRuns++
314+
}, configuration)
323315
check(values.parameters.size == values.result.size) { "Cannot create value for ${values.parameters}" }
324316
val valuesCache = mutableMapOf<Result<T, R>, R>()
325317
val result = values.result.map { valuesCache.computeIfAbsent(it) { r -> create(r) } }
@@ -405,9 +397,8 @@ private fun <TYPE, RESULT, DESCRIPTION : Description<TYPE>, FEEDBACK : Feedback<
405397
): Result<TYPE, RESULT> {
406398
return if (state.recursionTreeDepth > configuration.recursionTreeDepth) {
407399
Result.Empty { task.construct.builder(0) }
408-
} else {
400+
} else try {
409401
val iterations = when {
410-
configuration.generateEmptyListForMissedTypes && task.modify.types.any(state.missedTypes::contains) -> 0
411402
state.iterations >= 0 && random.flipCoin(configuration.probCreateRectangleCollectionInsteadSawLike) -> state.iterations
412403
else -> random.nextInt(0, configuration.collectionIterations + 1)
413404
}
@@ -435,6 +426,14 @@ private fun <TYPE, RESULT, DESCRIPTION : Description<TYPE>, FEEDBACK : Feedback<
435426
},
436427
iterations = iterations
437428
)
429+
} catch (nsv: NoSeedValueException) {
430+
@Suppress("UNCHECKED_CAST")
431+
state.missedTypes[nsv.type as TYPE] = task
432+
if (configuration.generateEmptyCollectionsForMissedTypes) {
433+
Result.Empty { task.construct.builder(0) }
434+
} else {
435+
throw nsv
436+
}
438437
}
439438
}
440439

@@ -452,7 +451,7 @@ private fun <TYPE, RESULT, DESCRIPTION : Description<TYPE>, FEEDBACK : Feedback<
452451
): Result<TYPE, RESULT> {
453452
return if (state.recursionTreeDepth > configuration.recursionTreeDepth) {
454453
Result.Empty { task.empty.builder() }
455-
} else {
454+
} else try {
456455
Result.Recursive(
457456
construct = fuzz(
458457
task.construct.types,
@@ -479,6 +478,14 @@ private fun <TYPE, RESULT, DESCRIPTION : Description<TYPE>, FEEDBACK : Feedback<
479478
take(random.nextInt(size + 1).coerceAtLeast(1))
480479
}
481480
)
481+
} catch (nsv: NoSeedValueException) {
482+
@Suppress("UNCHECKED_CAST")
483+
state.missedTypes[nsv.type as TYPE] = task
484+
if (configuration.generateEmptyRecursiveForMissedTypes) {
485+
Result.Empty { task.empty() }
486+
} else {
487+
throw nsv
488+
}
482489
}
483490
}
484491

@@ -617,7 +624,7 @@ private data class PassRoutine<T, R>(val description: String) : Routine<T, R>(em
617624
private class State<TYPE, RESULT>(
618625
val recursionTreeDepth: Int = 1,
619626
val cache: MutableMap<TYPE, List<Seed<TYPE, RESULT>>>,
620-
val missedTypes: Multiset<TYPE>,
627+
val missedTypes: MissedSeed<TYPE, RESULT>,
621628
val iterations: Int = -1,
622629
)
623630

utbot-fuzzing/src/main/kotlin/org/utbot/fuzzing/Configuration.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,11 @@ class Configuration(
7777
* When true any [Seed.Collection] will not try
7878
* to generate modification if a current type is already known to fail to generate values.
7979
*/
80-
var generateEmptyListForMissedTypes: Boolean = true,
80+
var generateEmptyCollectionsForMissedTypes: Boolean = true,
81+
82+
/**
83+
* When true nay [Seed.Recursive] will not try
84+
* to generate a recursive object, but will use [Seed.Recursive.empty] instead.
85+
*/
86+
var generateEmptyRecursiveForMissedTypes: Boolean = true,
8187
)

utbot-fuzzing/src/main/kotlin/org/utbot/fuzzing/Providers.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class BaseFuzzing<T, R, D : Description<T>, F : Feedback<T, R>>(
2929
val exec: suspend (description: D, values: List<R>) -> F
3030
) : Fuzzing<T, R, D, F> {
3131

32-
var update: (D, Statistic<T>, Configuration) -> Unit = { d, s, c -> super.update(d, s, c) }
32+
var update: suspend (D, Statistic<T, R>, Configuration) -> Unit = { d, s, c -> super.update(d, s, c) }
3333

3434
constructor(vararg providers: ValueProvider<T, R, D>, exec: suspend (description: D, values: List<R>) -> F) : this(providers.toList(), exec)
3535

@@ -52,7 +52,7 @@ class BaseFuzzing<T, R, D : Description<T>, F : Feedback<T, R>>(
5252
return exec(description, values)
5353
}
5454

55-
override fun update(description: D, statistic: Statistic<T>, configuration: Configuration) {
55+
override suspend fun update(description: D, statistic: Statistic<T, R>, configuration: Configuration) {
5656
update.invoke(description, statistic, configuration)
5757
}
5858
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package org.utbot.fuzzing
22

3-
import org.utbot.fuzzing.utils.Multiset
3+
import org.utbot.fuzzing.utils.MissedSeed
44

55
/**
66
* User class that holds data about current fuzzing running.
77
*
88
* Concrete implementation is passed to the [Fuzzing.update].
99
*/
10-
interface Statistic<TYPE> {
10+
interface Statistic<TYPE, RESULT> {
1111
val totalRuns: Long
1212
val elapsedTime: Long
13-
val missedTypes: Multiset<TYPE>
13+
val missedTypes: MissedSeed<TYPE, RESULT>
1414
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.utbot.fuzzing.utils
2+
3+
import org.utbot.fuzzing.Seed
4+
5+
class MissedSeed<T, R> : Iterable<T> {
6+
7+
private val values = hashMapOf<T, Seed<T, R>>()
8+
9+
operator fun set(value: T, seed: Seed<T, R>) {
10+
values[value] = seed
11+
}
12+
13+
operator fun get(value: T): Seed<T, R>? {
14+
return values[value]
15+
}
16+
17+
fun isEmpty(): Boolean {
18+
return values.size == 0
19+
}
20+
21+
fun isNotEmpty(): Boolean = !isEmpty()
22+
23+
override fun iterator(): Iterator<T> {
24+
return values.keys.iterator()
25+
}
26+
27+
}

utbot-fuzzing/src/main/kotlin/org/utbot/fuzzing/utils/Multiset.kt

Lines changed: 0 additions & 34 deletions
This file was deleted.

0 commit comments

Comments
 (0)