Skip to content

Commit cd12c2b

Browse files
IlyaMuravjovEgorkaKulikov
authored andcommitted
Add a prototype of Spring integration tests
1 parent c03c24b commit cd12c2b

File tree

35 files changed

+478
-149
lines changed

35 files changed

+478
-149
lines changed

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,14 @@ class UtLambdaModel(
636636
}
637637
}
638638

639+
class UtAutowiredModel(
640+
override val id: Int?,
641+
override val classId: ClassId,
642+
val beanName: String,
643+
) : UtReferenceModel(
644+
id, classId, modelName = "@Autowired $beanName#$id"
645+
)
646+
639647
/**
640648
* Model for a step to obtain [UtAssembleModel].
641649
*/
@@ -1257,6 +1265,23 @@ open class ApplicationContext(
12571265
): Boolean = field.isFinal || !field.isPublic
12581266
}
12591267

1268+
sealed class TypeReplacementApproach {
1269+
/**
1270+
* Do not replace interfaces and abstract classes with concrete implementors.
1271+
* Use mocking instead of it.
1272+
*/
1273+
object DoNotReplace : TypeReplacementApproach()
1274+
1275+
/**
1276+
* Try to replace interfaces and abstract classes with concrete implementors
1277+
* obtained from bean definitions.
1278+
* If it is impossible, use mocking.
1279+
*
1280+
* Currently used in Spring applications only.
1281+
*/
1282+
class ReplaceIfPossible(val config: String) : TypeReplacementApproach()
1283+
}
1284+
12601285
/**
12611286
* Data we get from Spring application context
12621287
* to manage engine and code generator behaviour.
@@ -1269,6 +1294,7 @@ class SpringApplicationContext(
12691294
staticsMockingIsConfigured: Boolean,
12701295
private val beanQualifiedNames: List<String> = emptyList(),
12711296
private val shouldUseImplementors: Boolean,
1297+
val typeReplacementApproach: TypeReplacementApproach
12721298
): ApplicationContext(mockInstalled, staticsMockingIsConfigured) {
12731299

12741300
companion object {

utbot-framework/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
configurations {
22
fetchSpringAnalyzerJar
3+
fetchInstrumentationJar
34
}
45

56
dependencies {
@@ -41,10 +42,14 @@ dependencies {
4142
implementation project(':utbot-spring-analyzer')
4243

4344
fetchSpringAnalyzerJar project(path: ':utbot-spring-analyzer', configuration: 'springAnalyzerJar')
45+
fetchInstrumentationJar project(path: ':utbot-instrumentation', configuration: 'instrumentationArchive')
4446
}
4547

4648
processResources {
4749
from(configurations.fetchSpringAnalyzerJar) {
4850
into "lib"
4951
}
52+
from(configurations.fetchInstrumentationJar) {
53+
into "lib"
54+
}
5055
}

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ import org.utbot.fuzzer.*
4242
import org.utbot.fuzzing.*
4343
import org.utbot.fuzzing.utils.Trie
4444
import org.utbot.instrumentation.ConcreteExecutor
45+
import org.utbot.instrumentation.instrumentation.Instrumentation
4546
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionData
4647
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult
47-
import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation
4848
import soot.jimple.Stmt
4949
import soot.tagkit.ParamNamesTag
5050
import java.lang.reflect.Method
@@ -105,7 +105,8 @@ class UtBotSymbolicEngine(
105105
dependencyPaths: String,
106106
val mockStrategy: MockStrategy = NO_MOCKS,
107107
chosenClassesToMockAlways: Set<ClassId>,
108-
applicationContext: ApplicationContext,
108+
val applicationContext: ApplicationContext,
109+
executionInstrumentation: Instrumentation<UtConcreteExecutionResult>,
109110
private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis
110111
) : UtContextInitializer() {
111112
private val graph = methodUnderTest.sootMethod.jimpleBody().apply {
@@ -154,7 +155,7 @@ class UtBotSymbolicEngine(
154155

155156
private val concreteExecutor =
156157
ConcreteExecutor(
157-
UtExecutionInstrumentation,
158+
executionInstrumentation,
158159
classpath,
159160
).apply { this.classLoader = utContext.classLoader }
160161

@@ -355,7 +356,18 @@ class UtBotSymbolicEngine(
355356
methodUnderTest,
356357
collectConstantsForFuzzer(graph),
357358
names,
358-
listOf(transform(ValueProvider.of(defaultValueProviders(defaultIdGenerator))))
359+
listOf(transform(ValueProvider.of(defaultValueProviders(defaultIdGenerator)))),
360+
thisInstanceFuzzedTypeWrapper = { fuzzedType ->
361+
when (applicationContext) {
362+
is SpringApplicationContext -> when (applicationContext.typeReplacementApproach) {
363+
// TODO use bean names here, which should be made available here via SpringApplicationContext
364+
// should be fixed together with ("use `springContext.getBean(String)` here" in SpringUtExecutionInstrumentation)
365+
is TypeReplacementApproach.ReplaceIfPossible -> AutowiredFuzzedType(fuzzedType, listOf(fuzzedType.classId.jClass.name))
366+
is TypeReplacementApproach.DoNotReplace -> fuzzedType
367+
}
368+
else -> fuzzedType
369+
}
370+
}
359371
) { thisInstance, descr, values ->
360372
if (thisInstance?.model is UtNullModel) {
361373
// We should not try to run concretely any models with null-this.
@@ -593,7 +605,7 @@ private fun ResolvedModels.constructStateForMethod(methodUnderTest: ExecutableId
593605
return EnvironmentModels(thisInstanceBefore, paramsBefore, statics)
594606
}
595607

596-
private suspend fun ConcreteExecutor<UtConcreteExecutionResult, UtExecutionInstrumentation>.executeConcretely(
608+
private suspend fun ConcreteExecutor<UtConcreteExecutionResult, Instrumentation<UtConcreteExecutionResult>>.executeConcretely(
597609
methodUnderTest: ExecutableId,
598610
stateBefore: EnvironmentModels,
599611
instrumentation: List<UtInstrumentation>,

utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utbot.framework.assemble
22

3+
import mu.KotlinLogging
34
import org.utbot.common.isPrivate
45
import org.utbot.common.isPublic
56
import org.utbot.engine.ResolvedExecution
@@ -55,6 +56,10 @@ import java.util.IdentityHashMap
5556
*/
5657
class AssembleModelGenerator(private val basePackageName: String) {
5758

59+
companion object {
60+
private val logger = KotlinLogging.logger {}
61+
}
62+
5863
//Instantiated models are stored to avoid cyclic references during reference graph analysis
5964
private val instantiatedModels: IdentityHashMap<UtModel, UtReferenceModel> =
6065
IdentityHashMap<UtModel, UtReferenceModel>()
@@ -175,8 +180,13 @@ class AssembleModelGenerator(private val basePackageName: String) {
175180
private fun assembleModel(utModel: UtModel): UtModel {
176181
val collectedCallChain = callChain.toMutableList()
177182

178-
// We cannot create an assemble model for an anonymous class instance
179-
if (utModel.classId.isAnonymous) {
183+
try {
184+
// We cannot create an assemble model for an anonymous class instance
185+
if (utModel.classId.isAnonymous) {
186+
return utModel
187+
}
188+
} catch (e: Exception) {
189+
logger.warn { "Failed to load jClass for class ${utModel.classId.name} when assembling model" }
180190
return utModel
181191
}
182192

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -689,23 +689,6 @@ enum class ProjectType {
689689
JavaScript,
690690
}
691691

692-
sealed class TypeReplacementApproach {
693-
/**
694-
* Do not replace interfaces and abstract classes with concrete implementors.
695-
* Use mocking instead of it.
696-
*/
697-
object DoNotReplace : TypeReplacementApproach()
698-
699-
/**
700-
* Try to replace interfaces and abstract classes with concrete implementors
701-
* obtained from bean definitions.
702-
* If it is impossible, use mocking.
703-
*
704-
* Currently used in Spring applications only.
705-
*/
706-
class ReplaceIfPossible(val config: String) : TypeReplacementApproach()
707-
}
708-
709692
abstract class DependencyInjectionFramework(
710693
override val id: String,
711694
override val displayName: String,

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,8 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
328328
when (methodType) {
329329
SUCCESSFUL -> error("Unexpected successful without exception method type for execution with exception $expectedException")
330330
PASSED_EXCEPTION -> {
331+
// TODO consider rendering message in a comment
332+
// expectedException.message?.let { +comment(it) }
331333
testFrameworkManager.expectException(expectedException::class.id) {
332334
methodInvocationBlock()
333335
}

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@ import org.utbot.framework.codegen.domain.context.CgContext
44
import org.utbot.framework.codegen.domain.models.CgValue
55
import org.utbot.framework.codegen.domain.models.CgVariable
66
import org.utbot.framework.plugin.api.UtAssembleModel
7+
import org.utbot.framework.plugin.api.UtAutowiredModel
78
import org.utbot.framework.plugin.api.UtCompositeModel
89
import org.utbot.framework.plugin.api.UtModel
910
import org.utbot.framework.plugin.api.isMockModel
11+
import org.utbot.framework.plugin.api.util.jClass
1012

1113
class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(context) {
1214
val injectedMocksModelsVariables: MutableMap<Set<UtModel>, CgValue> = mutableMapOf()
1315
val mockedModelsVariables: MutableMap<Set<UtModel>, CgValue> = mutableMapOf()
1416

1517
override fun getOrCreateVariable(model: UtModel, name: String?): CgValue {
18+
if (model is UtAutowiredModel)
19+
// TODO also, properly render corresponding @Autowired field, and make sure name isn't taken
20+
return CgVariable(name ?: model.classId.jClass.simpleName.let { it[0].lowercase() + it.drop(1) }, model.classId)
21+
1622
val alreadyCreatedInjectMocks = findCgValueByModel(model, injectedMocksModelsVariables)
1723
if (alreadyCreatedInjectMocks != null) {
1824
val fields: Collection<UtModel> = when (model) {

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ open class CgVariableConstructor(val context: CgContext) :
113113
is UtEnumConstantModel -> constructEnumConstant(model, baseName)
114114
is UtClassRefModel -> constructClassRef(model, baseName)
115115
is UtLambdaModel -> constructLambda(model, baseName)
116+
else -> error("Unexpected UtModel: ${model::class}")
116117
}
117118
} else valueByModel.getOrPut(model) {
118119
when (model) {

utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import org.utbot.framework.plugin.api.ClassId
88
import org.utbot.framework.plugin.api.MissingState
99
import org.utbot.framework.plugin.api.UtArrayModel
1010
import org.utbot.framework.plugin.api.UtAssembleModel
11+
import org.utbot.framework.plugin.api.UtAutowiredModel
1112
import org.utbot.framework.plugin.api.UtClassRefModel
1213
import org.utbot.framework.plugin.api.UtCompositeModel
1314
import org.utbot.framework.plugin.api.UtEnumConstantModel
@@ -237,6 +238,12 @@ private class FieldStateVisitor : UtModelVisitor<FieldData>() {
237238
recordFieldState(data, element)
238239
}
239240

241+
override fun visit(element: UtAutowiredModel, data: FieldData) {
242+
// TODO consider making UtAutowiredModel wrap UtCompositeModel or
243+
// even extend UtAssembleModel so here it's possible to recursively record field state
244+
recordFieldState(data, element)
245+
}
246+
240247
private fun recordFieldState(data: FieldData, model: UtModel) {
241248
val fields = data.fields
242249
val path = data.path

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import org.utbot.framework.util.SootUtils
3636
import org.utbot.framework.util.jimpleBody
3737
import org.utbot.framework.util.toModel
3838
import org.utbot.instrumentation.ConcreteExecutor
39+
import org.utbot.instrumentation.instrumentation.execution.SpringUtExecutionInstrumentation
3940
import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation
4041
import org.utbot.instrumentation.warmup
4142
import java.io.File
@@ -65,6 +66,15 @@ open class TestCaseGenerator(
6566
) {
6667
private val logger: KLogger = KotlinLogging.logger {}
6768
private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout")
69+
private val executionInstrumentation by lazy {
70+
when (applicationContext) {
71+
is SpringApplicationContext -> when (val approach = applicationContext.typeReplacementApproach) {
72+
is TypeReplacementApproach.ReplaceIfPossible -> SpringUtExecutionInstrumentation(UtExecutionInstrumentation, approach.config)
73+
is TypeReplacementApproach.DoNotReplace -> UtExecutionInstrumentation
74+
}
75+
else -> UtExecutionInstrumentation
76+
}
77+
}
6878

6979
private val classpathForEngine: String
7080
get() = (buildDirs + listOfNotNull(classpath)).joinToString(File.pathSeparator)
@@ -88,7 +98,7 @@ open class TestCaseGenerator(
8898
if (warmupConcreteExecution) {
8999
// force pool to create an appropriate executor
90100
ConcreteExecutor(
91-
UtExecutionInstrumentation,
101+
executionInstrumentation,
92102
classpathForEngine,
93103
).apply {
94104
warmup()
@@ -269,6 +279,7 @@ open class TestCaseGenerator(
269279
mockStrategy = mockStrategyApi.toModel(),
270280
chosenClassesToMockAlways = chosenClassesToMockAlways,
271281
applicationContext = applicationContext,
282+
executionInstrumentation = executionInstrumentation,
272283
solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis
273284
)
274285
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ fun defaultTestFlow(timeout: Long) = testFlow {
3030
*/
3131
fun defaultSpringFlow(params: GenerateParams) = testFlow {
3232
generationTimeout = params.generationTimeout
33-
isSymbolicEngineEnabled = true
33+
isSymbolicEngineEnabled = params.isSymbolicEngineEnabled
3434
isFuzzingEnabled = params.isFuzzingEnabled
3535
fuzzingValue = params.fuzzingValue
3636
}

utbot-framework/src/main/kotlin/org/utbot/framework/util/UtModelVisitor.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.utbot.framework.util
22

33
import org.utbot.framework.plugin.api.UtArrayModel
44
import org.utbot.framework.plugin.api.UtAssembleModel
5+
import org.utbot.framework.plugin.api.UtAutowiredModel
56
import org.utbot.framework.plugin.api.UtClassRefModel
67
import org.utbot.framework.plugin.api.UtCompositeModel
78
import org.utbot.framework.plugin.api.UtEnumConstantModel
@@ -35,6 +36,7 @@ abstract class UtModelVisitor<D> {
3536
is UtAssembleModel -> visit(element, data)
3637
is UtCompositeModel -> visit(element, data)
3738
is UtLambdaModel -> visit(element, data)
39+
is UtAutowiredModel -> visit(element, data)
3840
}
3941
}
4042

@@ -44,6 +46,7 @@ abstract class UtModelVisitor<D> {
4446
protected abstract fun visit(element: UtAssembleModel, data: D)
4547
protected abstract fun visit(element: UtCompositeModel, data: D)
4648
protected abstract fun visit(element: UtLambdaModel, data: D)
49+
protected abstract fun visit(element: UtAutowiredModel, data: D)
4750

4851
/**
4952
* Returns true when we can traverse the given model.

utbot-instrumentation/build.gradle

Lines changed: 0 additions & 64 deletions
This file was deleted.

0 commit comments

Comments
 (0)