Skip to content

Commit 701fb39

Browse files
committed
Support mock strategies and type replacement in Spring unit test fuzzing
1 parent 0587302 commit 701fb39

19 files changed

+161
-53
lines changed

Diff for: utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1397,7 +1397,7 @@ class Traverser(
13971397
// from Spring bean definitions, for example), we can just create a symbolic object
13981398
// with hard constraint on the mentioned type.
13991399
val replacedClassId = when (typeReplacer.typeReplacementMode) {
1400-
KnownImplementor -> typeReplacer.replaceTypeIfNeeded(type)
1400+
KnownImplementor -> typeReplacer.replaceTypeIfNeeded(type.id)
14011401
AnyImplementor,
14021402
NoImplementors -> null
14031403
}

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,6 @@ class UtBotSymbolicEngine(
423423
* Run fuzzing flow.
424424
*
425425
* @param until is used by fuzzer to cancel all tasks if the current time is over this value
426-
* @param transform provides model values for a method
427426
*/
428427
fun fuzzing(until: Long = Long.MAX_VALUE) = flow {
429428
val isFuzzable = methodUnderTest.parameters.all { classId ->
@@ -440,7 +439,12 @@ class UtBotSymbolicEngine(
440439
var testEmittedByFuzzer = 0
441440

442441
val fuzzingContext = try {
443-
concreteExecutionContext.tryCreateFuzzingContext(concreteExecutor, classUnderTest, defaultIdGenerator)
442+
concreteExecutionContext.tryCreateFuzzingContext(
443+
concreteExecutor,
444+
classUnderTest,
445+
mockStrategy,
446+
defaultIdGenerator,
447+
)
444448
} catch (e: Exception) {
445449
emit(UtError(e.message ?: "Failed to create ValueProvider", e))
446450
return@flow

Diff for: utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utbot.framework.context
22

3+
import org.utbot.engine.MockStrategy
34
import org.utbot.framework.plugin.api.ClassId
45
import org.utbot.framework.plugin.api.ConcreteContextLoadingResult
56
import org.utbot.framework.plugin.api.UtExecution
@@ -23,6 +24,7 @@ interface ConcreteExecutionContext {
2324
fun tryCreateFuzzingContext(
2425
concreteExecutor: ConcreteExecutor<UtConcreteExecutionResult, UtExecutionInstrumentation>,
2526
classUnderTest: ClassId,
27+
mockStrategy: MockStrategy,
2628
idGenerator: IdentityPreservingIdGenerator<Int>,
2729
): JavaFuzzingContext
2830
}

Diff for: utbot-framework/src/main/kotlin/org/utbot/framework/context/JavaFuzzingContext.kt

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utbot.framework.context
22

3+
import org.utbot.engine.MockStrategy
34
import org.utbot.framework.plugin.api.ClassId
45
import org.utbot.framework.plugin.api.EnvironmentModels
56
import org.utbot.framework.plugin.api.ExecutableId
@@ -11,6 +12,7 @@ import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionRe
1112

1213
interface JavaFuzzingContext {
1314
val classUnderTest: ClassId
15+
val mockStrategy: MockStrategy
1416
val idGenerator: IdentityPreservingIdGenerator<Int>
1517
val valueProvider: JavaValueProvider
1618

Diff for: utbot-framework/src/main/kotlin/org/utbot/framework/context/TypeReplacer.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package org.utbot.framework.context
22

33
import org.utbot.framework.plugin.api.ClassId
44
import org.utbot.framework.plugin.api.TypeReplacementMode
5-
import soot.RefType
65

76
interface TypeReplacer {
87
/**
@@ -14,5 +13,5 @@ interface TypeReplacer {
1413
* Finds a type to replace the original abstract type
1514
* if it is guided with some additional information.
1615
*/
17-
fun replaceTypeIfNeeded(type: RefType): ClassId?
16+
fun replaceTypeIfNeeded(classId: ClassId): ClassId?
1817
}

Diff for: utbot-framework/src/main/kotlin/org/utbot/framework/context/custom/MockingJavaFuzzingContext.kt

+20-10
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,48 @@ package org.utbot.framework.context.custom
22

33
import org.utbot.framework.context.JavaFuzzingContext
44
import org.utbot.fuzzing.JavaValueProvider
5+
import org.utbot.fuzzing.providers.AnyDepthNullValueProvider
56
import org.utbot.fuzzing.providers.MapValueProvider
67
import org.utbot.fuzzing.spring.unit.MockValueProvider
78
import org.utbot.fuzzing.providers.NullValueProvider
89
import org.utbot.fuzzing.providers.ObjectValueProvider
910
import org.utbot.fuzzing.providers.StringValueProvider
11+
import org.utbot.fuzzing.providers.anyObjectValueProvider
12+
import org.utbot.fuzzing.spring.decorators.filterTypes
1013
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult
1114

1215
/**
13-
* Makes fuzzer mock all types that don't have *specific* [JavaValueProvider],
14-
* like [MapValueProvider] or [StringValueProvider].
16+
* Allows fuzzer to use mocks in accordance with [JavaFuzzingContext.mockStrategy].
1517
*
16-
* NOTE: the caller is responsible for providing some *specific* [JavaValueProvider]
17-
* that can create values for class under test (otherwise it will be mocked),
18-
* [ObjectValueProvider] and [NullValueProvider] do not count as *specific*.
18+
* NOTE:
19+
* - fuzzer won't mock types, that have *specific* value providers (e.g. [MapValueProvider] and [StringValueProvider])
20+
* - [ObjectValueProvider] and [NullValueProvider] do not count as *specific* value providers
1921
*/
20-
fun JavaFuzzingContext.mockAllTypesWithoutSpecificValueProvider() =
22+
fun JavaFuzzingContext.allowMocks() =
2123
MockingJavaFuzzingContext(delegateContext = this)
2224

2325
class MockingJavaFuzzingContext(
24-
val delegateContext: JavaFuzzingContext
26+
val delegateContext: JavaFuzzingContext,
2527
) : JavaFuzzingContext by delegateContext {
2628
private val mockValueProvider = MockValueProvider(delegateContext.idGenerator)
2729

2830
override val valueProvider: JavaValueProvider =
29-
// NOTE: we first remove `NullValueProvider` from `delegateContext.valueProvider` and then
30-
// add it back as a part of our `withFallback` so it has the same priority as
31-
// `mockValueProvider`, otherwise mocks will never be used where `null` can be used.
31+
// NOTE: we first remove `NullValueProvider` and `ObjectValueProvider` from `delegateContext.valueProvider`
32+
// and then add them back as a part of our `withFallback` so they have the same priority as
33+
// `mockValueProvider`, otherwise mocks will never be used where `null` or new object can be used.
3234
delegateContext.valueProvider
3335
.except { it is NullValueProvider }
3436
.except { it is ObjectValueProvider }
3537
.withFallback(
3638
mockValueProvider
39+
.filterTypes { type ->
40+
mockStrategy.eligibleToMock(
41+
classToMock = type.classId,
42+
classUnderTest = classUnderTest
43+
)
44+
}
45+
.with(anyObjectValueProvider(idGenerator))
46+
.withFallback(mockValueProvider.with(AnyDepthNullValueProvider))
3747
.with(NullValueProvider)
3848
)
3949

Diff for: utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleConcreteExecutionContext.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utbot.framework.context.simple
22

3+
import org.utbot.engine.MockStrategy
34
import org.utbot.framework.context.ConcreteExecutionContext
45
import org.utbot.framework.context.JavaFuzzingContext
56
import org.utbot.framework.plugin.api.ClassId
@@ -28,6 +29,7 @@ class SimpleConcreteExecutionContext(fullClassPath: String) : ConcreteExecutionC
2829
override fun tryCreateFuzzingContext(
2930
concreteExecutor: ConcreteExecutor<UtConcreteExecutionResult, UtExecutionInstrumentation>,
3031
classUnderTest: ClassId,
32+
mockStrategy: MockStrategy,
3133
idGenerator: IdentityPreservingIdGenerator<Int>
32-
): JavaFuzzingContext = SimpleJavaFuzzingContext(classUnderTest, idGenerator)
34+
): JavaFuzzingContext = SimpleJavaFuzzingContext(classUnderTest, mockStrategy, idGenerator)
3335
}

Diff for: utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleJavaFuzzingContext.kt

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utbot.framework.context.simple
22

3+
import org.utbot.engine.MockStrategy
34
import org.utbot.framework.context.JavaFuzzingContext
45
import org.utbot.framework.plugin.api.ClassId
56
import org.utbot.framework.plugin.api.EnvironmentModels
@@ -14,6 +15,7 @@ import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionRe
1415

1516
class SimpleJavaFuzzingContext(
1617
override val classUnderTest: ClassId,
18+
override val mockStrategy: MockStrategy,
1719
override val idGenerator: IdentityPreservingIdGenerator<Int>,
1820
) : JavaFuzzingContext {
1921
override val valueProvider: JavaValueProvider =

Diff for: utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleTypeReplacer.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ package org.utbot.framework.context.simple
33
import org.utbot.framework.context.TypeReplacer
44
import org.utbot.framework.plugin.api.ClassId
55
import org.utbot.framework.plugin.api.TypeReplacementMode
6-
import soot.RefType
76

87
class SimpleTypeReplacer : TypeReplacer {
98
override val typeReplacementMode: TypeReplacementMode = TypeReplacementMode.AnyImplementor
109

11-
override fun replaceTypeIfNeeded(type: RefType): ClassId? = null
10+
override fun replaceTypeIfNeeded(classId: ClassId): ClassId? = null
1211
}

Diff for: utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt

+14-5
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import org.utbot.framework.context.ConcreteExecutionContext
1111
import org.utbot.framework.context.NonNullSpeculator
1212
import org.utbot.framework.context.TypeReplacer
1313
import org.utbot.framework.context.custom.CoverageFilteringConcreteExecutionContext
14-
import org.utbot.framework.context.custom.mockAllTypesWithoutSpecificValueProvider
14+
import org.utbot.framework.context.custom.allowMocks
1515
import org.utbot.framework.context.utils.transformJavaFuzzingContext
16-
import org.utbot.framework.context.utils.withValueProvider
16+
import org.utbot.framework.context.utils.transformValueProvider
1717
import org.utbot.framework.plugin.api.BeanDefinitionData
1818
import org.utbot.framework.plugin.api.ClassId
1919
import org.utbot.framework.plugin.api.ConcreteContextLoadingResult
@@ -24,7 +24,9 @@ import org.utbot.framework.plugin.api.util.allSuperTypes
2424
import org.utbot.framework.plugin.api.util.id
2525
import org.utbot.framework.plugin.api.util.jClass
2626
import org.utbot.framework.plugin.api.util.utContext
27+
import org.utbot.fuzzing.spring.decorators.replaceTypes
2728
import org.utbot.fuzzing.spring.unit.InjectMockValueProvider
29+
import org.utbot.fuzzing.toFuzzerType
2830

2931
class SpringApplicationContextImpl(
3032
private val delegateContext: ApplicationContext,
@@ -64,13 +66,20 @@ class SpringApplicationContextImpl(
6466
return when (springTestType) {
6567
SpringTestType.UNIT_TEST -> delegateConcreteExecutionContext.transformJavaFuzzingContext { fuzzingContext ->
6668
fuzzingContext
67-
.withValueProvider(
69+
.allowMocks()
70+
.transformValueProvider { origValueProvider ->
6871
InjectMockValueProvider(
6972
idGenerator = fuzzingContext.idGenerator,
7073
classToUseCompositeModelFor = fuzzingContext.classUnderTest
7174
)
72-
)
73-
.mockAllTypesWithoutSpecificValueProvider()
75+
.withFallback(origValueProvider)
76+
.replaceTypes { description, type ->
77+
typeReplacer.replaceTypeIfNeeded(type.classId)?.let { replacement ->
78+
// TODO infer generic type
79+
toFuzzerType(replacement.jClass, description.typeCache)
80+
} ?: type
81+
}
82+
}
7483
}
7584
SpringTestType.INTEGRATION_TEST -> SpringIntegrationTestConcreteExecutionContext(
7685
delegateConcreteExecutionContext,

Diff for: utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.utbot.framework.context.spring
22

33
import mu.KotlinLogging
4+
import org.utbot.engine.MockStrategy
45
import org.utbot.framework.context.ConcreteExecutionContext
56
import org.utbot.framework.context.JavaFuzzingContext
67
import org.utbot.framework.plugin.api.ClassId
@@ -55,6 +56,7 @@ class SpringIntegrationTestConcreteExecutionContext(
5556
override fun tryCreateFuzzingContext(
5657
concreteExecutor: ConcreteExecutor<UtConcreteExecutionResult, UtExecutionInstrumentation>,
5758
classUnderTest: ClassId,
59+
mockStrategy: MockStrategy,
5860
idGenerator: IdentityPreservingIdGenerator<Int>
5961
): JavaFuzzingContext {
6062
if (springApplicationContext.getBeansAssignableTo(classUnderTest).isEmpty())
@@ -68,7 +70,12 @@ class SpringIntegrationTestConcreteExecutionContext(
6870
logger.info { "Detected relevant repositories for class $classUnderTest: $relevantRepositories" }
6971

7072
return SpringIntegrationTestJavaFuzzingContext(
71-
delegateContext = delegateContext.tryCreateFuzzingContext(concreteExecutor, classUnderTest, idGenerator),
73+
delegateContext = delegateContext.tryCreateFuzzingContext(
74+
concreteExecutor,
75+
classUnderTest,
76+
mockStrategy,
77+
idGenerator,
78+
),
7279
relevantRepositories = relevantRepositories,
7380
springApplicationContext = springApplicationContext,
7481
)

Diff for: utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringTypeReplacer.kt

+4-6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ package org.utbot.framework.context.spring
33
import org.utbot.framework.context.TypeReplacer
44
import org.utbot.framework.plugin.api.ClassId
55
import org.utbot.framework.plugin.api.TypeReplacementMode
6-
import org.utbot.framework.plugin.api.id
7-
import org.utbot.framework.plugin.api.isAbstractType
6+
import org.utbot.framework.plugin.api.util.isAbstract
87
import org.utbot.framework.plugin.api.util.isSubtypeOf
9-
import soot.RefType
108

119
class SpringTypeReplacer(
1210
private val delegateTypeReplacer: TypeReplacer,
@@ -19,7 +17,7 @@ class SpringTypeReplacer(
1917
else
2018
TypeReplacementMode.NoImplementors
2119

22-
override fun replaceTypeIfNeeded(type: RefType): ClassId? =
23-
if (type.isAbstractType) springApplicationContext.injectedTypes.singleOrNull { it.isSubtypeOf(type.id) }
24-
else delegateTypeReplacer.replaceTypeIfNeeded(type)
20+
override fun replaceTypeIfNeeded(classId: ClassId): ClassId? =
21+
if (classId.isAbstract) springApplicationContext.injectedTypes.singleOrNull { it.isSubtypeOf(classId) }
22+
else delegateTypeReplacer.replaceTypeIfNeeded(classId)
2523
}

Diff for: utbot-framework/src/main/kotlin/org/utbot/framework/context/utils/ConcreteExecutionContextUtils.kt

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utbot.framework.context.utils
22

3+
import org.utbot.engine.MockStrategy
34
import org.utbot.framework.context.ConcreteExecutionContext
45
import org.utbot.framework.context.JavaFuzzingContext
56
import org.utbot.framework.plugin.api.ClassId
@@ -15,9 +16,15 @@ fun ConcreteExecutionContext.transformJavaFuzzingContext(
1516
override fun tryCreateFuzzingContext(
1617
concreteExecutor: ConcreteExecutor<UtConcreteExecutionResult, UtExecutionInstrumentation>,
1718
classUnderTest: ClassId,
19+
mockStrategy: MockStrategy,
1820
idGenerator: IdentityPreservingIdGenerator<Int>
1921
): JavaFuzzingContext = transformer(
20-
this@transformJavaFuzzingContext.tryCreateFuzzingContext(concreteExecutor, classUnderTest, idGenerator)
22+
this@transformJavaFuzzingContext.tryCreateFuzzingContext(
23+
concreteExecutor,
24+
classUnderTest,
25+
mockStrategy,
26+
idGenerator,
27+
)
2128
)
2229
}
2330

Diff for: utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/JavaLanguage.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ internal fun FuzzedType.traverseHierarchy(typeCache: MutableMap<Type, FuzzedType
171171
* @param type to be resolved
172172
* @param cache is used to store same [FuzzedType] for same java types
173173
*/
174-
internal fun toFuzzerType(type: Type, cache: MutableMap<Type, FuzzedType>): FuzzedType {
174+
fun toFuzzerType(type: Type, cache: MutableMap<Type, FuzzedType>): FuzzedType {
175175
return toFuzzerType(
176176
type = type,
177177
classId = { t -> toClassId(t, cache) },

Diff for: utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/ModifyingWithMethodsProviderWrapper.kt

+6-8
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import org.utbot.fuzzer.FuzzedValue
99
import org.utbot.fuzzing.FuzzedDescription
1010
import org.utbot.fuzzing.JavaValueProvider
1111
import org.utbot.fuzzing.Routine
12-
import org.utbot.fuzzing.Scope
1312
import org.utbot.fuzzing.Seed
13+
import org.utbot.fuzzing.spring.decorators.ValueProviderDecorator
1414

1515
/**
1616
* Value provider that is a buddy for another provider
@@ -22,8 +22,11 @@ import org.utbot.fuzzing.Seed
2222
*/
2323
class ModifyingWithMethodsProviderWrapper(
2424
private val classUnderTest: ClassId,
25-
private val delegate: JavaValueProvider
26-
) : JavaValueProvider by delegate {
25+
delegate: JavaValueProvider
26+
) : ValueProviderDecorator<FuzzedType, FuzzedValue, FuzzedDescription>(delegate) {
27+
28+
override fun wrap(provider: JavaValueProvider): JavaValueProvider =
29+
ModifyingWithMethodsProviderWrapper(classUnderTest, provider)
2730

2831
override fun generate(description: FuzzedDescription, type: FuzzedType): Sequence<Seed<FuzzedType, FuzzedValue>> =
2932
delegate
@@ -50,9 +53,4 @@ class ModifyingWithMethodsProviderWrapper(
5053
)
5154
} else seed
5255
}
53-
54-
override fun enrich(description: FuzzedDescription, type: FuzzedType, scope: Scope) =
55-
delegate.enrich(description, type, scope)
56-
57-
override fun accept(type: FuzzedType): Boolean = delegate.accept(type)
5856
}

Diff for: utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/spring/PropertyPreservingValueProvider.kt

+7-13
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import org.utbot.fuzzer.FuzzedValue
66
import org.utbot.fuzzing.FuzzedDescription
77
import org.utbot.fuzzing.JavaValueProvider
88
import org.utbot.fuzzing.Routine
9-
import org.utbot.fuzzing.Scope
109
import org.utbot.fuzzing.Seed
10+
import org.utbot.fuzzing.spring.decorators.ValueProviderDecorator
1111

1212
/**
1313
* @see preserveProperties
@@ -25,14 +25,14 @@ interface PreservableFuzzedTypeProperty<T> : FuzzedTypeProperty<T>
2525
fun JavaValueProvider.preserveProperties() : JavaValueProvider =
2626
PropertyPreservingValueProvider(this)
2727

28-
class PropertyPreservingValueProvider(private val delegateProvider: JavaValueProvider) : JavaValueProvider {
29-
override fun enrich(description: FuzzedDescription, type: FuzzedType, scope: Scope) =
30-
delegateProvider.enrich(description, type, scope)
31-
32-
override fun accept(type: FuzzedType): Boolean = delegateProvider.accept(type)
28+
class PropertyPreservingValueProvider(
29+
delegate: JavaValueProvider
30+
) : ValueProviderDecorator<FuzzedType, FuzzedValue, FuzzedDescription>(delegate) {
31+
override fun wrap(provider: JavaValueProvider): JavaValueProvider =
32+
provider.preserveProperties()
3333

3434
override fun generate(description: FuzzedDescription, type: FuzzedType): Sequence<Seed<FuzzedType, FuzzedValue>> {
35-
val delegateSeeds = delegateProvider.generate(description, type)
35+
val delegateSeeds = delegate.generate(description, type)
3636

3737
val preservedProperties = type.properties.entries
3838
.filter { it.property is PreservableFuzzedTypeProperty }
@@ -67,10 +67,4 @@ class PropertyPreservingValueProvider(private val delegateProvider: JavaValuePro
6767
}
6868
}
6969
}
70-
71-
override fun map(transform: (JavaValueProvider) -> JavaValueProvider): JavaValueProvider =
72-
delegateProvider.map(transform).preserveProperties()
73-
74-
override fun except(filter: (JavaValueProvider) -> Boolean): JavaValueProvider =
75-
delegateProvider.except(filter).preserveProperties()
7670
}

0 commit comments

Comments
 (0)