Skip to content

Commit 9431dd8

Browse files
authored
Enhance fuzzing for arrays #738 (#755)
1 parent e0b2fc8 commit 9431dd8

File tree

7 files changed

+278
-144
lines changed

7 files changed

+278
-144
lines changed

utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ class UtBotSymbolicEngine(
439439
packageName = executableId.classId.packageName
440440
}
441441
fuzz(thisMethodDescription, ObjectModelProvider(defaultIdGenerator).apply {
442-
limitValuesCreatedByFieldAccessors = 500
442+
totalLimit = 500
443443
})
444444
}.withMutations(
445445
TrieBasedFuzzerStatistics(coveredInstructionValues), methodUnderTestDescription, *defaultModelMutators().toTypedArray()

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

+11-5
Original file line numberDiff line numberDiff line change
@@ -164,22 +164,28 @@ fun defaultModelProviders(idGenerator: IdentityPreservingIdGenerator<Int>): Mode
164164
}
165165

166166
/**
167-
* Creates a model provider for [ObjectModelProvider] that generates values for object constructor.
167+
* Creates a model provider from a list of providers that we want to use by default in [RecursiveModelProvider]
168168
*/
169-
fun objectModelProviders(idGenerator: IdentityPreservingIdGenerator<Int>): ModelProvider {
170-
return ModelProvider.of(
169+
internal fun modelProviderForRecursiveCalls(idGenerator: IdentityPreservingIdGenerator<Int>, recursionDepth: Int): ModelProvider {
170+
val nonRecursiveProviders = ModelProvider.of(
171171
CollectionModelProvider(idGenerator),
172-
ArrayModelProvider(idGenerator),
173172
EnumModelProvider(idGenerator),
174173
StringConstantModelProvider,
175-
RegexModelProvider,
176174
CharToStringModelProvider,
177175
ConstantsModelProvider,
178176
PrimitiveDefaultsModelProvider,
179177
PrimitiveWrapperModelProvider,
180178
)
179+
180+
return if (recursionDepth >= 0)
181+
nonRecursiveProviders
182+
.with(ObjectModelProvider(idGenerator, recursionDepth))
183+
.with(ArrayModelProvider(idGenerator, recursionDepth))
184+
else
185+
nonRecursiveProviders
181186
}
182187

188+
183189
fun defaultModelMutators(): List<ModelMutator> = listOf(
184190
StringRandomMutator,
185191
RegexStringModelMutator,

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

+24-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ fun interface ModelProvider {
4141
}
4242
}
4343

44+
/**
45+
* Applies [transform] for current provider
46+
*/
47+
fun map(transform: (ModelProvider) -> ModelProvider): ModelProvider {
48+
return if (this is Combined) {
49+
Combined(providers.map(transform))
50+
} else {
51+
transform(this)
52+
}
53+
}
54+
4455
/**
4556
* Creates [ModelProvider] that passes unprocessed classes to `modelProvider`.
4657
*
@@ -123,7 +134,19 @@ fun interface ModelProvider {
123134
/**
124135
* Wrapper class that delegates implementation to the [providers].
125136
*/
126-
private class Combined(val providers: List<ModelProvider>): ModelProvider {
137+
private class Combined(providers: List<ModelProvider>): ModelProvider {
138+
val providers: List<ModelProvider>
139+
140+
init {
141+
// Flattening to avoid Combined inside Combined (for correct work of except, map, etc.)
142+
this.providers = providers.flatMap {
143+
if (it is Combined)
144+
it.providers
145+
else
146+
listOf(it)
147+
}
148+
}
149+
127150
override fun generate(description: FuzzedMethodDescription): Sequence<FuzzedParameter> = sequence {
128151
providers.forEach { provider ->
129152
provider.generate(description).forEach {
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,65 @@
11
package org.utbot.fuzzer.providers
22

3+
import org.utbot.framework.plugin.api.ClassId
34
import org.utbot.framework.plugin.api.UtArrayModel
5+
import org.utbot.framework.plugin.api.UtModel
6+
import org.utbot.framework.plugin.api.UtPrimitiveModel
47
import org.utbot.framework.plugin.api.util.defaultValueModel
8+
import org.utbot.framework.plugin.api.util.intClassId
59
import org.utbot.framework.plugin.api.util.isArray
610
import org.utbot.fuzzer.FuzzedMethodDescription
7-
import org.utbot.fuzzer.FuzzedParameter
8-
import org.utbot.fuzzer.IdGenerator
9-
import org.utbot.fuzzer.ModelProvider
10-
import org.utbot.fuzzer.ModelProvider.Companion.yieldAllValues
11-
import java.util.function.IntSupplier
11+
import org.utbot.fuzzer.IdentityPreservingIdGenerator
1212

1313
class ArrayModelProvider(
14-
private val idGenerator: IdGenerator<Int>
15-
) : ModelProvider {
16-
override fun generate(description: FuzzedMethodDescription): Sequence<FuzzedParameter> = sequence {
17-
description.parametersMap
18-
.asSequence()
19-
.filter { (classId, _) -> classId.isArray }
20-
.forEach { (arrayClassId, indices) ->
21-
yieldAllValues(indices, listOf(0, 10).map { arraySize ->
22-
UtArrayModel(
23-
id = idGenerator.createId(),
24-
arrayClassId,
25-
length = arraySize,
26-
arrayClassId.elementClassId!!.defaultValueModel(),
27-
mutableMapOf()
28-
).fuzzed {
29-
this.summary = "%var% = ${arrayClassId.elementClassId!!.simpleName}[$arraySize]"
30-
}
31-
})
14+
idGenerator: IdentityPreservingIdGenerator<Int>,
15+
recursionDepthLeft: Int = 1
16+
) : RecursiveModelProvider(idGenerator, recursionDepthLeft) {
17+
override fun createNewInstance(parentProvider: RecursiveModelProvider, newTotalLimit: Int): RecursiveModelProvider =
18+
ArrayModelProvider(parentProvider.idGenerator, parentProvider.recursionDepthLeft - 1)
19+
.copySettingsFrom(parentProvider)
20+
.apply {
21+
totalLimit = newTotalLimit
22+
if (parentProvider is ArrayModelProvider)
23+
branchingLimit = 1 // This improves performance but also forbids generating "jagged" arrays
3224
}
25+
26+
override fun generateModelConstructors(
27+
description: FuzzedMethodDescription,
28+
classId: ClassId
29+
): List<ModelConstructor> {
30+
if (!classId.isArray)
31+
return listOf()
32+
val lengths = generateArrayLengths(description)
33+
return lengths.map { length ->
34+
ModelConstructor(List(length) { classId.elementClassId!! }) { values ->
35+
createFuzzedArrayModel(classId, length, values.map { it.model } )
36+
}
37+
}
38+
}
39+
40+
private fun generateArrayLengths(description: FuzzedMethodDescription): List<Int> {
41+
val fuzzedLengths = fuzzValuesRecursively(
42+
types = listOf(intClassId),
43+
baseMethodDescription = description,
44+
modelProvider = ConstantsModelProvider
45+
)
46+
47+
// Firstly we will use most "interesting" default sizes, then we will use small sizes obtained from src
48+
return listOf(3, 0) + fuzzedLengths
49+
.map { (it.single().model as UtPrimitiveModel).value as Int }
50+
.filter { it in 1..10 && it != 3 }
51+
.toSortedSet()
52+
.toList()
3353
}
54+
55+
private fun createFuzzedArrayModel(arrayClassId: ClassId, length: Int, values: List<UtModel>?) =
56+
UtArrayModel(
57+
idGenerator.createId(),
58+
arrayClassId,
59+
length,
60+
arrayClassId.elementClassId!!.defaultValueModel(),
61+
values?.withIndex()?.associate { it.index to it.value }?.toMutableMap() ?: mutableMapOf()
62+
).fuzzed {
63+
this.summary = "%var% = ${arrayClassId.elementClassId!!.simpleName}[$length]"
64+
}
3465
}

0 commit comments

Comments
 (0)