@@ -5,6 +5,7 @@ import mu.KotlinLogging
55import org.utbot.fuzzing.seeds.KnownValue
66import org.utbot.fuzzing.utils.chooseOne
77import org.utbot.fuzzing.utils.flipCoin
8+ import org.utbot.fuzzing.utils.transformIfNotEmpty
89import kotlin.random.Random
910
1011private val logger by lazy { KotlinLogging .logger {} }
@@ -30,16 +31,18 @@ interface Fuzzing<TYPE, RESULT, DESCRIPTION : Description<TYPE>, FEEDBACK : Feed
3031 fun generate (description : DESCRIPTION , type : TYPE ): Sequence <Seed <TYPE , RESULT >>
3132
3233 /* *
34+ * This method is called on every value list generated by fuzzer.
35+ *
3336 * Fuzzing combines, randomize and mutates values using the seeds.
3437 * Then it generates values and runs them with this method. This method should provide some feedback,
3538 * which is the most important part for good fuzzing result. [emptyFeedback] can be provided only for test
3639 * or infinite loops. Consider to implement own implementation of [Feedback] to provide more correct data or
3740 * use [BaseFeedback] to generate key based feedback. In this case, the key is used to analyse what value should be next.
3841 *
3942 * @param description contains user-defined information about current run. Can be used as a state of the run.
40- * @param values current values to run .
43+ * @param values current values to process .
4144 */
42- suspend fun run (description : DESCRIPTION , values : List <RESULT >): FEEDBACK
45+ suspend fun handle (description : DESCRIPTION , values : List <RESULT >): FEEDBACK
4346}
4447
4548/* *
@@ -277,9 +280,11 @@ suspend fun <T, R, D : Description<T>, F : Feedback<T, R>> Fuzzing<T, R, D, F>.f
277280 }
278281 }
279282 }.forEach execution@ { values ->
280- val result = values.map { create(it) }
283+ check(values.parameters.size == values.result.size) { " Cannot create value for ${values.parameters} " }
284+ val valuesCache = mutableMapOf<Result <T , R >, R > ()
285+ val result = values.result.map { valuesCache.computeIfAbsent(it) { r -> create(r) } }
281286 val feedback = try {
282- fuzzing.run (description, result)
287+ fuzzing.handle (description, result)
283288 } catch (t: Throwable ) {
284289 logger.error(t) { " Error when running fuzzing with $values " }
285290 return @execution
@@ -312,7 +317,17 @@ private fun <TYPE, RESULT, DESCRIPTION : Description<TYPE>, FEEDBACK : Feedback<
312317 builder : Routine <TYPE , RESULT >,
313318 state : State <TYPE , RESULT >,
314319): Node <TYPE , RESULT > {
315- val result = parameters.map { type -> produce(type, fuzzing, description, random, configuration, state) }
320+ val typeCache = mutableMapOf<TYPE , MutableList <Result <TYPE , RESULT >>>()
321+ val result = parameters.map { type ->
322+ val results = typeCache.computeIfAbsent(type) { mutableListOf () }
323+ if (results.isNotEmpty() && random.flipCoin(configuration.probReuseGeneratedValueForSameType)) {
324+ results.random(random)
325+ } else {
326+ produce(type, fuzzing, description, random, configuration, state).also {
327+ results + = it
328+ }
329+ }
330+ }
316331 // is not inlined to debug values generated for a concrete type
317332 return Node (result, parameters, builder)
318333}
@@ -413,8 +428,7 @@ private fun <TYPE, RESULT, DESCRIPTION : Description<TYPE>, FEEDBACK : Feedback<
413428 State (state.recursionTreeDepth + 1 , state.cache)
414429 ),
415430 modify = task.modify
416- .shuffled()
417- .take(configuration.maximumObjectModifications.coerceAtLeast(1 ))
431+ .shuffled(random)
418432 .mapTo(arrayListOf ()) { routine ->
419433 fuzz(
420434 routine.types,
@@ -425,6 +439,8 @@ private fun <TYPE, RESULT, DESCRIPTION : Description<TYPE>, FEEDBACK : Feedback<
425439 routine,
426440 State (state.recursionTreeDepth + 1 , state.cache)
427441 )
442+ }.transformIfNotEmpty {
443+ take(random.nextInt(size + 1 ).coerceAtLeast(1 ))
428444 }
429445 )
430446 }
@@ -462,6 +478,11 @@ private fun <TYPE, RESULT, DESCRIPTION : Description<TYPE>, FEEDBACK : Feedback<
462478 construct = mutate(resultToMutate.construct, fuzzing, random, configuration, State (state.recursionTreeDepth + 1 , state.cache)),
463479 modify = resultToMutate.modify
464480 )
481+ } else if (random.flipCoin(configuration.probShuffleAndCutRecursiveObjectModificationMutation)) {
482+ Result .Recursive (
483+ construct = resultToMutate.construct,
484+ modify = resultToMutate.modify.shuffled(random).take(random.nextInt(resultToMutate.modify.size + 1 ).coerceAtLeast(1 ))
485+ )
465486 } else {
466487 Result .Recursive (
467488 construct = resultToMutate.construct,
@@ -609,7 +630,7 @@ private class Node<TYPE, RESULT>(
609630 val result : List <Result <TYPE , RESULT >>,
610631 val parameters : List <TYPE >,
611632 val builder : Routine <TYPE , RESULT >,
612- ) : Iterable<Result<TYPE, RESULT>> by result
633+ )
613634
614635
615636private class Statistics <TYPE , RESULT , FEEDBACK : Feedback <TYPE , RESULT >> {
0 commit comments