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 81e364d

Browse files
IlyaMuravjovEgorkaKulikov
authored andcommitted
Add a prototype of Spring integration tests
1 parent dd1506e commit 81e364d

File tree

62 files changed

+1149
-328
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1149
-328
lines changed

‎settings.gradle.kts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,4 @@ if (goIde.split(",").contains(ideType)) {
7171
}
7272

7373
include("utbot-spring-analyzer")
74+
include("utbot-spring-commons")
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.utbot.common
2+
3+
import java.io.File
4+
import java.net.URL
5+
import java.nio.file.Files
6+
import java.nio.file.StandardCopyOption
7+
8+
object JarUtils {
9+
private const val UNKNOWN_MODIFICATION_TIME = 0L
10+
11+
fun extractJarFileFromResources(jarFileName: String, jarResourcePath: String, targetDirectoryName: String): File {
12+
val targetDirectory =
13+
Files.createDirectories(utBotTempDirectory.toFile().resolve(targetDirectoryName).toPath()).toFile()
14+
return targetDirectory.resolve(jarFileName).also { jarFile ->
15+
val resource = this::class.java.classLoader.getResource(jarResourcePath)
16+
?: error("Unable to find \"$jarResourcePath\" in resources, make sure it's on the classpath")
17+
updateJarIfRequired(jarFile, resource)
18+
}
19+
}
20+
21+
private fun updateJarIfRequired(jarFile: File, resource: URL) {
22+
val resourceConnection = resource.openConnection()
23+
resourceConnection.getInputStream().use { inputStream ->
24+
val lastResourceModification = resourceConnection.lastModified
25+
if (
26+
!jarFile.exists() ||
27+
jarFile.lastModified() == UNKNOWN_MODIFICATION_TIME ||
28+
lastResourceModification == UNKNOWN_MODIFICATION_TIME ||
29+
jarFile.lastModified() < lastResourceModification
30+
) {
31+
Files.copy(inputStream, jarFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
32+
}
33+
}
34+
}
35+
}

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

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,12 @@ open class EnvironmentModels(
236236
operator fun component1(): UtModel? = thisInstance
237237
operator fun component2(): List<UtModel> = parameters
238238
operator fun component3(): Map<FieldId, UtModel> = statics
239+
240+
fun copy(
241+
thisInstance: UtModel? = this.thisInstance,
242+
parameters: List<UtModel> = this.parameters,
243+
statics: Map<FieldId, UtModel> = this.statics
244+
) = EnvironmentModels(thisInstance, parameters, statics)
239245
}
240246

241247
/**
@@ -632,6 +638,46 @@ class UtLambdaModel(
632638
}
633639
}
634640

641+
abstract class UtAutowiredBaseModel(
642+
override val id: Int?,
643+
override val classId: ClassId,
644+
val origin: UtModel,
645+
modelName: String
646+
) : UtReferenceModel(
647+
id, classId, modelName
648+
)
649+
650+
class UtAutowiredStateBeforeModel(
651+
id: Int?,
652+
classId: ClassId,
653+
origin: UtModel,
654+
val beanName: String,
655+
val repositoriesContent: List<RepositoryContentModel>,
656+
) : UtAutowiredBaseModel(
657+
id, classId, origin, modelName = "@Autowired $beanName#$id"
658+
)
659+
660+
data class RepositoryContentModel(
661+
val repositoryBeanName: String,
662+
val entityModels: List<UtModel>,
663+
)
664+
665+
class UtAutowiredStateAfterModel(
666+
id: Int?,
667+
classId: ClassId,
668+
origin: UtModel,
669+
val repositoryInteractions: List<RepositoryInteractionModel>,
670+
) : UtAutowiredBaseModel(
671+
id, classId, origin, modelName = "@Autowired ${classId.name}#$id"
672+
)
673+
674+
data class RepositoryInteractionModel(
675+
val beanName: String,
676+
val executableId: ExecutableId,
677+
val args: List<UtModel>,
678+
val result: UtExecutionResult
679+
)
680+
635681
/**
636682
* Model for a step to obtain [UtAssembleModel].
637683
*/
@@ -1253,18 +1299,46 @@ open class ApplicationContext(
12531299
): Boolean = field.isFinal || !field.isPublic
12541300
}
12551301

1302+
sealed class TypeReplacementApproach {
1303+
/**
1304+
* Do not replace interfaces and abstract classes with concrete implementors.
1305+
* Use mocking instead of it.
1306+
*/
1307+
object DoNotReplace : TypeReplacementApproach()
1308+
1309+
/**
1310+
* Try to replace interfaces and abstract classes with concrete implementors
1311+
* obtained from bean definitions.
1312+
* If it is impossible, use mocking.
1313+
*
1314+
* Currently used in Spring applications only.
1315+
*/
1316+
class ReplaceIfPossible(val config: String) : TypeReplacementApproach()
1317+
}
1318+
12561319
/**
12571320
* Data we get from Spring application context
12581321
* to manage engine and code generator behaviour.
12591322
*
12601323
* @param beanDefinitions describes bean definitions (bean name, type, some optional additional data)
12611324
* @param shouldUseImplementors describes it we want to replace interfaces with injected types or not
12621325
*/
1326+
// TODO move this class to utbot-framework so we can use it as abstract factory
1327+
// to get rid of numerous `when`s and polymorphically create things like:
1328+
// - Instrumentation<UtConcreteExecutionResult>
1329+
// - FuzzedType (to get rid of thisInstanceFuzzedTypeWrapper)
1330+
// - JavaValueProvider
1331+
// - CgVariableConstructor
1332+
// - CodeGeneratorResult (generateForSpringClass)
1333+
// Right now this refactoring is blocked because some interfaces need to get extracted and moved to utbot-framework-api
1334+
// As an alternative we can just move ApplicationContext itself to utbot-framework
12631335
class SpringApplicationContext(
12641336
mockInstalled: Boolean,
12651337
staticsMockingIsConfigured: Boolean,
1266-
privateval beanDefinitions: List<BeanDefinitionData> = emptyList(),
1338+
val beanDefinitions: List<BeanDefinitionData> = emptyList(),
12671339
private val shouldUseImplementors: Boolean,
1340+
val typeReplacementApproach: TypeReplacementApproach,
1341+
val testType: SpringTestType
12681342
): ApplicationContext(mockInstalled, staticsMockingIsConfigured) {
12691343

12701344
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: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,14 @@ import org.utbot.framework.util.graph
4040
import org.utbot.framework.util.sootMethod
4141
import org.utbot.fuzzer.*
4242
import org.utbot.fuzzing.*
43+
import org.utbot.fuzzing.providers.AutowiredValueProvider
44+
import org.utbot.fuzzing.type.factories.SimpleFuzzedTypeFactory
45+
import org.utbot.fuzzing.type.factories.SpringFuzzedTypeFactory
4346
import org.utbot.fuzzing.utils.Trie
4447
import org.utbot.instrumentation.ConcreteExecutor
48+
import org.utbot.instrumentation.instrumentation.Instrumentation
4549
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionData
4650
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult
47-
import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation
4851
import soot.jimple.Stmt
4952
import soot.tagkit.ParamNamesTag
5053
import java.lang.reflect.Method
@@ -105,7 +108,8 @@ class UtBotSymbolicEngine(
105108
dependencyPaths: String,
106109
val mockStrategy: MockStrategy = NO_MOCKS,
107110
chosenClassesToMockAlways: Set<ClassId>,
108-
applicationContext: ApplicationContext,
111+
val applicationContext: ApplicationContext,
112+
executionInstrumentation: Instrumentation<UtConcreteExecutionResult>,
109113
private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis
110114
) : UtContextInitializer() {
111115
private val graph = methodUnderTest.sootMethod.jimpleBody().apply {
@@ -154,7 +158,7 @@ class UtBotSymbolicEngine(
154158

155159
private val concreteExecutor =
156160
ConcreteExecutor(
157-
UtExecutionInstrumentation,
161+
executionInstrumentation,
158162
classpath,
159163
).apply { this.classLoader = utContext.classLoader }
160164

@@ -355,7 +359,29 @@ class UtBotSymbolicEngine(
355359
methodUnderTest,
356360
collectConstantsForFuzzer(graph),
357361
names,
358-
listOf(transform(ValueProvider.of(defaultValueProviders(defaultIdGenerator))))
362+
listOf(transform(ValueProvider.of(defaultValueProviders(defaultIdGenerator)))),
363+
fuzzedTypeFactory = when (applicationContext) {
364+
is SpringApplicationContext -> when (applicationContext.typeReplacementApproach) {
365+
is TypeReplacementApproach.ReplaceIfPossible -> SpringFuzzedTypeFactory(
366+
autowiredValueProvider = AutowiredValueProvider(
367+
defaultIdGenerator,
368+
autowiredModelOriginCreator = { beanName ->
369+
runBlocking {
370+
logger.info { "Getting bean: $beanName" }
371+
concreteExecutor.withProcess { getBean(beanName) }
372+
}
373+
}
374+
),
375+
beanNamesFinder = { classId ->
376+
applicationContext.beanDefinitions
377+
.filter { it.beanTypeFqn == classId.name }
378+
.map { it.beanName }
379+
}
380+
)
381+
is TypeReplacementApproach.DoNotReplace -> SimpleFuzzedTypeFactory()
382+
}
383+
else -> SimpleFuzzedTypeFactory()
384+
},
359385
) { thisInstance, descr, values ->
360386
if (thisInstance?.model is UtNullModel) {
361387
// We should not try to run concretely any models with null-this.
@@ -593,7 +619,7 @@ private fun ResolvedModels.constructStateForMethod(methodUnderTest: ExecutableId
593619
return EnvironmentModels(thisInstanceBefore, paramsBefore, statics)
594620
}
595621

596-
private suspend fun ConcreteExecutor<UtConcreteExecutionResult, UtExecutionInstrumentation>.executeConcretely(
622+
private suspend fun ConcreteExecutor<UtConcreteExecutionResult, Instrumentation<UtConcreteExecutionResult>>.executeConcretely(
597623
methodUnderTest: ExecutableId,
598624
stateBefore: EnvironmentModels,
599625
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: ClassNotFoundException) {
189+
// happens, for example, when `utModel.classId.name` is something like "jdk.proxy3.$Proxy144"
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: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,38 @@ 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.UtAutowiredBaseModel
9+
import org.utbot.framework.plugin.api.UtAutowiredStateBeforeModel
810
import org.utbot.framework.plugin.api.UtCompositeModel
911
import org.utbot.framework.plugin.api.UtModel
1012
import org.utbot.framework.plugin.api.isMockModel
13+
import org.utbot.framework.plugin.api.util.jClass
1114

1215
class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(context) {
1316
val injectedMocksModelsVariables: MutableSet<UtModelWrapper> = mutableSetOf()
1417
val mockedModelsVariables: MutableSet<UtModelWrapper> = mutableSetOf()
1518

1619
override fun getOrCreateVariable(model: UtModel, name: String?): CgValue {
20+
if (model is UtAutowiredBaseModel) {
21+
// TODO also, properly render corresponding @Autowired field(s), and make sure name isn't taken
22+
if (model is UtAutowiredStateBeforeModel) {
23+
comment("begin repository fill up")
24+
model.repositoriesContent.forEach { repositoryContent ->
25+
repositoryContent.entityModels.forEach { entity ->
26+
// TODO actually fill up repositories
27+
getOrCreateVariable(entity)
28+
emptyLine()
29+
}
30+
}
31+
comment("end repository fill up")
32+
}
33+
emptyLine()
34+
return CgVariable(
35+
name ?: model.classId.jClass.simpleName.let { it[0].lowercase() + it.drop(1) },
36+
model.classId
37+
)
38+
}
39+
1740
val alreadyCreatedInjectMocks = findCgValueByModel(model, injectedMocksModelsVariables)
1841
if (alreadyCreatedInjectMocks != null) {
1942
val fields: Collection<UtModel> = when (model) {

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

Lines changed: 5 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.UtAutowiredBaseModel
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,10 @@ private class FieldStateVisitor : UtModelVisitor<FieldData>() {
237238
recordFieldState(data, element)
238239
}
239240

241+
override fun visit(element: UtAutowiredBaseModel, data: FieldData) {
242+
element.origin.accept(this, data)
243+
}
244+
240245
private fun recordFieldState(data: FieldData, model: UtModel) {
241246
val fields = data.fields
242247
val path = data.path

0 commit comments

Comments
(0)

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