Skip to content

Use user classpath and Spring Boot in utbot-spring-analyzer #2125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS
* The property is useful only for the IntelliJ IDEs.
* If the property is set in true the spring analyzer process opens a debug port.
* @see runInstrumentedProcessWithDebug
* @see org.utbot.framework.process.SpringAnalyzerProcess
* @see org.utbot.spring.process.SpringAnalyzerProcess
*/
var runSpringAnalyzerProcessWithDebug by getBooleanProperty(false)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,12 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
}.toTypedArray())))
}
watchdog.measureTimeForActiveCall(getSpringBeanQualifiedNames, "Getting Spring bean definitions") { params ->
val springAnalyzerProcess = SpringAnalyzerProcess.createBlocking()
val springAnalyzerProcess = SpringAnalyzerProcess.createBlocking(params.classpath.toList())
val beans = springAnalyzerProcess.terminateOnException { _ ->
springAnalyzerProcess.getBeanQualifiedNames(
params.classpath.toList(),
params.config,
// TODO remove once spring-analyzer learns to find resources on its own, temporarily leaving it here for testing with hardcoded absolute paths
propertyFilesPaths = emptyList(),
xmlConfigurationPaths = emptyList(),
params.useSpringAnalyzer
params.fileStorage,
).toTypedArray()
}
springAnalyzerProcess.terminate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class EngineProcessModel private constructor(

private val __StringArraySerializer = FrameworkMarshallers.String.array()

const val serializationHash = 1955031277042475752L
const val serializationHash = -8371458556124482010L

}
override val serializersOwner: ISerializersOwner get() = EngineProcessModel
Expand Down Expand Up @@ -755,7 +755,7 @@ data class GenerateTestReportResult (
data class GetSpringBeanQualifiedNamesParams (
val classpath: Array<String>,
val config: String,
val useSpringAnalyzer: Boolean
val fileStorage: String?
) : IPrintable {
//companion

Expand All @@ -766,14 +766,14 @@ data class GetSpringBeanQualifiedNamesParams (
override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): GetSpringBeanQualifiedNamesParams {
val classpath = buffer.readArray {buffer.readString()}
val config = buffer.readString()
val useSpringAnalyzer = buffer.readBool()
return GetSpringBeanQualifiedNamesParams(classpath, config, useSpringAnalyzer)
val fileStorage = buffer.readNullable { buffer.readString() }
return GetSpringBeanQualifiedNamesParams(classpath, config, fileStorage)
}

override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: GetSpringBeanQualifiedNamesParams) {
buffer.writeArray(value.classpath) { buffer.writeString(it) }
buffer.writeString(value.config)
buffer.writeBool(value.useSpringAnalyzer)
buffer.writeNullable(value.fileStorage) { buffer.writeString(it) }
}


Expand All @@ -791,7 +791,7 @@ data class GetSpringBeanQualifiedNamesParams (

if (!(classpath contentDeepEquals other.classpath)) return false
if (config != other.config) return false
if (useSpringAnalyzer != other.useSpringAnalyzer) return false
if (fileStorage != other.fileStorage) return false

return true
}
Expand All @@ -800,7 +800,7 @@ data class GetSpringBeanQualifiedNamesParams (
var __r = 0
__r = __r*31 + classpath.contentDeepHashCode()
__r = __r*31 + config.hashCode()
__r = __r*31 + useSpringAnalyzer.hashCode()
__r = __r*31 + if (fileStorage != null) fileStorage.hashCode() else 0
return __r
}
//pretty print
Expand All @@ -809,7 +809,7 @@ data class GetSpringBeanQualifiedNamesParams (
printer.indent {
print("classpath = "); classpath.print(printer); println()
print("config = "); config.print(printer); println()
print("useSpringAnalyzer = "); useSpringAnalyzer.print(printer); println()
print("fileStorage = "); fileStorage.print(printer); println()
}
printer.print(")")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.intellij.task.impl.ProjectTaskList
import com.intellij.util.concurrency.AppExecutorUtil
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.nullize
import com.jetbrains.python.sdk.basePath
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
Expand All @@ -46,7 +47,7 @@ import org.utbot.framework.CancellationStrategyType.NONE
import org.utbot.framework.CancellationStrategyType.SAVE_PROCESSED_RESULTS
import org.utbot.framework.UtSettings
import org.utbot.framework.codegen.domain.ProjectType.*
import org.utbot.framework.codegen.domain.TypeReplacementApproach
import org.utbot.framework.codegen.domain.TypeReplacementApproach.*
import org.utbot.framework.plugin.api.ApplicationContext
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.JavaDocCommentStyle
Expand Down Expand Up @@ -168,8 +169,8 @@ object UtTestsDialogProcessor {

private fun createTests(project: Project, model: GenerateTestsModel) {
val springConfigClass = when (val approach = model.typeReplacementApproach) {
TypeReplacementApproach.DoNotReplace -> null
is TypeReplacementApproach.ReplaceIfPossible ->
DoNotReplace -> null
is ReplaceIfPossible ->
approach.config.takeUnless { it.endsWith(".xml") }?.let {
JavaPsiFacade.getInstance(project).findClass(it, GlobalSearchScope.projectScope(project)) ?:
error("Can't find configuration class $it")
Expand Down Expand Up @@ -227,25 +228,31 @@ object UtTestsDialogProcessor {
val process = EngineProcess.createBlocking(project, classNameToPath)

process.terminateOnException { _ ->
process.setupUtContext(buildDirs + classpathList)
val classpathForClassLoader = buildDirs + classpathList
process.setupUtContext(classpathForClassLoader)
val applicationContext = when (model.projectType) {
Spring -> {
val beanQualifiedNames =
when (val approach = model.typeReplacementApproach) {
TypeReplacementApproach.DoNotReplace -> emptyList()
is TypeReplacementApproach.ReplaceIfPossible ->
DoNotReplace -> emptyList()
is ReplaceIfPossible -> {
// TODO: use common parent path for srcModule and used Spring
// config module if they are different modules
val projectFileStorage = model.srcModule.basePath
process.getSpringBeanQualifiedNames(
buildDirs + classpathList,
classpathForClassLoader,
approach.config,
model.useSpringAnalyzer
).also { logger.info { "Detected Spring Beans: $it" } }
projectFileStorage,
)
}
}
val shouldUseImplementors = model.typeReplacementApproach is ReplaceIfPossible

SpringApplicationContext(
mockFrameworkInstalled,
staticMockingConfigured,
beanQualifiedNames,
shouldUseImplementors = beanQualifiedNames.isNotEmpty(),
shouldUseImplementors,
)
}
else -> ApplicationContext(mockFrameworkInstalled, staticMockingConfigured)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@ class GenerateTestsModel(

lateinit var typeReplacementApproach: TypeReplacementApproach

// TODO remove once issue with spring-analyzer expecting .properties and .xml file paths is resolved
var useSpringAnalyzer = false

val conflictTriggers: ConflictTriggers = ConflictTriggers()

var runGeneratedTestsWithCoverage : Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,10 @@ class EngineProcess private constructor(val project: Project, private val classN
engineModel.setupUtContext.startBlocking(SetupContextParams(classpathForUrlsClassloader))
}

fun getSpringBeanQualifiedNames(classpathList: List<String>, config: String, useSpringAnalyzer: Boolean): List<String> {
fun getSpringBeanQualifiedNames(classpathList: List<String>, config: String, fileStorage: String?): List<String> {
assertReadAccessNotAllowed()
return engineModel.getSpringBeanQualifiedNames.startBlocking(
GetSpringBeanQualifiedNamesParams(classpathList.toTypedArray(), config, useSpringAnalyzer)
GetSpringBeanQualifiedNamesParams(classpathList.toTypedArray(), config, fileStorage)
).toList()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ object EngineProcessModel : Ext(EngineProcessRoot) {
val getSpringBeanQualifiedNamesParams = structdef {
field("classpath", array(PredefinedType.string))
field("config", PredefinedType.string)
field("useSpringAnalyzer", PredefinedType.bool)
field("fileStorage", PredefinedType.string.nullable)
}
val methodDescription = structdef {
field("name", PredefinedType.string)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ object SpringAnalyzerProcessModel : Ext(SpringAnalyzerRoot) {
val springAnalyzerParams = structdef {
field("classpath", array(PredefinedType.string))
field("configuration", PredefinedType.string)
field("propertyFilesPaths", array(PredefinedType.string))
field("xmlConfigurationPaths", array(PredefinedType.string))
field("useSpringAnalyzer", PredefinedType.bool)
field("fileStorage", PredefinedType.string.nullable)
}

val springAnalyzerResult = structdef {
Expand Down
69 changes: 48 additions & 21 deletions utbot-spring-analyzer/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer
import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer

Expand All @@ -18,26 +19,62 @@ java {
targetCompatibility = JavaVersion.VERSION_1_8
}

val withoutSpringConfiguration by configurations.creating {}
val withSpringConfiguration by configurations.creating {
extendsFrom(withoutSpringConfiguration)
}
configurations.implementation.get().extendsFrom(withSpringConfiguration)

dependencies {
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot
implementation("org.springframework.boot:spring-boot:$springBootVersion")

implementation(project(":utbot-rd"))
implementation(project(":utbot-core"))
implementation(project(":utbot-framework-api"))
implementation("com.jetbrains.rd:rd-framework:$rdVersion")
implementation("com.jetbrains.rd:rd-core:$rdVersion")
implementation("commons-logging:commons-logging:$commonsLoggingVersion")
withSpringConfiguration("org.springframework.boot:spring-boot:$springBootVersion")

implementation("io.github.microutils:kotlin-logging:$kotlinLoggingVersion")
implementation("commons-io:commons-io:$commonsIOVersion")

fun ModuleDependency.excludeSlf4jApi() = exclude(group = "org.slf4j", module = "slf4j-api")

withoutSpringConfiguration(project(":utbot-rd")) { excludeSlf4jApi() }
withoutSpringConfiguration(project(":utbot-core")) { excludeSlf4jApi() }
withoutSpringConfiguration(project(":utbot-framework-api")) { excludeSlf4jApi() }
withoutSpringConfiguration("com.jetbrains.rd:rd-framework:$rdVersion") { excludeSlf4jApi() }
withoutSpringConfiguration("com.jetbrains.rd:rd-core:$rdVersion") { excludeSlf4jApi() }
withoutSpringConfiguration("commons-logging:commons-logging:$commonsLoggingVersion") { excludeSlf4jApi() }
withoutSpringConfiguration("commons-io:commons-io:$commonsIOVersion") { excludeSlf4jApi() }
}

application {
mainClass.set("org.utbot.spring.process.SpringAnalyzerProcessMainKt")
}

// see more details about this task -- https://github.com/spring-projects/spring-boot/issues/1828
tasks.shadowJar {
val shadowWithoutSpring by tasks.register<ShadowJar>("shadowJarWithoutSpring") {
configureShadowJar(withoutSpringConfiguration)
archiveFileName.set("utbot-spring-analyzer-shadow.jar")
}

val shadowWithSpring by tasks.register<ShadowJar>("shadowJarWithSpring") {
configureShadowJar(withSpringConfiguration)
archiveFileName.set("utbot-spring-analyzer-with-spring-shadow.jar")
}

val springAnalyzerJar: Configuration by configurations.creating {
isCanBeResolved = false
isCanBeConsumed = true
}

artifacts {
add(springAnalyzerJar.name, shadowWithoutSpring)
add(springAnalyzerJar.name, shadowWithSpring)
}

fun ShadowJar.configureShadowJar(configuration: Configuration) {
// see more details -- https://github.com/johnrengelman/shadow/blob/master/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy
group = "shadow"
from(sourceSets.main.get().output)
exclude("META-INF/INDEX.LIST", "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "module-info.class")

configurations = listOf(configuration)

// see more details -- https://github.com/spring-projects/spring-boot/issues/1828
isZip64 = true
// Required for Spring
mergeServiceFiles()
Expand All @@ -50,14 +87,4 @@ tasks.shadowJar {
})

transform(Log4j2PluginsCacheFileTransformer::class.java)
archiveFileName.set("utbot-spring-analyzer-shadow.jar")
}

val springAnalyzerJar: Configuration by configurations.creating {
isCanBeResolved = false
isCanBeConsumed = true
}

artifacts {
add(springAnalyzerJar.name, tasks.shadowJar)
}

This file was deleted.

Loading