Skip to content

Commit 9b9a0b3

Browse files
committed
Add mock and inject mocks value providers
1 parent e76aaff commit 9b9a0b3

File tree

3 files changed

+122
-0
lines changed
  • utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api
  • utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/spring/unit

3 files changed

+122
-0
lines changed

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,8 @@ data class UtClassRefModel(
498498
* - isMock flag
499499
* - calculated field values (models)
500500
* - mocks for methods with return values
501+
* - [canHaveRedundantOrMissingMocks] flag, which is set to `true` for mocks
502+
* created by fuzzer without knowing which methods will actually be called
501503
*
502504
* [fields] contains non-static fields
503505
*/
@@ -507,6 +509,8 @@ data class UtCompositeModel(
507509
val isMock: Boolean,
508510
val fields: MutableMap<FieldId, UtModel> = mutableMapOf(),
509511
val mocks: MutableMap<ExecutableId, List<UtModel>> = mutableMapOf(),
512+
// TODO handle it in instrumentation & codegen
513+
val canHaveRedundantOrMissingMocks: Boolean = false,
510514
) : UtReferenceModel(id, classId) {
511515
//TODO: SAT-891 - rewrite toString() method
512516
override fun toString() = withToStringThreadLocalReentrancyGuard {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.utbot.fuzzing.spring.unit
2+
3+
import org.utbot.framework.plugin.api.ClassId
4+
import org.utbot.framework.plugin.api.UtCompositeModel
5+
import org.utbot.framework.plugin.api.util.allDeclaredFieldIds
6+
import org.utbot.framework.plugin.api.util.isFinal
7+
import org.utbot.framework.plugin.api.util.isStatic
8+
import org.utbot.framework.plugin.api.util.jField
9+
import org.utbot.fuzzer.FuzzedType
10+
import org.utbot.fuzzer.FuzzedValue
11+
import org.utbot.fuzzer.IdGenerator
12+
import org.utbot.fuzzer.fuzzed
13+
import org.utbot.fuzzing.FuzzedDescription
14+
import org.utbot.fuzzing.JavaValueProvider
15+
import org.utbot.fuzzing.Routine
16+
import org.utbot.fuzzing.Seed
17+
import org.utbot.fuzzing.toFuzzerType
18+
19+
/**
20+
* Models created by this class can be used with `@InjectMock` annotation, because
21+
* they are [UtCompositeModel]s similar to the ones created by the symbolic engine.
22+
*/
23+
class InjectMockValueProvider(
24+
private val idGenerator: IdGenerator<Int>,
25+
private val classToUseCompositeModelFor: ClassId
26+
) : JavaValueProvider {
27+
override fun accept(type: FuzzedType): Boolean = type.classId == classToUseCompositeModelFor
28+
29+
override fun generate(description: FuzzedDescription, type: FuzzedType): Sequence<Seed<FuzzedType, FuzzedValue>> {
30+
val fields = type.classId.allDeclaredFieldIds.filterNot { it.isStatic && it.isFinal }.toList()
31+
return sequenceOf(Seed.Recursive(
32+
construct = Routine.Create(types = fields.map { toFuzzerType(it.jField.genericType, description.typeCache) }) { values ->
33+
emptyFuzzedValue(type.classId).also {
34+
(it.model as UtCompositeModel).fields.putAll(
35+
fields.zip(values).associate { (field, value) -> field to value.model }
36+
)
37+
}
38+
},
39+
empty = Routine.Empty { emptyFuzzedValue(type.classId) }
40+
))
41+
}
42+
43+
private fun emptyFuzzedValue(classId: ClassId) = UtCompositeModel(
44+
id = idGenerator.createId(),
45+
classId = classId,
46+
isMock = false,
47+
).fuzzed { summary = "%var% = ${classId.simpleName}()" }
48+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.utbot.fuzzing.spring.unit
2+
3+
import mu.KotlinLogging
4+
import org.utbot.framework.plugin.api.ClassId
5+
import org.utbot.framework.plugin.api.MethodId
6+
import org.utbot.framework.plugin.api.UtCompositeModel
7+
import org.utbot.framework.plugin.api.util.executableId
8+
import org.utbot.framework.plugin.api.util.jClass
9+
import org.utbot.framework.plugin.api.util.method
10+
import org.utbot.fuzzer.FuzzedType
11+
import org.utbot.fuzzer.FuzzedValue
12+
import org.utbot.fuzzer.IdGenerator
13+
import org.utbot.fuzzer.fuzzed
14+
import org.utbot.fuzzing.FuzzedDescription
15+
import org.utbot.fuzzing.JavaValueProvider
16+
import org.utbot.fuzzing.Routine
17+
import org.utbot.fuzzing.Scope
18+
import org.utbot.fuzzing.ScopeProperty
19+
import org.utbot.fuzzing.Seed
20+
import org.utbot.fuzzing.toFuzzerType
21+
22+
val methodsToMockProperty = ScopeProperty<Set<MethodId>>(
23+
description = "Method ids that can be mocked by `MockValueProvider`"
24+
)
25+
26+
// TODO shouldn't be used for primitives and other "easy" to create types
27+
class MockValueProvider(private val idGenerator: IdGenerator<Int>) : JavaValueProvider {
28+
companion object {
29+
private val logger = KotlinLogging.logger {}
30+
private val loggedMockedMethods = mutableSetOf<MethodId>()
31+
}
32+
33+
private val methodsToMock = mutableSetOf<MethodId>()
34+
35+
override fun enrich(description: FuzzedDescription, type: FuzzedType, scope: Scope) {
36+
val publicMethods = type.classId.jClass.methods.map { it.executableId }
37+
publicMethods.intersect(methodsToMock).takeIf { it.isNotEmpty() }?.let {
38+
scope.putProperty(methodsToMockProperty, it)
39+
}
40+
}
41+
42+
override fun generate(description: FuzzedDescription, type: FuzzedType): Sequence<Seed<FuzzedType, FuzzedValue>> =
43+
sequenceOf(Seed.Recursive(
44+
construct = Routine.Create(types = emptyList()) { emptyMockFuzzedValue(type.classId) },
45+
empty = Routine.Empty { emptyMockFuzzedValue(type.classId) },
46+
modify = (description.scope?.getProperty(methodsToMockProperty)?.asSequence() ?: emptySequence()).map { methodId ->
47+
if (loggedMockedMethods.add(methodId))
48+
logger.info { "Actually mocked $methodId for the first time" }
49+
// TODO accept `List<returnType>` instead of singular `returnType`
50+
Routine.Call(types = listOf(
51+
toFuzzerType(methodId.method.genericReturnType, description.typeCache)
52+
)) { instance, (value) ->
53+
(instance.model as UtCompositeModel).mocks[methodId] = listOf(value.model)
54+
}
55+
}
56+
))
57+
58+
private fun emptyMockFuzzedValue(classId: ClassId) = UtCompositeModel(
59+
id = idGenerator.createId(),
60+
classId = classId,
61+
isMock = true,
62+
canHaveRedundantOrMissingMocks = true,
63+
).fuzzed { summary = "%var% = mock()" }
64+
65+
fun addMockingCandidates(detectedMockingCandidates: Set<MethodId>) =
66+
detectedMockingCandidates.forEach { methodId ->
67+
if (methodsToMock.add(methodId))
68+
logger.info { "Detected that $methodId may need mocking" }
69+
}
70+
}

0 commit comments

Comments
 (0)