Skip to content

Commit 3a9b87f

Browse files
committed
Fuzzer can pass one object into several arguments of same type
1 parent 97d8336 commit 3a9b87f

File tree

9 files changed

+73
-16
lines changed

9 files changed

+73
-16
lines changed

utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/FuzzedType.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import org.utbot.framework.plugin.api.ClassId
1414
*
1515
* @see ClassId.typeParameters
1616
*/
17-
class FuzzedType(
17+
data class FuzzedType(
1818
val classId: ClassId,
1919
val generics: List<FuzzedType> = emptyList(),
2020
)

utbot-fuzzers/src/main/kotlin/org/utbot/fuzzing/JavaLanguage.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,13 @@ suspend fun runJavaFuzzing(
9090
val tracer = Trie(Instruction::id)
9191
val descriptionWithOptionalThisInstance = FuzzedDescription(createFuzzedMethodDescription(thisInstance), tracer)
9292
val descriptionWithOnlyParameters = FuzzedDescription(createFuzzedMethodDescription(null), tracer)
93-
BaseFuzzing(providers) { _, t ->
93+
runFuzzing(ValueProvider.of(providers), descriptionWithOptionalThisInstance) { _, t ->
9494
if (thisInstance == null) {
9595
exec(null, descriptionWithOnlyParameters, t)
9696
} else {
9797
exec(t.first(), descriptionWithOnlyParameters, t.drop(1))
9898
}
99-
}.fuzz(descriptionWithOptionalThisInstance)
99+
}
100100
}
101101

102102
private fun toFuzzerType(type: Type): FuzzedType {

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

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import mu.KotlinLogging
55
import org.utbot.fuzzing.seeds.KnownValue
66
import org.utbot.fuzzing.utils.chooseOne
77
import org.utbot.fuzzing.utils.flipCoin
8+
import org.utbot.fuzzing.utils.transformIfNotEmpty
89
import kotlin.random.Random
910

1011
private 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

615636
private class Statistics<TYPE, RESULT, FEEDBACK : Feedback<TYPE, RESULT>> {

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ class Configuration(
1818
*/
1919
var collectionIterations: Int = 5,
2020

21-
var maximumObjectModifications: Int = Int.MAX_VALUE,
22-
2321
/**
2422
* Energy function that is used to choose seeded value.
2523
*/
@@ -40,6 +38,11 @@ class Configuration(
4038
*/
4139
var probConstructorMutationInsteadModificationMutation: Int = 90,
4240

41+
/**
42+
* Probability to shuffle modification list of the recursive object
43+
*/
44+
var probShuffleAndCutRecursiveObjectModificationMutation: Int = 10,
45+
4346
/**
4447
* Probability to prefer create rectangle collections instead of creating saw-like one.
4548
*/
@@ -64,4 +67,9 @@ class Configuration(
6467
* Probability of removing an old character from StringValue when mutating
6568
*/
6669
var probStringRemoveCharacter: Int = 50,
70+
71+
/**
72+
* Probability of reusing same generated value when 2 or more parameters have the same type.
73+
*/
74+
var probReuseGeneratedValueForSameType: Int = 1
6775
)

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
package org.utbot.fuzzing
22

33
import mu.KotlinLogging
4+
import kotlin.random.Random
45

56
private val logger by lazy { KotlinLogging.logger {} }
67

8+
/**
9+
* Entry point to run fuzzing.
10+
*/
11+
suspend fun <TYPE, RESULT, DESCRIPTION : Description<TYPE>, FEEDBACK : Feedback<TYPE, RESULT>> runFuzzing(
12+
provider: ValueProvider<TYPE, RESULT, DESCRIPTION>,
13+
description: DESCRIPTION,
14+
random: Random = Random(0),
15+
configuration: Configuration = Configuration(),
16+
handle: suspend (description: DESCRIPTION, values: List<RESULT>) -> FEEDBACK
17+
) {
18+
BaseFuzzing(listOf(provider), handle).fuzz(description, random, configuration)
19+
}
20+
721
/**
822
* Implements base concepts that use providers to generate values for some types.
923
*
@@ -32,7 +46,7 @@ class BaseFuzzing<T, R, D : Description<T>, F : Feedback<T, R>>(
3246
}
3347
}
3448

35-
override suspend fun run(description: D, values: List<R>): F {
49+
override suspend fun handle(description: D, values: List<R>): F {
3650
return exec(description, values)
3751
}
3852
}

utbot-fuzzing/src/main/kotlin/org/utbot/fuzzing/demo/AbcFuzzing.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ suspend fun main() {
4747
* This implementation just calls the target function and returns a result. After it returns an empty feedback.
4848
* If some returned value equals to the length of the source string then feedback returns 'stop' signal.
4949
*/
50-
override suspend fun run(description: Description<Unit>, values: List<String>): BaseFeedback<Int, Unit, String> {
50+
override suspend fun handle(description: Description<Unit>, values: List<String>): BaseFeedback<Int, Unit, String> {
5151
check(values.size == 1) {
5252
"Only one value must be generated because of `description.parameters.size = ${description.parameters.size}`"
5353
}

utbot-fuzzing/src/main/kotlin/org/utbot/fuzzing/demo/JavaFuzzing.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ object JavaFuzzing : Fuzzing<Class<*>, Any?, Description<Class<*>>, Feedback<Cla
7171
}
7272
}
7373

74-
override suspend fun run(description: Description<Class<*>>, values: List<Any?>): Feedback<Class<*>, Any?> {
74+
override suspend fun handle(description: Description<Class<*>>, values: List<Any?>): Feedback<Class<*>, Any?> {
7575
println(values.joinToString {
7676
when (it) {
7777
is BooleanArray -> it.contentToString()

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ internal enum class BinaryFormat : (Int) -> Boolean {
8181
DOUBLE { override fun invoke(index: Int) = index % 16 == 0 && index != 0 },
8282
}
8383

84+
internal fun <T> List<T>.transformIfNotEmpty(transform: List<T>.() -> List<T>): List<T> {
85+
return if (isNotEmpty()) transform() else this
86+
}
87+
8488
fun main() {
8589
val endian = Endian.BE
8690
println(255.toUByte().toBinaryString(endian))

utbot-fuzzing/src/test/kotlin/org/utbot/fuzzing/samples/Objects.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,14 @@ public boolean testMe(Recursive r) {
2323
return false;
2424
}
2525

26+
private int data;
27+
28+
public static void foo(Objects a, Objects b) {
29+
a.data = 1;
30+
b.data = 2;
31+
//noinspection ConstantValue
32+
if (a.data == b.data) {
33+
throw new IllegalArgumentException();
34+
}
35+
}
2636
}

0 commit comments

Comments
 (0)