Skip to content

Commit 1ba00d4

Browse files
authored
Other class is not mocked as required #747 (#1033)
1 parent 312e347 commit 1ba00d4

File tree

8 files changed

+275
-25
lines changed

8 files changed

+275
-25
lines changed

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class UtBotSymbolicEngine(
158158
private val methodUnderTest: ExecutableId,
159159
classpath: String,
160160
dependencyPaths: String,
161-
mockStrategy: MockStrategy = NO_MOCKS,
161+
val mockStrategy: MockStrategy = NO_MOCKS,
162162
chosenClassesToMockAlways: Set<ClassId>,
163163
private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis
164164
) : UtContextInitializer() {
@@ -429,6 +429,7 @@ class UtBotSymbolicEngine(
429429
val names = graph.body.method.tags.filterIsInstance<ParamNamesTag>().firstOrNull()?.names
430430
parameterNameMap = { index -> names?.getOrNull(index) }
431431
fuzzerType = { try { toFuzzerType(methodUnderTest.executable.genericParameterTypes[it]) } catch (_: Throwable) { null } }
432+
shouldMock = { mockStrategy.eligibleToMock(it, classUnderTest) }
432433
}
433434
val coveredInstructionTracker = Trie(Instruction::id)
434435
val coveredInstructionValues = linkedMapOf<Trie.Node<Instruction>, List<FuzzedValue>>()

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

+5
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ class FuzzedMethodDescription(
5252
*/
5353
var fuzzerType: (Int) -> FuzzedType? = { null }
5454

55+
/**
56+
* Returns true if class should be mocked.
57+
*/
58+
var shouldMock: (ClassId) -> Boolean = { false }
59+
5560
/**
5661
* Map class id to indices of this class in parameters list.
5762
*/

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

+12-15
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,19 @@ import org.utbot.framework.plugin.api.UtModel
55
/**
66
* Fuzzed Value stores information about concrete UtModel, reference to [ModelProvider]
77
* and reasons about why this value was generated.
8+
*
9+
* [summary] is a piece of useful information that clarify why this value has a concrete value.
10+
*
11+
* It supports a special character `%var%` that is used as a placeholder for parameter name.
12+
*
13+
* For example:
14+
* 1. `%var% = 2` for a value that have value 2
15+
* 2. `%var% >= 4` for a value that shouldn't be less than 4
16+
* 3. `foo(%var%) returns true` for values that should be passed as a function parameter
17+
* 4. `%var% has special characters` to describe content
818
*/
919
open class FuzzedValue(
1020
val model: UtModel,
1121
val createdBy: ModelProvider? = null,
12-
) {
13-
14-
/**
15-
* Summary is a piece of useful information that clarify why this value has a concrete value.
16-
*
17-
* It supports a special character `%var%` that is used as a placeholder for parameter name.
18-
*
19-
* For example:
20-
* 1. `%var% = 2` for a value that have value 2
21-
* 2. `%var% >= 4` for a value that shouldn't be less than 4
22-
* 3. `foo(%var%) returns true` for values that should be passed as a function parameter
23-
* 4. `%var% has special characters` to describe content
24-
*/
25-
var summary: String? = null
26-
}
22+
var summary: String? = null,
23+
)

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import org.utbot.framework.plugin.api.util.voidClassId
77
import org.utbot.fuzzer.mutators.NumberRandomMutator
88
import org.utbot.fuzzer.mutators.RegexStringModelMutator
99
import org.utbot.fuzzer.mutators.StringRandomMutator
10+
import org.utbot.fuzzer.objects.replaceWithMock
1011
import org.utbot.fuzzer.providers.ArrayModelProvider
1112
import org.utbot.fuzzer.providers.CharToStringModelProvider
1213
import org.utbot.fuzzer.providers.CollectionWithEmptyStatesModelProvider
@@ -117,7 +118,10 @@ fun fuzz(description: FuzzedMethodDescription, vararg modelProviders: ModelProvi
117118
val values = List<MutableList<FuzzedValue>>(description.parameters.size) { mutableListOf() }
118119
modelProviders.forEach { fuzzingProvider ->
119120
fuzzingProvider.generate(description).forEach { (index, model) ->
120-
values[index].add(model)
121+
val mock = replaceWithMock(model.model, description.shouldMock)
122+
values[index].add(FuzzedValue(mock, model.createdBy).apply {
123+
summary = model.summary
124+
})
121125
}
122126
}
123127
description.parameters.forEachIndexed { index, classId ->

utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/AssembleModelUtils.kt

+18
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package org.utbot.fuzzer.objects
33
import org.utbot.framework.plugin.api.ClassId
44
import org.utbot.framework.plugin.api.ConstructorId
55
import org.utbot.framework.plugin.api.ExecutableId
6+
import org.utbot.framework.plugin.api.FieldId
67
import org.utbot.framework.plugin.api.MethodId
78
import org.utbot.framework.plugin.api.UtAssembleModel
9+
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
810
import org.utbot.framework.plugin.api.UtExecutableCallModel
911
import org.utbot.framework.plugin.api.UtModel
1012
import org.utbot.framework.plugin.api.UtStatementModel
@@ -38,6 +40,7 @@ class AssembleModelDsl internal constructor(
3840
val call = KeyWord.Call
3941
val constructor = KeyWord.Constructor(classId)
4042
val method = KeyWord.Method(classId)
43+
val field = KeyWord.Field(classId)
4144

4245
var id: () -> Int? = { null }
4346
var name: (Int?) -> String = { "<dsl generated model>" }
@@ -53,10 +56,15 @@ class AssembleModelDsl internal constructor(
5356

5457
infix fun <T : ExecutableId> KeyWord.Call.instance(executableId: T) = CallDsl(executableId, false)
5558

59+
infix fun <T : FieldId> KeyWord.Call.instance(field: T) = FieldDsl(field, false)
60+
5661
infix fun <T : ExecutableId> KeyWord.Using.static(executableId: T) = UsingDsl(executableId)
5762

5863
infix fun <T : ExecutableId> KeyWord.Call.static(executableId: T) = CallDsl(executableId, true)
5964

65+
infix fun <T : FieldId> KeyWord.Call.static(field: T) = FieldDsl(field, true)
66+
67+
@Suppress("UNUSED_PARAMETER")
6068
infix fun KeyWord.Using.empty(ignored: KeyWord.Constructor) {
6169
initialization = { UtExecutableCallModel(null, ConstructorId(classId, emptyList()), emptyList()) }
6270
}
@@ -73,6 +81,10 @@ class AssembleModelDsl internal constructor(
7381
modChain += { UtExecutableCallModel(it, executableId, models.toList()) }
7482
}
7583

84+
infix fun FieldDsl.with(model: UtModel) {
85+
modChain += { UtDirectSetFieldModel(it, fieldId, model) }
86+
}
87+
7688
internal fun build(): UtAssembleModel {
7789
val objectId = id()
7890
return UtAssembleModel(
@@ -102,8 +114,14 @@ class AssembleModelDsl internal constructor(
102114
return MethodId(classId, name, returns, params)
103115
}
104116
}
117+
class Field(val classId: ClassId) : KeyWord() {
118+
operator fun invoke(name: String): FieldId {
119+
return FieldId(classId, name)
120+
}
121+
}
105122
}
106123

107124
class UsingDsl(val executableId: ExecutableId)
108125
class CallDsl(val executableId: ExecutableId, val isStatic: Boolean)
126+
class FieldDsl(val fieldId: FieldId, val isStatic: Boolean)
109127
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.utbot.fuzzer.objects
2+
3+
import org.utbot.framework.plugin.api.ClassId
4+
import org.utbot.framework.plugin.api.ExecutableId
5+
import org.utbot.framework.plugin.api.MethodId
6+
import org.utbot.framework.plugin.api.UtAssembleModel
7+
import org.utbot.framework.plugin.api.UtCompositeModel
8+
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
9+
import org.utbot.framework.plugin.api.UtExecutableCallModel
10+
import org.utbot.framework.plugin.api.UtModel
11+
import org.utbot.framework.plugin.api.UtStatementModel
12+
13+
/**
14+
* Implements [MethodId] but also can supply a mock for this execution.
15+
*
16+
* Simplest example: setter and getter,
17+
* when this methodId is a setter, getter can be used for a mock to supply correct value.
18+
*/
19+
internal class FuzzerMockableMethodId(
20+
classId: ClassId,
21+
name: String,
22+
returnType: ClassId,
23+
parameters: List<ClassId>,
24+
val mock: () -> Map<ExecutableId, List<UtModel>> = { emptyMap() },
25+
) : MethodId(classId, name, returnType, parameters) {
26+
27+
constructor(copyOf: MethodId, mock: () -> Map<ExecutableId, List<UtModel>> = { emptyMap() }) : this(
28+
copyOf.classId, copyOf.name, copyOf.returnType, copyOf.parameters, mock
29+
)
30+
31+
}
32+
33+
internal fun MethodId.toFuzzerMockable(block: suspend SequenceScope<Pair<MethodId, List<UtModel>>>.() -> Unit): FuzzerMockableMethodId {
34+
return FuzzerMockableMethodId(this) {
35+
sequence { block() }.toMap()
36+
}
37+
}
38+
39+
internal fun replaceWithMock(assembleModel: UtModel, shouldMock: (ClassId) -> Boolean): UtModel = when {
40+
assembleModel !is UtAssembleModel -> assembleModel
41+
shouldMock(assembleModel.classId) -> createMockModelFromFuzzerMockable(assembleModel, shouldMock)
42+
else -> updateInnerModels(assembleModel, shouldMock)
43+
}
44+
45+
private fun createMockModelFromFuzzerMockable(model: UtAssembleModel, shouldMock: (ClassId) -> Boolean): UtCompositeModel {
46+
val mock = UtCompositeModel(model.id, model.classId, true)
47+
for (mutator in model.modificationsChain) {
48+
if (mutator is UtDirectSetFieldModel) {
49+
mock.fields[mutator.fieldId] = replaceWithMock(mutator.fieldModel, shouldMock)
50+
}
51+
if (mutator is UtExecutableCallModel && mutator.executable is FuzzerMockableMethodId) {
52+
(mutator.executable as FuzzerMockableMethodId).mock().forEach { (executionId, models) ->
53+
mock.mocks[executionId] = models.map { p -> replaceWithMock(p, shouldMock) }
54+
}
55+
}
56+
}
57+
return mock
58+
}
59+
60+
private fun updateInnerModels(model: UtAssembleModel, shouldMock: (ClassId) -> Boolean): UtAssembleModel {
61+
val models = model.modificationsChain.map { call ->
62+
var mockedStatementModel: UtStatementModel? = null
63+
when (call) {
64+
is UtDirectSetFieldModel -> {
65+
val mock = replaceWithMock(call.fieldModel, shouldMock)
66+
if (mock != call.fieldModel) {
67+
mockedStatementModel = UtDirectSetFieldModel(call.instance, call.fieldId, mock)
68+
}
69+
}
70+
is UtExecutableCallModel -> {
71+
val params = call.params.map { m -> replaceWithMock(m, shouldMock) }
72+
if (params != call.params) {
73+
mockedStatementModel = UtExecutableCallModel(call.instance, call.executable, params)
74+
}
75+
}
76+
}
77+
mockedStatementModel ?: call
78+
}
79+
return with(model) {
80+
UtAssembleModel(id, classId, modelName, instantiationCall, origin) { models }
81+
}
82+
}

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

+28-8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import org.utbot.fuzzer.FuzzedMethodDescription
2727
import org.utbot.fuzzer.FuzzedType
2828
import org.utbot.fuzzer.FuzzedValue
2929
import org.utbot.fuzzer.IdentityPreservingIdGenerator
30+
import org.utbot.fuzzer.objects.FuzzerMockableMethodId
3031
import org.utbot.fuzzer.objects.assembleModel
3132

3233
/**
@@ -101,11 +102,22 @@ class ObjectModelProvider(
101102
)
102103
field.setter != null -> UtExecutableCallModel(
103104
fuzzedModel.model,
104-
MethodId(
105+
FuzzerMockableMethodId(
105106
constructorId.classId,
106107
field.setter.name,
107108
field.setter.returnType.id,
108-
listOf(field.classId)
109+
listOf(field.classId),
110+
mock = {
111+
field.getter?.let { g ->
112+
val getterMethodID = MethodId(
113+
classId = constructorId.classId,
114+
name = g.name,
115+
returnType = g.returnType.id,
116+
parameters = emptyList()
117+
)
118+
mapOf(getterMethodID to listOf(value.model))
119+
} ?: emptyMap()
120+
}
109121
),
110122
listOf(value.model)
111123
)
@@ -144,16 +156,23 @@ class ObjectModelProvider(
144156
private fun findSuitableFields(classId: ClassId, description: FuzzedMethodDescription): List<FieldDescription> {
145157
val jClass = classId.jClass
146158
return jClass.declaredFields.map { field ->
159+
val setterAndGetter = jClass.findPublicSetterGetterIfHasPublicGetter(field, description)
147160
FieldDescription(
148-
field.name,
149-
field.type.id,
150-
isAccessible(field, description.packageName) && !isFinal(field.modifiers) && !isStatic(field.modifiers),
151-
jClass.findPublicSetterIfHasPublicGetter(field, description)
161+
name = field.name,
162+
classId = field.type.id,
163+
canBeSetDirectly = isAccessible(field, description.packageName) && !isFinal(field.modifiers) && !isStatic(field.modifiers),
164+
setter = setterAndGetter?.setter,
165+
getter = setterAndGetter?.getter,
152166
)
153167
}
154168
}
155169

156-
private fun Class<*>.findPublicSetterIfHasPublicGetter(field: Field, description: FuzzedMethodDescription): Method? {
170+
private class PublicSetterGetter(
171+
val setter: Method,
172+
val getter: Method,
173+
)
174+
175+
private fun Class<*>.findPublicSetterGetterIfHasPublicGetter(field: Field, description: FuzzedMethodDescription): PublicSetterGetter? {
157176
val postfixName = field.name.capitalize()
158177
val setterName = "set$postfixName"
159178
val getterName = "get$postfixName"
@@ -164,7 +183,7 @@ class ObjectModelProvider(
164183
it.name == setterName &&
165184
it.parameterCount == 1 &&
166185
it.parameterTypes[0] == field.type
167-
}
186+
}?.let { PublicSetterGetter(it, getter) }
168187
} else {
169188
null
170189
}
@@ -184,6 +203,7 @@ class ObjectModelProvider(
184203
val classId: ClassId,
185204
val canBeSetDirectly: Boolean,
186205
val setter: Method?,
206+
val getter: Method?
187207
)
188208
}
189209
}

0 commit comments

Comments
 (0)