Skip to content

Commit cd7e15a

Browse files
committed
Introduce SpringSettings
1 parent 9167d10 commit cd7e15a

File tree

25 files changed

+369
-307
lines changed

25 files changed

+369
-307
lines changed

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

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,13 @@ enum class TypeReplacementMode {
13231323
NoImplementors,
13241324
}
13251325

1326+
interface CodeGenerationContext
1327+
1328+
interface SpringCodeGenerationContext : CodeGenerationContext {
1329+
val springTestType: SpringTestType
1330+
val springSettings: SpringSettings?
1331+
}
1332+
13261333
/**
13271334
* A context to use when no specific data is required.
13281335
*
@@ -1332,7 +1339,7 @@ enum class TypeReplacementMode {
13321339
open class ApplicationContext(
13331340
val mockFrameworkInstalled: Boolean = true,
13341341
staticsMockingIsConfigured: Boolean = true,
1335-
) {
1342+
) : CodeGenerationContext {
13361343
var staticsMockingIsConfigured = staticsMockingIsConfigured
13371344
private set
13381345

@@ -1384,23 +1391,16 @@ open class ApplicationContext(
13841391
): Boolean = field.isFinal || !field.isPublic
13851392
}
13861393

1387-
sealed class TypeReplacementApproach {
1388-
/**
1389-
* Do not replace interfaces and abstract classes with concrete implementors.
1390-
* Use mocking instead of it.
1391-
*/
1392-
object DoNotReplace : TypeReplacementApproach()
1393-
1394-
/**
1395-
* Try to replace interfaces and abstract classes with concrete implementors
1396-
* obtained from bean definitions.
1397-
* If it is impossible, use mocking.
1398-
*
1399-
* Currently used in Spring applications only.
1400-
*/
1401-
class ReplaceIfPossible(val config: String) : TypeReplacementApproach()
1394+
sealed interface SpringConfiguration {
1395+
class JavaConfiguration(val classBinaryName: String) : SpringConfiguration
1396+
class XMLConfiguration(val absolutePath: String) : SpringConfiguration
14021397
}
14031398

1399+
class SpringSettings(
1400+
val configuration: SpringConfiguration,
1401+
val profileExpression: String
1402+
)
1403+
14041404
/**
14051405
* Data we get from Spring application context
14061406
* to manage engine and code generator behaviour.
@@ -1422,9 +1422,9 @@ class SpringApplicationContext(
14221422
staticsMockingIsConfigured: Boolean,
14231423
val beanDefinitions: List<BeanDefinitionData> = emptyList(),
14241424
private val shouldUseImplementors: Boolean,
1425-
val typeReplacementApproach: TypeReplacementApproach,
1426-
val testType: SpringTestsType
1427-
): ApplicationContext(mockInstalled, staticsMockingIsConfigured) {
1425+
override val springTestType: SpringTestType,
1426+
override val springSettings: SpringSettings?,
1427+
): ApplicationContext(mockInstalled, staticsMockingIsConfigured), SpringCodeGenerationContext {
14281428

14291429
companion object {
14301430
private val logger = KotlinLogging.logger {}
@@ -1500,19 +1500,19 @@ class SpringApplicationContext(
15001500
): Boolean = field.fieldId in classUnderTest.allDeclaredFieldIds && field.declaringClass.id !in springInjectedClasses
15011501
}
15021502

1503-
enum class SpringTestsType(
1503+
enum class SpringTestType(
15041504
override val id: String,
15051505
override val displayName: String,
15061506
override val description: String,
15071507
// Integration tests generation requires spring test framework being installed
15081508
var frameworkInstalled: Boolean = false,
15091509
) : CodeGenerationSettingItem {
1510-
UNIT_TESTS(
1510+
UNIT_TEST(
15111511
"Unit tests",
15121512
"Unit tests",
15131513
"Generate unit tests mocking other classes"
15141514
),
1515-
INTEGRATION_TESTS(
1515+
INTEGRATION_TEST(
15161516
"Integration tests",
15171517
"Integration tests",
15181518
"Generate integration tests autowiring real instance"
@@ -1521,8 +1521,8 @@ enum class SpringTestsType(
15211521
override fun toString() = id
15221522

15231523
companion object : CodeGenerationSettingBox {
1524-
override val defaultItem = UNIT_TESTS
1525-
override val allItems: List<SpringTestsType> = values().toList()
1524+
override val defaultItem = UNIT_TEST
1525+
override val allItems: List<SpringTestType> = values().toList()
15261526
}
15271527
}
15281528

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ object SpringModelUtils {
3131
val springBootTestContextBootstrapperClassId =
3232
ClassId("org.springframework.boot.test.context.SpringBootTestContextBootstrapper")
3333

34+
val activeProfilesClassId = ClassId("org.springframework.test.context.ActiveProfiles")
35+
val contextConfigurationClassId = ClassId("org.springframework.test.context.ContextConfiguration")
36+
3437

3538
// most likely only one persistent library is on the classpath, but we need to be able to work with either of them
3639
private val persistentLibraries = listOf("javax.persistence", "jakarta.persistence")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ class UtBotSymbolicEngine(
388388
var testEmittedByFuzzer = 0
389389
val valueProviders = ValueProvider.of(defaultValueProviders(defaultIdGenerator))
390390
.letIf(applicationContext is SpringApplicationContext
391-
&& applicationContext.typeReplacementApproach is TypeReplacementApproach.ReplaceIfPossible
391+
&& applicationContext.springSettings != null
392392
) { provider ->
393393
val relevantRepositories = concreteExecutor.getRelevantSpringRepositories(methodUnderTest.classId)
394394
logger.info { "Detected relevant repositories for class ${methodUnderTest.classId}: $relevantRepositories" }

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@ class SpringTestClassModel(
3030
val springSpecificInformation: SpringSpecificInformation,
3131
): TestClassModel(classUnderTest, methodTestSets, nestedClasses)
3232

33-
3433
class SpringSpecificInformation(
35-
val thisInstanceModels: TypedModelWrappers = mapOf(),
36-
val thisInstanceDependentMocks: TypedModelWrappers = mapOf(),
37-
val autowiredFromContextModels: TypedModelWrappers = mapOf(),
34+
val thisInstanceModels: TypedModelWrappers,
35+
val thisInstanceDependentMocks: TypedModelWrappers,
36+
val autowiredFromContextModels: TypedModelWrappers,
3837
)

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/SpringCodeGenerator.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ import org.utbot.framework.plugin.api.ClassId
1717
import org.utbot.framework.plugin.api.CodegenLanguage
1818
import org.utbot.framework.plugin.api.ExecutableId
1919
import org.utbot.framework.plugin.api.MockFramework
20-
import org.utbot.framework.plugin.api.SpringTestsType
20+
import org.utbot.framework.plugin.api.SpringTestType
21+
import org.utbot.framework.plugin.api.SpringCodeGenerationContext
2122

2223
class SpringCodeGenerator(
23-
private val springTestsType: SpringTestsType = SpringTestsType.defaultItem,
2424
val classUnderTest: ClassId,
2525
val projectType: ProjectType,
26+
val codeGenerationContext: SpringCodeGenerationContext,
2627
paramNames: MutableMap<ExecutableId, List<String>> = mutableMapOf(),
2728
generateUtilClassFile: Boolean = false,
2829
testFramework: TestFramework = TestFramework.defaultItem,
@@ -59,9 +60,12 @@ class SpringCodeGenerator(
5960
val testClassModel = SpringTestClassModelBuilder(context).createTestClassModel(classUnderTest, testSets)
6061

6162
logger.info { "Code generation phase started at ${now()}" }
62-
val astConstructor = when (springTestsType) {
63-
SpringTestsType.UNIT_TESTS -> CgSpringUnitTestClassConstructor(context)
64-
SpringTestsType.INTEGRATION_TESTS -> CgSpringIntegrationTestClassConstructor(context)
63+
val astConstructor = when (codeGenerationContext.springTestType) {
64+
SpringTestType.UNIT_TEST -> CgSpringUnitTestClassConstructor(context)
65+
SpringTestType.INTEGRATION_TEST ->
66+
codeGenerationContext.springSettings
67+
?.let { CgSpringIntegrationTestClassConstructor(context, it) }
68+
?: error("Error text.")
6569
}
6670
val testClassFile = astConstructor.construct(testClassModel)
6771
logger.info { "Code generation phase finished at ${now()}" }

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,50 +8,67 @@ import org.utbot.framework.codegen.domain.context.CgContext
88
import org.utbot.framework.codegen.domain.models.*
99
import org.utbot.framework.codegen.domain.models.AnnotationTarget.*
1010
import org.utbot.framework.plugin.api.ClassId
11-
import org.utbot.framework.plugin.api.CodegenLanguage
11+
import org.utbot.framework.plugin.api.SpringConfiguration
12+
import org.utbot.framework.plugin.api.SpringSettings
1213
import org.utbot.framework.plugin.api.util.SpringModelUtils
14+
import org.utbot.framework.plugin.api.util.SpringModelUtils.activeProfilesClassId
1315
import org.utbot.framework.plugin.api.util.SpringModelUtils.autoConfigureTestDbClassId
1416
import org.utbot.framework.plugin.api.util.SpringModelUtils.autowiredClassId
1517
import org.utbot.framework.plugin.api.util.SpringModelUtils.bootstrapWithClassId
18+
import org.utbot.framework.plugin.api.util.SpringModelUtils.contextConfigurationClassId
1619
import org.utbot.framework.plugin.api.util.SpringModelUtils.dirtiesContextClassId
1720
import org.utbot.framework.plugin.api.util.SpringModelUtils.dirtiesContextClassModeClassId
1821
import org.utbot.framework.plugin.api.util.SpringModelUtils.springBootTestContextBootstrapperClassId
1922
import org.utbot.framework.plugin.api.util.SpringModelUtils.springExtensionClassId
2023
import org.utbot.framework.plugin.api.util.SpringModelUtils.transactionalClassId
2124
import org.utbot.framework.plugin.api.util.utContext
2225

23-
class CgSpringIntegrationTestClassConstructor(context: CgContext) : CgAbstractSpringTestClassConstructor(context) {
26+
class CgSpringIntegrationTestClassConstructor(
27+
context: CgContext,
28+
private val springSettings: SpringSettings
29+
) : CgAbstractSpringTestClassConstructor(context) {
2430
override fun constructTestClass(testClassModel: SpringTestClassModel): CgClass {
25-
collectSpringSpecificAnnotations()
31+
addNecessarySpringSpecificAnnotations()
2632
return super.constructTestClass(testClassModel)
2733
}
2834

2935
override fun constructClassFields(testClassModel: SpringTestClassModel): List<CgFieldDeclaration> {
30-
val autowiredFromContextModels = testClassModel.springSpecificInformation.autowiredFromContextModels
36+
val autowiredFromContextModels =
37+
testClassModel.springSpecificInformation.autowiredFromContextModels
3138
return constructFieldsWithAnnotation(autowiredClassId, autowiredFromContextModels)
3239
}
3340

34-
override fun constructAdditionalMethods() = CgMethodsCluster(header = null, content = emptyList())
41+
override fun constructAdditionalMethods() =
42+
CgMethodsCluster(header = null, content = emptyList())
3543

36-
private fun collectSpringSpecificAnnotations() {
44+
private fun addNecessarySpringSpecificAnnotations() {
3745
val springRunnerType = when (testFramework) {
3846
Junit4 -> SpringModelUtils.runWithClassId
3947
Junit5 -> SpringModelUtils.extendWithClassId
4048
TestNg -> error("Spring extension is not implemented in TestNg")
4149
else -> error("Trying to generate tests for Spring project with non-JVM framework")
4250
}
4351

44-
statementConstructor.addAnnotation(
52+
addAnnotation(
4553
classId = springRunnerType,
4654
argument = createGetClassExpression(springExtensionClassId, codegenLanguage),
4755
target = Class,
4856
)
49-
statementConstructor.addAnnotation(
57+
addAnnotation(
5058
classId = bootstrapWithClassId,
5159
argument = createGetClassExpression(springBootTestContextBootstrapperClassId, codegenLanguage),
5260
target = Class,
5361
)
54-
62+
addAnnotation(
63+
classId = activeProfilesClassId,
64+
argument = springSettings.profileExpression, // TODO: separate by comma
65+
target = Class,
66+
)
67+
addAnnotation(
68+
classId = contextConfigurationClassId,
69+
argument = (springSettings.configuration as SpringConfiguration.JavaConfiguration).classBinaryName, // TODO: unpacking
70+
target = Class,
71+
)
5572
addAnnotation(
5673
classId = dirtiesContextClassId,
5774
namedArguments = listOf(

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,19 @@ open class TestCaseGenerator(
6969
private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout")
7070
private val executionInstrumentation by lazy {
7171
when (applicationContext) {
72-
is SpringApplicationContext -> when (val approach = applicationContext.typeReplacementApproach) {
73-
is TypeReplacementApproach.ReplaceIfPossible ->
74-
when (applicationContext.testType) {
75-
SpringTestsType.UNIT_TESTS -> UtExecutionInstrumentation
76-
SpringTestsType.INTEGRATION_TESTS -> SpringUtExecutionInstrumentation(
77-
UtExecutionInstrumentation,
78-
approach.config,
79-
applicationContext.beanDefinitions,
80-
buildDirs.map { it.toURL() }.toTypedArray(),
81-
)
82-
}
83-
is TypeReplacementApproach.DoNotReplace -> UtExecutionInstrumentation
72+
is SpringApplicationContext -> when (val springSettings = applicationContext.springSettings) {
73+
null -> UtExecutionInstrumentation
74+
else -> when (applicationContext.springTestType) {
75+
SpringTestType.UNIT_TEST -> UtExecutionInstrumentation
76+
SpringTestType.INTEGRATION_TEST -> SpringUtExecutionInstrumentation(
77+
UtExecutionInstrumentation,
78+
springSettings,
79+
applicationContext.beanDefinitions,
80+
buildDirs.map { it.toURL() }.toTypedArray(),
81+
)
82+
}
8483
}
84+
8585
else -> UtExecutionInstrumentation
8686
}
8787
}

0 commit comments

Comments
 (0)