Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /