Skip to content

Commit 570809e

Browse files
committed
Moved common logic from ArrayMP and ObjectMP to RecursiveMP
1 parent 1a730a9 commit 570809e

File tree

7 files changed

+162
-170
lines changed

7 files changed

+162
-170
lines changed

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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ fun defaultModelProviders(idGenerator: IdentityPreservingIdGenerator<Int>): Mode
166166
/**
167167
* Creates a model provider consisting of providers that do not make recursive calls inside them
168168
*/
169-
fun nonRecursiveProviders(idGenerator: IdentityPreservingIdGenerator<Int>): ModelProvider {
169+
private fun nonRecursiveProviders(idGenerator: IdentityPreservingIdGenerator<Int>): ModelProvider {
170170
return ModelProvider.of(
171171
CollectionModelProvider(idGenerator),
172172
EnumModelProvider(idGenerator),
@@ -180,15 +180,25 @@ fun nonRecursiveProviders(idGenerator: IdentityPreservingIdGenerator<Int>): Mode
180180
}
181181

182182
/**
183-
* Creates a model provider consisting of providers that will make no more than [recursion] nested recursive calls.
183+
* TODO: write doc here
184184
*/
185-
fun recursiveModelProviders(idGenerator: IdentityPreservingIdGenerator<Int>, recursion: Int): ModelProvider {
185+
private fun recursiveModelProviders(idGenerator: IdentityPreservingIdGenerator<Int>, recursionDepth: Int): ModelProvider {
186186
return ModelProvider.of(
187-
ObjectModelProvider(idGenerator, recursion),
188-
ArrayModelProvider(idGenerator, recursion)
187+
ObjectModelProvider(idGenerator, recursionDepth),
188+
ArrayModelProvider(idGenerator, recursionDepth)
189189
)
190190
}
191191

192+
/**
193+
* Creates a model provider from a list of default providers.
194+
*/
195+
fun defaultModelProviders(idGenerator: IdentityPreservingIdGenerator<Int>, recursionDepth: Int = 1): ModelProvider =
196+
if (recursionDepth >= 0)
197+
nonRecursiveProviders(idGenerator).with(recursiveModelProviders(idGenerator, recursionDepth))
198+
else
199+
nonRecursiveProviders(idGenerator)
200+
201+
192202
fun defaultModelMutators(): List<ModelMutator> = listOf(
193203
StringRandomMutator,
194204
RegexStringModelMutator,

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,16 @@ fun interface ModelProvider {
3737
return if (this is Combined) {
3838
Combined(providers.filterNot(filter))
3939
} else {
40-
Combined(if (filter(this)) emptyList() else listOf(this))
40+
Combined(if (filter(this)) emptyList() else listOf(this)) // TODO: remove Combined from here (?)
41+
}
42+
}
43+
44+
// TODO: add KDoc here
45+
fun map(transform: (ModelProvider) -> ModelProvider): ModelProvider {
46+
return if (this is Combined) {
47+
Combined(providers.map(transform))
48+
} else {
49+
transform(this)
4150
}
4251
}
4352

@@ -123,6 +132,7 @@ fun interface ModelProvider {
123132
/**
124133
* Wrapper class that delegates implementation to the [providers].
125134
*/
135+
// TODO: flatten Combined instances in providers (?)
126136
private class Combined(val providers: List<ModelProvider>): ModelProvider {
127137
override fun generate(description: FuzzedMethodDescription): Sequence<FuzzedParameter> = sequence {
128138
providers.forEach { provider ->

utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ArrayModelProvider.kt

Lines changed: 17 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,70 +8,43 @@ import org.utbot.framework.plugin.api.util.defaultValueModel
88
import org.utbot.framework.plugin.api.util.intClassId
99
import org.utbot.framework.plugin.api.util.isArray
1010
import org.utbot.fuzzer.FuzzedMethodDescription
11-
import org.utbot.fuzzer.FuzzedParameter
12-
import org.utbot.fuzzer.FuzzedValue
1311
import org.utbot.fuzzer.IdentityPreservingIdGenerator
14-
import org.utbot.fuzzer.ModelProvider.Companion.yieldAllValues
1512

1613
class ArrayModelProvider(
1714
idGenerator: IdentityPreservingIdGenerator<Int>,
1815
recursion: Int = 1
1916
) : RecursiveModelProvider(idGenerator, recursion) {
20-
21-
private val defaultArraySize = 3
22-
23-
private val limitRecursivelyFuzzed =
24-
when(recursion) {
25-
1 -> Int.MAX_VALUE
26-
else -> 3
27-
}
28-
29-
override fun generate(description: FuzzedMethodDescription): Sequence<FuzzedParameter> = sequence {
30-
description.parametersMap
31-
.asSequence()
32-
.filter { (classId, _) -> classId.isArray }
33-
.forEach { (arrayClassId, indices) ->
34-
35-
// Fuzz small arrays with interesting elements
36-
yieldAllValues(indices, generateArrayRecursively(arrayClassId, description, defaultArraySize))
37-
38-
// Fuzz arrays with interesting lengths and default-valued elements
39-
val lengths = generateArrayLengths(description)
40-
yieldAllValues(indices, lengths.asSequence().map { length ->
41-
createFuzzedArrayModel(arrayClassId, length, null)
42-
})
17+
override fun copy(idGenerator: IdentityPreservingIdGenerator<Int>, recursionDepthLeft: Int) =
18+
ArrayModelProvider(idGenerator, recursionDepthLeft)
19+
20+
override fun generateModelConstructors(
21+
description: FuzzedMethodDescription,
22+
clazz: ClassId
23+
): List<ModelConstructor> {
24+
if (!clazz.isArray)
25+
return listOf()
26+
val lengths = generateArrayLengths(description).sorted()
27+
return lengths.map { length ->
28+
ModelConstructor(List(length) { clazz.elementClassId!! }) { values ->
29+
createFuzzedArrayModel(clazz, length, values.map { it.model } )
4330
}
31+
}
4432
}
4533

4634
private fun generateArrayLengths(description: FuzzedMethodDescription): Set<Int> {
35+
description.concreteValues
4736
val fuzzedLengths = fuzzValuesRecursively(
4837
types = listOf(intClassId),
4938
baseMethodDescription = description,
50-
modelProvider = ConstantsModelProvider,
51-
generatedValuesName = "array length"
39+
modelProvider = ConstantsModelProvider
5240
)
5341

5442
return fuzzedLengths
5543
.map { (it.single().model as UtPrimitiveModel).value as Int }
5644
.filter { it in 0..10 }
5745
.toSet()
5846
.plus(0)
59-
}
60-
61-
private fun generateArrayRecursively(arrayClassId: ClassId, description: FuzzedMethodDescription, length: Int): Sequence<FuzzedValue> {
62-
val elementClassId = arrayClassId.elementClassId ?: error("expected ClassId for array but got ${arrayClassId.name}")
63-
val fuzzedArrayElements = fuzzValuesRecursively(
64-
types = List(length) { elementClassId },
65-
baseMethodDescription = description,
66-
modelProvider = generateRecursiveProvider(),
67-
generatedValuesName = "elements of array"
68-
)
69-
70-
return fuzzedArrayElements
71-
.take(limitRecursivelyFuzzed)
72-
.map { elements ->
73-
createFuzzedArrayModel(arrayClassId, length, elements.map { it.model })
74-
}
47+
.plus(3)
7548
}
7649

7750
private fun createFuzzedArrayModel(arrayClassId: ClassId, length: Int, values: List<UtModel>?) =
Lines changed: 62 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.utbot.fuzzer.providers
22

3-
import mu.KotlinLogging
43
import org.utbot.framework.plugin.api.ClassId
54
import org.utbot.framework.plugin.api.ConstructorId
65
import org.utbot.framework.plugin.api.FieldId
@@ -16,113 +15,93 @@ import org.utbot.framework.plugin.api.util.jClass
1615
import org.utbot.framework.plugin.api.util.stringClassId
1716
import org.utbot.fuzzer.IdentityPreservingIdGenerator
1817
import org.utbot.fuzzer.FuzzedMethodDescription
19-
import org.utbot.fuzzer.FuzzedParameter
2018
import org.utbot.fuzzer.FuzzedValue
21-
import org.utbot.fuzzer.ModelProvider
22-
import org.utbot.fuzzer.ModelProvider.Companion.yieldValue
23-
import org.utbot.fuzzer.TooManyCombinationsException
24-
import org.utbot.fuzzer.fuzz
2519
import org.utbot.fuzzer.providers.ConstantsModelProvider.fuzzed
2620
import java.lang.reflect.Constructor
2721
import java.lang.reflect.Field
2822
import java.lang.reflect.Member
2923
import java.lang.reflect.Method
3024
import java.lang.reflect.Modifier.*
3125

32-
private val logger by lazy { KotlinLogging.logger {} }
33-
3426
/**
3527
* Creates [UtAssembleModel] for objects which have public constructors with primitives types and String as parameters.
3628
*/
3729
class ObjectModelProvider(
3830
idGenerator: IdentityPreservingIdGenerator<Int>,
3931
recursion: Int = 1,
4032
) : RecursiveModelProvider(idGenerator, recursion) {
33+
override fun copy(idGenerator: IdentityPreservingIdGenerator<Int>, recursionDepthLeft: Int) =
34+
ObjectModelProvider(idGenerator, recursionDepthLeft)
35+
36+
override fun generateModelConstructors(
37+
description: FuzzedMethodDescription,
38+
clazz: ClassId
39+
): List<ModelConstructor> {
40+
if (clazz == stringClassId || clazz.isPrimitiveWrapper)
41+
return listOf()
42+
43+
val constructors = collectConstructors(clazz) { javaConstructor ->
44+
isAccessible(javaConstructor, description.packageName)
45+
}.sortedWith(
46+
primitiveParameterizedConstructorsFirstAndThenByParameterCount
47+
)
4148

42-
// TODO: can we make it private val (maybe depending on recursion)?
43-
var limitValuesCreatedByFieldAccessors: Int = 100
44-
set(value) {
45-
field = maxOf(0, value)
46-
}
47-
48-
private val limit: Int =
49-
when(recursion) {
50-
1 -> Int.MAX_VALUE
51-
else -> 1
52-
}
49+
return buildList {
5350

54-
override fun generate(description: FuzzedMethodDescription): Sequence<FuzzedParameter> = sequence {
55-
val fuzzedValues = with(description) {
56-
parameters.asSequence()
57-
.filterNot { it == stringClassId || it.isPrimitiveWrapper }
58-
.flatMap { classId ->
59-
collectConstructors(classId) { javaConstructor ->
60-
isAccessible(javaConstructor, description.packageName)
61-
}.sortedWith(
62-
primitiveParameterizedConstructorsFirstAndThenByParameterCount
63-
).take(limit)
64-
}
65-
.associateWith { constructorId ->
66-
fuzzParameters(
67-
constructorId,
68-
generateRecursiveProvider()
51+
constructors.forEach { constructorId ->
52+
with(constructorId) {
53+
add(
54+
ModelConstructor(parameters) { assembleModel(idGenerator.createId(), constructorId, it) }
6955
)
70-
}
71-
.asSequence()
72-
.flatMap { (constructorId, fuzzedParameters) ->
73-
if (constructorId.parameters.isEmpty()) {
74-
sequenceOf(assembleModel(idGenerator.createId(), constructorId, emptyList())) +
75-
generateModelsWithFieldsInitialization(constructorId, description)
76-
}
77-
else {
78-
fuzzedParameters.map { params ->
79-
assembleModel(idGenerator.createId(), constructorId, params)
56+
if (parameters.isEmpty()) {
57+
val fields = findSuitableFields(classId, description)
58+
if (fields.isNotEmpty()) {
59+
add(
60+
ModelConstructor(fields.map { it.classId }) {
61+
generateModelsWithFieldsInitialization(this, fields, it)
62+
}
63+
)
8064
}
8165
}
8266
}
83-
}
84-
85-
fuzzedValues.forEach { fuzzedValue ->
86-
description.parametersMap[fuzzedValue.model.classId]?.forEach { index ->
87-
yieldValue(index, fuzzedValue)
8867
}
68+
69+
//add(ModelConstructor(listOf()) { UtNullModel(clazz).fuzzed {}})
8970
}
9071
}
9172

92-
private fun generateModelsWithFieldsInitialization(constructorId: ConstructorId, description: FuzzedMethodDescription): Sequence<FuzzedValue> {
93-
if (limitValuesCreatedByFieldAccessors == 0) return emptySequence()
94-
val fields = findSuitableFields(constructorId.classId, description)
95-
96-
val fieldValuesSets = fuzzValuesRecursively(
97-
types = fields.map { it.classId },
98-
baseMethodDescription = description,
99-
modelProvider = generateRecursiveProvider(),
100-
generatedValuesName = "${constructorId.classId.simpleName} fields"
101-
).take(limitValuesCreatedByFieldAccessors) // limit the number of fuzzed values in this particular case
102-
103-
return fieldValuesSets
104-
.map { fieldValues ->
105-
val fuzzedModel = assembleModel(idGenerator.createId(), constructorId, emptyList())
106-
val assembleModel = fuzzedModel.model as? UtAssembleModel ?: error("Expected UtAssembleModel but ${fuzzedModel.model::class.java} found")
107-
val modificationChain = assembleModel.modificationsChain as? MutableList ?: error("Modification chain must be mutable")
108-
fieldValues.asSequence().mapIndexedNotNull { index, value ->
109-
val field = fields[index]
110-
when {
111-
field.canBeSetDirectly -> UtDirectSetFieldModel(
112-
fuzzedModel.model,
113-
FieldId(constructorId.classId, field.name),
114-
value.model
115-
)
116-
field.setter != null -> UtExecutableCallModel(
117-
fuzzedModel.model,
118-
MethodId(constructorId.classId, field.setter.name, field.setter.returnType.id, listOf(field.classId)),
119-
listOf(value.model)
120-
)
121-
else -> null
122-
}
123-
}.forEach(modificationChain::add)
124-
fuzzedModel
73+
private fun generateModelsWithFieldsInitialization(
74+
constructorId: ConstructorId,
75+
fields: List<FieldDescription>,
76+
fieldValues: List<FuzzedValue>
77+
): FuzzedValue {
78+
val fuzzedModel = assembleModel(idGenerator.createId(), constructorId, emptyList())
79+
val assembleModel = fuzzedModel.model as? UtAssembleModel
80+
?: error("Expected UtAssembleModel but ${fuzzedModel.model::class.java} found")
81+
val modificationChain =
82+
assembleModel.modificationsChain as? MutableList ?: error("Modification chain must be mutable")
83+
fieldValues.asSequence().mapIndexedNotNull { index, value ->
84+
val field = fields[index]
85+
when {
86+
field.canBeSetDirectly -> UtDirectSetFieldModel(
87+
fuzzedModel.model,
88+
FieldId(constructorId.classId, field.name),
89+
value.model
90+
)
91+
field.setter != null -> UtExecutableCallModel(
92+
fuzzedModel.model,
93+
MethodId(
94+
constructorId.classId,
95+
field.setter.name,
96+
field.setter.returnType.id,
97+
listOf(field.classId)
98+
),
99+
listOf(value.model)
100+
)
101+
else -> null
125102
}
103+
}.forEach(modificationChain::add)
104+
return fuzzedModel
126105
}
127106

128107
companion object {
@@ -146,21 +125,6 @@ class ObjectModelProvider(
146125
return !hasAnyAccessModifier
147126
}
148127

149-
private fun FuzzedMethodDescription.fuzzParameters(constructorId: ConstructorId, vararg modelProviders: ModelProvider): Sequence<List<FuzzedValue>> {
150-
val fuzzedMethod = FuzzedMethodDescription(
151-
executableId = constructorId,
152-
concreteValues = this.concreteValues
153-
).apply {
154-
this.packageName = this@fuzzParameters.packageName
155-
}
156-
return try {
157-
fuzz(fuzzedMethod, *modelProviders)
158-
} catch (t: TooManyCombinationsException) {
159-
logger.warn(t) { "Number of combination of ${parameters.size} parameters is huge. Fuzzing is skipped for $name" }
160-
emptySequence()
161-
}
162-
}
163-
164128
private fun assembleModel(id: Int, constructorId: ConstructorId, params: List<FuzzedValue>): FuzzedValue {
165129
val instantiationChain = mutableListOf<UtStatementModel>()
166130
return UtAssembleModel(
@@ -221,4 +185,4 @@ class ObjectModelProvider(
221185
val setter: Method?,
222186
)
223187
}
224-
}
188+
}

0 commit comments

Comments
 (0)