Skip to content

Commit c15e554

Browse files
committed
Support mock strategies and type replacement in Spring unit test fuzzing
1 parent dd787d0 commit c15e554

File tree

15 files changed

+134
-49
lines changed

15 files changed

+134
-49
lines changed

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

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

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,6 @@ class UtBotSymbolicEngine(
424424
* Run fuzzing flow.
425425
*
426426
* @param until is used by fuzzer to cancel all tasks if the current time is over this value
427-
* @param transform provides model values for a method
428427
*/
429428
fun fuzzing(until: Long = Long.MAX_VALUE) = flow {
430429
val isFuzzable = methodUnderTest.parameters.all { classId ->

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

Lines changed: 2 additions & 0 deletions
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

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

Lines changed: 1 addition & 2 deletions
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
}

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

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,48 @@ package org.utbot.framework.context.custom
33
import org.utbot.framework.context.JavaFuzzingContext
44
import org.utbot.framework.plugin.api.ExecutableId
55
import org.utbot.fuzzing.JavaValueProvider
6+
import org.utbot.fuzzing.providers.AnyDepthNullValueProvider
67
import org.utbot.fuzzing.providers.MapValueProvider
78
import org.utbot.fuzzing.spring.unit.MockValueProvider
89
import org.utbot.fuzzing.providers.NullValueProvider
910
import org.utbot.fuzzing.providers.ObjectValueProvider
1011
import org.utbot.fuzzing.providers.StringValueProvider
12+
import org.utbot.fuzzing.providers.anyObjectValueProvider
13+
import org.utbot.fuzzing.spring.decorators.filterTypes
1114
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult
1215

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

2426
class MockingJavaFuzzingContext(
25-
val delegateContext: JavaFuzzingContext
27+
val delegateContext: JavaFuzzingContext,
2628
) : JavaFuzzingContext by delegateContext {
2729
private val mockValueProvider = MockValueProvider(delegateContext.idGenerator)
2830

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

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

Lines changed: 2 additions & 0 deletions
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 =

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

Lines changed: 1 addition & 2 deletions
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
}

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

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

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

Lines changed: 6 additions & 8 deletions
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
}

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

Lines changed: 7 additions & 13 deletions
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
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.utbot.fuzzing.spring.decorators
2+
3+
import org.utbot.fuzzing.Description
4+
import org.utbot.fuzzing.ValueProvider
5+
6+
fun <T, R, D : Description<T>> ValueProvider<T, R, D>.filterTypes(predicate: (T) -> Boolean) =
7+
FilteredValueProvider(delegate = this, predicate)
8+
9+
class FilteredValueProvider<T, R, D : Description<T>>(
10+
delegate: ValueProvider<T, R, D>,
11+
private val predicate: (T) -> Boolean
12+
) : ValueProviderDecorator<T, R, D>(delegate) {
13+
override fun wrap(provider: ValueProvider<T, R, D>): ValueProvider<T, R, D> =
14+
provider.filterTypes(predicate)
15+
16+
override fun accept(type: T): Boolean =
17+
predicate(type) && super.accept(type)
18+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.utbot.fuzzing.spring.decorators
2+
3+
import org.utbot.fuzzing.Description
4+
import org.utbot.fuzzing.Scope
5+
import org.utbot.fuzzing.Seed
6+
import org.utbot.fuzzing.ValueProvider
7+
8+
fun <T, R, D : Description<T>> ValueProvider<T, R, D>.replaceTypes(typeReplacer: (D, T) -> T) =
9+
TypeReplacingValueProvider(delegate = this, typeReplacer)
10+
11+
class TypeReplacingValueProvider<T, R, D : Description<T>>(
12+
delegate: ValueProvider<T, R, D>,
13+
private val typeReplacer: (D, T) -> T
14+
) : ValueProviderDecorator<T, R, D>(delegate) {
15+
override fun wrap(provider: ValueProvider<T, R, D>): ValueProvider<T, R, D> =
16+
provider.replaceTypes(typeReplacer)
17+
18+
override fun enrich(description: D, type: T, scope: Scope) =
19+
super.enrich(description, typeReplacer(description, type), scope)
20+
21+
override fun accept(type: T): Boolean = true
22+
23+
override fun generate(description: D, type: T): Sequence<Seed<T, R>> =
24+
if (super.accept(typeReplacer(description, type)))
25+
super.generate(description, typeReplacer(description, type))
26+
else
27+
emptySequence()
28+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.utbot.fuzzing.spring.decorators
2+
3+
import org.utbot.fuzzing.Description
4+
import org.utbot.fuzzing.Scope
5+
import org.utbot.fuzzing.Seed
6+
import org.utbot.fuzzing.ValueProvider
7+
8+
abstract class ValueProviderDecorator<T, R, D : Description<T>>(
9+
protected val delegate: ValueProvider<T, R, D>
10+
) : ValueProvider<T, R, D> {
11+
protected abstract fun wrap(provider: ValueProvider<T, R, D>): ValueProvider<T, R, D>
12+
13+
override fun enrich(description: D, type: T, scope: Scope) =
14+
delegate.enrich(description, type, scope)
15+
16+
override fun accept(type: T): Boolean =
17+
delegate.accept(type)
18+
19+
override fun generate(description: D, type: T): Sequence<Seed<T, R>> =
20+
delegate.generate(description, type)
21+
22+
override fun except(filter: (ValueProvider<T, R, D>) -> Boolean): ValueProvider<T, R, D> {
23+
val res = wrap(delegate.except(filter))
24+
return if (filter(res)) ValueProvider.of(emptyList()) else res
25+
}
26+
27+
override fun map(transform: (ValueProvider<T, R, D>) -> ValueProvider<T, R, D>): ValueProvider<T, R, D> =
28+
transform(wrap(delegate.map(transform)))
29+
}

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import org.utbot.framework.context.NonNullSpeculator
1212
import org.utbot.framework.context.TypeReplacer
1313
import org.utbot.framework.context.custom.CoverageFilteringConcreteExecutionContext
1414
import org.utbot.framework.context.custom.RerunningConcreteExecutionContext
15-
import org.utbot.framework.context.custom.mockAllTypesWithoutSpecificValueProvider
15+
import org.utbot.framework.context.custom.allowMocks
1616
import org.utbot.framework.context.utils.transformJavaFuzzingContext
17-
import org.utbot.framework.context.utils.withValueProvider
17+
import org.utbot.framework.context.utils.transformValueProvider
1818
import org.utbot.framework.plugin.api.BeanDefinitionData
1919
import org.utbot.framework.plugin.api.ClassId
2020
import org.utbot.framework.plugin.api.ConcreteContextLoadingResult
@@ -25,7 +25,9 @@ import org.utbot.framework.plugin.api.util.allSuperTypes
2525
import org.utbot.framework.plugin.api.util.id
2626
import org.utbot.framework.plugin.api.util.jClass
2727
import org.utbot.framework.plugin.api.util.utContext
28+
import org.utbot.fuzzing.spring.decorators.replaceTypes
2829
import org.utbot.fuzzing.spring.unit.InjectMockValueProvider
30+
import org.utbot.fuzzing.toFuzzerType
2931

3032
class SpringApplicationContextImpl(
3133
private val delegateContext: ApplicationContext,
@@ -65,13 +67,20 @@ class SpringApplicationContextImpl(
6567
return when (springTestType) {
6668
SpringTestType.UNIT_TEST -> delegateConcreteExecutionContext.transformJavaFuzzingContext { fuzzingContext ->
6769
fuzzingContext
68-
.withValueProvider(
70+
.allowMocks()
71+
.transformValueProvider { origValueProvider ->
6972
InjectMockValueProvider(
7073
idGenerator = fuzzingContext.idGenerator,
7174
classToUseCompositeModelFor = fuzzingContext.classUnderTest
7275
)
73-
)
74-
.mockAllTypesWithoutSpecificValueProvider()
76+
.withFallback(origValueProvider)
77+
.replaceTypes { description, type ->
78+
typeReplacer.replaceTypeIfNeeded(type.classId)?.let { replacement ->
79+
// TODO infer generic type
80+
toFuzzerType(replacement.jClass, description.typeCache)
81+
} ?: type
82+
}
83+
}
7584
}
7685
SpringTestType.INTEGRATION_TEST ->
7786
RerunningConcreteExecutionContext(

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

Lines changed: 4 additions & 6 deletions
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
}

0 commit comments

Comments
 (0)