Skip to content

Commit 389fe66

Browse files
authored
Introduce and test UtBotSpringApi (#2632)
* Introduce and test `UtBotSpringApi` * Remove repeated dependency * Make `UtBotSpringApi` validate that integration tests are only used with `PresentSpringSettings` * Add `META-INF/spring` to spring-sample shadow jar * Add `UtBotSpringApi` tests for `AbsentSpringSettings` and profiles
1 parent 4bb4329 commit 389fe66

File tree

21 files changed

+421
-32
lines changed

21 files changed

+421
-32
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1430,7 +1430,10 @@ sealed class SpringConfiguration(val fullDisplayName: String) {
14301430

14311431
class JavaConfiguration(override val configBinaryName: String) : JavaBasedConfiguration(configBinaryName)
14321432

1433-
class SpringBootConfiguration(override val configBinaryName: String, val isUnique: Boolean): JavaBasedConfiguration(configBinaryName)
1433+
class SpringBootConfiguration(
1434+
override val configBinaryName: String,
1435+
val isDefinitelyUnique: Boolean
1436+
) : JavaBasedConfiguration(configBinaryName)
14341437

14351438
class XMLConfiguration(val absolutePath: String) : SpringConfiguration(absolutePath)
14361439
}

utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
250250
}
251251
watchdog.measureTimeForActiveCall(perform, "Performing dynamic task") { params ->
252252
val task = kryoHelper.readObject<EngineProcessTask<Any?>>(params.engineProcessTask)
253-
val result = task.perform(kryoHelper)
253+
val result = task.perform()
254254
kryoHelper.writeObject(result)
255255
}
256256
}
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.utbot.framework.process
22

3-
import org.utbot.framework.process.kryo.KryoHelper
4-
53
/**
64
* Implementations of this interface can be passed to engine process for execution and should
75
* be used for adding feature-specific (e.g. Spring-specific) tasks without inflating core UtBot codebase.
@@ -12,5 +10,5 @@ import org.utbot.framework.process.kryo.KryoHelper
1210
* @param R result type of the task (should be present on the classpath of both processes).
1311
*/
1412
interface EngineProcessTask<R> {
15-
fun perform(kryoHelper: KryoHelper): R
13+
fun perform(): R
1614
}

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ object UtTestsDialogProcessor {
292292
val clarifiedBeanDefinitions =
293293
clarifyBeanDefinitionReturnTypes(beanDefinitions, project)
294294

295-
SpringApplicationContextImpl(
295+
SpringApplicationContextImpl.internalCreate(
296296
simpleApplicationContext,
297297
clarifiedBeanDefinitions,
298298
model.springTestType,

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m
727727
if (springBootConfigs.contains(classBinaryName)) {
728728
SpringConfiguration.SpringBootConfiguration(
729729
configBinaryName = classBinaryName,
730-
isUnique = springBootConfigs.size == 1,
730+
isDefinitelyUnique = springBootConfigs.size == 1,
731731
)
732732
} else {
733733
SpringConfiguration.JavaConfiguration(classBinaryName)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package org.utbot.external.api
2+
3+
import org.utbot.framework.context.ApplicationContext
4+
import org.utbot.framework.context.simple.SimpleApplicationContext
5+
import org.utbot.framework.context.spring.SpringApplicationContext
6+
import org.utbot.framework.context.spring.SpringApplicationContextImpl
7+
import org.utbot.framework.plugin.api.SpringConfiguration
8+
import org.utbot.framework.plugin.api.SpringSettings
9+
import org.utbot.framework.plugin.api.SpringTestType
10+
import org.utbot.framework.process.SpringAnalyzerTask
11+
import java.io.File
12+
13+
object UtBotSpringApi {
14+
private val springBootConfigAnnotations = setOf(
15+
"org.springframework.boot.autoconfigure.SpringBootApplication",
16+
"org.springframework.boot.SpringBootConfiguration"
17+
)
18+
19+
/**
20+
* NOTE: [classpath] should include project under test classpath (with all dependencies) as well as
21+
* `spring-test`, `spring-boot-test`, and `spring-security-test` if respectively `spring-beans`,
22+
* `spring-boot`, and `spring-security-core` are dependencies of project under test.
23+
*
24+
* UtBot doesn't add Spring test modules to classpath automatically to let API users control their versions.
25+
*/
26+
@JvmOverloads
27+
@JvmStatic
28+
fun createSpringApplicationContext(
29+
springSettings: SpringSettings,
30+
springTestType: SpringTestType,
31+
classpath: List<String>,
32+
delegateContext: ApplicationContext = SimpleApplicationContext()
33+
): SpringApplicationContext {
34+
if (springTestType == SpringTestType.INTEGRATION_TEST) {
35+
require(springSettings is SpringSettings.PresentSpringSettings) {
36+
"Integration tests can't be generated without Spring settings"
37+
}
38+
val configuration = springSettings.configuration
39+
require(configuration !is SpringConfiguration.XMLConfiguration) {
40+
"Integration tests aren't supported for XML configurations, consider using Java " +
41+
"configuration that imports your XML configuration with @ImportResource"
42+
}
43+
}
44+
return SpringApplicationContextImpl.internalCreate(
45+
delegateContext = delegateContext,
46+
beanDefinitions = when (springSettings) {
47+
SpringSettings.AbsentSpringSettings -> listOf()
48+
is SpringSettings.PresentSpringSettings -> SpringAnalyzerTask(classpath, springSettings).perform()
49+
},
50+
springTestType = springTestType,
51+
springSettings = springSettings,
52+
)
53+
}
54+
55+
@JvmStatic
56+
fun createXmlSpringConfiguration(xmlConfig: File): SpringConfiguration.XMLConfiguration =
57+
SpringConfiguration.XMLConfiguration(xmlConfig.absolutePath)
58+
59+
@JvmStatic
60+
fun createJavaSpringConfiguration(javaConfig: Class<*>): SpringConfiguration.JavaBasedConfiguration =
61+
if (javaConfig.annotations.any { it.annotationClass.java.name in springBootConfigAnnotations }) {
62+
SpringConfiguration.SpringBootConfiguration(javaConfig.name, isDefinitelyUnique = false)
63+
} else {
64+
SpringConfiguration.JavaConfiguration(javaConfig.name)
65+
}
66+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ class CgSpringIntegrationTestClassConstructor(
194194
// TODO: we support only JavaBasedConfiguration in integration tests.
195195
// Adapt for XMLConfigurations when supported.
196196
val configClass = springSettings.configuration as JavaBasedConfiguration
197-
if (configClass is JavaConfiguration || configClass is SpringBootConfiguration && !configClass.isUnique) {
197+
if (configClass is JavaConfiguration || configClass is SpringBootConfiguration && !configClass.isDefinitelyUnique) {
198198
addAnnotation(
199199
classId = contextConfigurationClassId,
200200
namedArguments = listOf(

utbot-spring-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import org.utbot.common.dynamicPropertiesOf
55
import org.utbot.common.isAbstract
66
import org.utbot.common.isStatic
77
import org.utbot.common.withValue
8+
import org.utbot.external.api.UtBotSpringApi
89
import org.utbot.framework.UtSettings
910
import org.utbot.framework.codegen.generator.AbstractCodeGenerator
1011
import org.utbot.framework.codegen.generator.CodeGeneratorParams
@@ -39,14 +40,29 @@ import org.utbot.fuzzing.spring.unit.InjectMockValueProvider
3940
import org.utbot.fuzzing.toFuzzerType
4041
import org.utbot.instrumentation.instrumentation.execution.RemovingConstructFailsUtExecutionInstrumentation
4142

42-
class SpringApplicationContextImpl(
43+
class SpringApplicationContextImpl private constructor(
4344
private val delegateContext: ApplicationContext,
44-
override val beanDefinitions: List<BeanDefinitionData> = emptyList(),
45+
override val beanDefinitions: List<BeanDefinitionData>,
4546
private val springTestType: SpringTestType,
4647
override val springSettings: SpringSettings,
4748
): ApplicationContext by delegateContext, SpringApplicationContext {
4849
companion object {
4950
private val logger = KotlinLogging.logger {}
51+
52+
/**
53+
* Used internally by UtBot to create an instance of [SpringApplicationContextImpl]
54+
* when [beanDefinitions] are already known.
55+
*
56+
* NOTE: Bean definitions defined in config from [springSettings] are IGNORED.
57+
*
58+
* API users should use [UtBotSpringApi.createSpringApplicationContext]
59+
*/
60+
fun internalCreate(
61+
delegateContext: ApplicationContext,
62+
beanDefinitions: List<BeanDefinitionData>,
63+
springTestType: SpringTestType,
64+
springSettings: SpringSettings,
65+
) = SpringApplicationContextImpl(delegateContext, beanDefinitions, springTestType, springSettings)
5066
}
5167

5268
private object ReplacedFuzzedTypeFlag : FuzzedTypeFlag

utbot-spring-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerTask.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import mu.KotlinLogging
44
import org.utbot.framework.plugin.api.BeanAdditionalData
55
import org.utbot.framework.plugin.api.BeanDefinitionData
66
import org.utbot.framework.plugin.api.SpringSettings.PresentSpringSettings
7-
import org.utbot.framework.process.kryo.KryoHelper
87
import org.utbot.rd.use
98
import org.utbot.spring.process.SpringAnalyzerProcess
109

@@ -16,7 +15,7 @@ class SpringAnalyzerTask(
1615
private val logger = KotlinLogging.logger {}
1716
}
1817

19-
override fun perform(kryoHelper: KryoHelper): List<BeanDefinitionData> = try {
18+
override fun perform(): List<BeanDefinitionData> = try {
2019
SpringAnalyzerProcess.createBlocking(classpath).use {
2120
it.getBeanDefinitions(settings)
2221
}.beanDefinitions

utbot-spring-sample/build.gradle

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

0 commit comments

Comments
 (0)