Skip to content

Commit da4ba4a

Browse files
Support @SpringBootApplication config type #1974 (#2181)
Co-authored-by: Egor Kulikov <egor.k.kulikov@gmail.com>
1 parent b022a5b commit da4ba4a

File tree

5 files changed

+62
-42
lines changed

5 files changed

+62
-42
lines changed

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -224,16 +224,21 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m
224224
)
225225

226226
private fun shortenConfigurationNames(): Set<Pair<String?, Collection<String>>> {
227-
val shortenedSortedSpringConfigurationClasses =
228-
javaConfigurationHelper.shortenSpringConfigNames(model.getSortedSpringConfigurationClasses())
227+
val springBootApplicationClasses = model.getSortedSpringBootApplicationClasses()
228+
val configurationClasses = model.getSortedSpringConfigurationClasses()
229+
val xmlConfigurationFiles = model.getSpringXMLConfigurationFiles()
230+
231+
val shortenedJavaConfigurationClasses =
232+
javaConfigurationHelper.shortenSpringConfigNames(springBootApplicationClasses + configurationClasses)
229233

230234
val shortenedSpringXMLConfigurationFiles =
231-
xmlConfigurationHelper.shortenSpringConfigNames(model.getSpringXMLConfigurationFiles())
235+
xmlConfigurationHelper.shortenSpringConfigNames(xmlConfigurationFiles)
232236

233237
return setOf(
234238
null to listOf(NO_SPRING_CONFIGURATION_OPTION),
235-
"Java-based configurations" to shortenedSortedSpringConfigurationClasses,
236-
"XML-based configurations" to shortenedSpringXMLConfigurationFiles
239+
"@SpringBootApplication" to springBootApplicationClasses.map(shortenedJavaConfigurationClasses::getValue),
240+
"@Configuration" to configurationClasses.map(shortenedJavaConfigurationClasses::getValue),
241+
"XML configuration" to xmlConfigurationFiles.map(shortenedSpringXMLConfigurationFiles::getValue)
237242
)
238243
}
239244

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/SpringConfigurationsHelper.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class SpringConfigurationsHelper(val separator: String) {
4242
?.fullName
4343
?: error("Full name of configuration file cannot be restored by shortened name $shortenedName")
4444

45-
fun shortenSpringConfigNames(fullNames: Set<String>): Set<String> {
45+
fun shortenSpringConfigNames(fullNames: Set<String>): Map<String, String> {
4646
fullNames.forEach { nameToInfo[it] = NameInfo(it) }
4747
var nameInfoCollection = nameToInfo.values
4848

@@ -87,6 +87,6 @@ class SpringConfigurationsHelper(val separator: String) {
8787
return collectShortenedNames()
8888
}
8989

90-
private fun collectShortenedNames() = nameToInfo.values.mapTo(mutableSetOf()) { it.shortenedName }
90+
private fun collectShortenedNames() = nameToInfo.values.associate { it.fullName to it.shortenedName }
9191

9292
}

utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/configurators/ApplicationType.kt renamed to utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/configurators/ApplicationConfigurationType.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ enum class ApplicationConfigurationType {
44

55
XmlConfiguration,
66

7+
/**
8+
* Any Java-based configuration, including both simple @Configuration and @SpringBootApplication
9+
*/
710
JavaConfiguration,
8-
9-
SpringBootConfiguration
1011
}

utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/utils/SourceFinder.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ open class SourceFinder(
2323
configurationManager.patchImportResourceAnnotation(Path(applicationData.configurationFile).fileName)
2424
arrayOf(TestApplicationConfiguration::class.java)
2525
}
26-
else -> {
26+
ApplicationConfigurationType.JavaConfiguration -> {
2727
logger.info { "Using java Spring configuration" }
2828
arrayOf(
2929
TestApplicationConfiguration::class.java,
@@ -32,7 +32,6 @@ open class SourceFinder(
3232
}
3333
}
3434

35-
//TODO: support Spring Boot Applications here.
3635
private val configurationType: ApplicationConfigurationType
3736
get() = when (File(applicationData.configurationFile).extension) {
3837
"xml" -> ApplicationConfigurationType.XmlConfiguration

utbot-ui-commons/src/main/kotlin/org/utbot/intellij/plugin/models/BaseTestModel.kt

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ import kotlin.streams.asSequence
3535
val PsiClass.packageName: String get() = this.containingFile.containingDirectory.getPackage()?.qualifiedName ?: ""
3636
const val HISTORY_LIMIT = 10
3737

38+
const val SPRINGBOOT_APPLICATION_FQN = "org.springframework.boot.autoconfigure.SpringBootApplication"
39+
const val SPRINGBOOT_CONFIGURATION_FQN = "org.springframework.boot.SpringBootConfiguration"
40+
const val SPRING_CONFIGURATION_ANNOTATION = "org.springframework.context.annotation.Configuration"
41+
const val SPRING_TESTCONFIGURATION_ANNOTATION = "org.springframework.boot.test.context.TestConfiguration"
42+
43+
const val SPRING_BEANS_SCHEMA_URL = "http://www.springframework.org/schema/beans"
44+
const val SPRING_LOAD_DTD_GRAMMAR_PROPERTY = "http://apache.org/xml/features/nonvalidating/load-dtd-grammar"
45+
const val SPRING_LOAD_EXTERNAL_DTD_PROPERTY = "http://apache.org/xml/features/nonvalidating/load-external-dtd"
46+
3847
open class BaseTestsModel(
3948
val project: Project,
4049
val srcModule: Module,
@@ -84,46 +93,52 @@ open class BaseTestsModel(
8493
)
8594

8695
/**
87-
* Searches configuration classes in Spring application.
96+
* Finds @SpringBootApplication classes in Spring application.
8897
*
89-
* Classes are selected and sorted in the following order:
90-
* - Classes marked with `@TestConfiguration` annotation
91-
* - Classes marked with `@Configuration` annotation
92-
* - firstly, from test source roots (in the order provided by [getSortedTestRoots])
93-
* - after that, from source roots
98+
* @see [getSortedAnnotatedClasses]
9499
*/
95-
fun getSortedSpringConfigurationClasses(): Set<String> {
96-
val testRootToIndex = getSortedTestRoots().withIndex().associate { (i, root) -> root.dir to i }
100+
fun getSortedSpringBootApplicationClasses(): Set<String> =
101+
getSortedAnnotatedClasses(SPRINGBOOT_CONFIGURATION_FQN) + getSortedAnnotatedClasses(SPRINGBOOT_APPLICATION_FQN)
97102

98-
// Not using `srcModule.testModules(project)` here because it returns
99-
// test modules for dependent modules if no test roots are found in the source module itself.
100-
// We don't want to search configurations there because they seem useless.
101-
val testModules = ModuleManager.getInstance(project)
102-
.modules
103-
.filter { module -> TestModuleProperties.getInstance(module).productionModule == srcModule }
103+
/**
104+
* Finds @TestConfiguration and @Configuration classes in Spring application.
105+
*
106+
* @see [getSortedAnnotatedClasses]
107+
*/
108+
fun getSortedSpringConfigurationClasses(): Set<String> =
109+
getSortedAnnotatedClasses(SPRING_TESTCONFIGURATION_ANNOTATION) + getSortedAnnotatedClasses(SPRING_CONFIGURATION_ANNOTATION)
104110

105-
val searchScope = testModules.fold(GlobalSearchScope.moduleScope(srcModule)) { accScope, module ->
111+
/**
112+
* Finds classes annotated with given annotation in [srcModule] and [potentialTestModules].
113+
*
114+
* Sorting order:
115+
* - classes from test source roots (in the order provided by [getSortedTestRoots])
116+
* - classes from production source roots
117+
*/
118+
private fun getSortedAnnotatedClasses(annotationFqn: String): Set<String> {
119+
val searchScope = potentialTestModules.fold(GlobalSearchScope.moduleScope(srcModule)) { accScope, module ->
106120
accScope.union(GlobalSearchScope.moduleScope(module))
107121
}
108122

109-
val annotationClasses = listOf(
110-
"org.springframework.boot.test.context.TestConfiguration",
111-
"org.springframework.context.annotation.Configuration"
112-
).mapNotNull {
113-
JavaPsiFacade.getInstance(project).findClass(it, GlobalSearchScope.allScope(project))
114-
}
123+
val annotationClass = JavaPsiFacade
124+
.getInstance(project)
125+
.findClass(annotationFqn, GlobalSearchScope.allScope(project)) ?: return emptySet()
126+
127+
val testRootToIndex = getSortedTestRoots().withIndex().associate { (i, root) -> root.dir to i }
115128

116-
return annotationClasses.flatMap { annotation ->
117-
AnnotatedElementsSearch
118-
.searchPsiClasses(annotation, searchScope)
119-
.findAll()
120-
.sortedBy { testRootToIndex[it.containingFile.sourceRoot] ?: Int.MAX_VALUE }
121-
}.mapNotNullTo(mutableSetOf()) { it.qualifiedName }
129+
return AnnotatedElementsSearch
130+
.searchPsiClasses(annotationClass, searchScope)
131+
.findAll()
132+
.sortedBy { testRootToIndex[it.containingFile.sourceRoot] ?: Int.MAX_VALUE }
133+
.mapNotNullTo(mutableSetOf()) { it.qualifiedName }
122134
}
123135

124136
fun getSpringXMLConfigurationFiles(): Set<String> {
125137
val resourcesPaths =
126-
setOf(testModule, srcModule).flatMapTo(mutableSetOf()) { it.getResourcesPaths() }
138+
buildList {
139+
addAll(potentialTestModules)
140+
add(srcModule)
141+
}.distinct().flatMapTo(mutableSetOf()) { it.getResourcesPaths() }
127142
val xmlFilePaths = resourcesPaths.flatMapTo(mutableListOf()) { path ->
128143
Files.walk(path)
129144
.asSequence()
@@ -136,7 +151,7 @@ open class BaseTestsModel(
136151
val doc = builder.parse(path.toFile())
137152

138153
val hasBeanTagName = doc.documentElement.tagName == "beans"
139-
val hasAttribute = doc.documentElement.getAttribute("xmlns") == "http://www.springframework.org/schema/beans"
154+
val hasAttribute = doc.documentElement.getAttribute("xmlns") == SPRING_BEANS_SCHEMA_URL
140155
when {
141156
hasBeanTagName && hasAttribute -> path.toString()
142157
else -> null
@@ -162,8 +177,8 @@ open class BaseTestsModel(
162177
builderFactory.isNamespaceAware = true
163178

164179
// See documentation https://xerces.apache.org/xerces2-j/features.html
165-
builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false)
166-
builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
180+
builderFactory.setFeature(SPRING_LOAD_DTD_GRAMMAR_PROPERTY, false)
181+
builderFactory.setFeature(SPRING_LOAD_EXTERNAL_DTD_PROPERTY, false)
167182

168183
return builderFactory.newDocumentBuilder()
169184
}

0 commit comments

Comments
 (0)