Skip to content

Commit 1448cc9

Browse files
IlyaMuravjovEgorkaKulikov
authored andcommitted
Add a prototype of Spring integration tests
1 parent c78fa93 commit 1448cc9

File tree

34 files changed

+477
-149
lines changed

34 files changed

+477
-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
@@ -632,6 +632,14 @@ class UtLambdaModel(
632632
}
633633
}
634634

635+
class UtAutowiredModel(
636+
override val id: Int?,
637+
override val classId: ClassId,
638+
val beanName: String,
639+
) : UtReferenceModel(
640+
id, classId, modelName = "@Autowired $beanName#$id"
641+
)
642+
635643
/**
636644
* Model for a step to obtain [UtAssembleModel].
637645
*/
@@ -1253,6 +1261,23 @@ open class ApplicationContext(
12531261
): Boolean = field.isFinal || !field.isPublic
12541262
}
12551263

1264+
sealed class TypeReplacementApproach {
1265+
/**
1266+
* Do not replace interfaces and abstract classes with concrete implementors.
1267+
* Use mocking instead of it.
1268+
*/
1269+
object DoNotReplace : TypeReplacementApproach()
1270+
1271+
/**
1272+
* Try to replace interfaces and abstract classes with concrete implementors
1273+
* obtained from bean definitions.
1274+
* If it is impossible, use mocking.
1275+
*
1276+
* Currently used in Spring applications only.
1277+
*/
1278+
class ReplaceIfPossible(val config: String) : TypeReplacementApproach()
1279+
}
1280+
12561281
/**
12571282
* Data we get from Spring application context
12581283
* to manage engine and code generator behaviour.
@@ -1265,6 +1290,7 @@ class SpringApplicationContext(
12651290
staticsMockingIsConfigured: Boolean,
12661291
private val beanDefinitions: List<BeanDefinitionData> = emptyList(),
12671292
private val shouldUseImplementors: Boolean,
1293+
val typeReplacementApproach: TypeReplacementApproach
12681294
): ApplicationContext(mockInstalled, staticsMockingIsConfigured) {
12691295

12701296
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
@@ -688,23 +688,6 @@ enum class ProjectType {
688688
JavaScript,
689689
}
690690

691-
sealed class TypeReplacementApproach {
692-
/**
693-
* Do not replace interfaces and abstract classes with concrete implementors.
694-
* Use mocking instead of it.
695-
*/
696-
object DoNotReplace : TypeReplacementApproach()
697-
698-
/**
699-
* Try to replace interfaces and abstract classes with concrete implementors
700-
* obtained from bean definitions.
701-
* If it is impossible, use mocking.
702-
*
703-
* Currently used in Spring applications only.
704-
*/
705-
class ReplaceIfPossible(val config: String) : TypeReplacementApproach()
706-
}
707-
708691
abstract class DependencyInjectionFramework(
709692
override val id: String,
710693
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
@@ -5,15 +5,21 @@ import org.utbot.framework.codegen.domain.context.CgContext
55
import org.utbot.framework.codegen.domain.models.CgValue
66
import org.utbot.framework.codegen.domain.models.CgVariable
77
import org.utbot.framework.plugin.api.UtAssembleModel
8+
import org.utbot.framework.plugin.api.UtAutowiredModel
89
import org.utbot.framework.plugin.api.UtCompositeModel
910
import org.utbot.framework.plugin.api.UtModel
1011
import org.utbot.framework.plugin.api.isMockModel
12+
import org.utbot.framework.plugin.api.util.jClass
1113

1214
class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(context) {
1315
val injectedMocksModelsVariables: MutableSet<UtModelWrapper> = mutableSetOf()
1416
val mockedModelsVariables: MutableSet<UtModelWrapper> = mutableSetOf()
1517

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