Skip to content

Commit 52efe90

Browse files
committed
Add SpringAnalyzerProcess with RD
1 parent 8ff1930 commit 52efe90

File tree

24 files changed

+829
-159
lines changed

24 files changed

+829
-159
lines changed

.run/Debug All.run.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<toRun name="Run IDEA" type="GradleRunConfiguration" />
44
<toRun name="Listen for Engine Process" type="Remote" />
55
<toRun name="Listen for Instrumented Process" type="Remote" />
6+
<toRun name="Listen for Spring Analyzer Process" type="Remote" />
67
<method v="2" />
78
</configuration>
89
</component>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<component name="ProjectRunConfigurationManager">
2+
<configuration default="false" name="Debug Spring Analyzer Process" type="CompoundRunConfigurationType">
3+
<toRun name="Run IDEA" type="GradleRunConfiguration" />
4+
<toRun name="Listen for Spring Analyzer Process" type="Remote" />
5+
<method v="2" />
6+
</configuration>
7+
</component>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<component name="ProjectRunConfigurationManager">
2+
<configuration default="false" name="Listen for Spring Analyzer Process" type="Remote" folderName="Utility Configurations">
3+
<option name="USE_SOCKET_TRANSPORT" value="true" />
4+
<option name="SERVER_MODE" value="true" />
5+
<option name="SHMEM_ADDRESS" />
6+
<option name="HOST" value="localhost" />
7+
<option name="PORT" value="5007" />
8+
<option name="AUTO_RESTART" value="true" />
9+
<RunnerSettings RunnerId="Debug">
10+
<option name="DEBUG_PORT" value="5007" />
11+
<option name="LOCAL" value="false" />
12+
</RunnerSettings>
13+
<method v="2" />
14+
</configuration>
15+
</component>

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-analyzer-model")

utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,36 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS
296296

297297
// endregion
298298

299+
// region spring analyzer process debug
300+
/**
301+
* Path to custom log4j2 configuration file for SpringAnalyzerProcess.
302+
* By default utbot-intellij/src/main/resources/log4j2.xml is used.
303+
* Also default value is used if provided value is not a file.
304+
*/
305+
var springAnalyzerProcessLogConfigFile by getStringProperty("")
306+
307+
/**
308+
* The property is useful only for the IntelliJ IDEs.
309+
* If the property is set in true the spring analyzer process opens a debug port.
310+
* @see runInstrumentedProcessWithDebug
311+
* @see org.utbot.intellij.plugin.process.EngineProcess
312+
*/
313+
var runSpringAnalyzerProcessWithDebug by getBooleanProperty(false)
314+
315+
/**
316+
* The spring analyzer process JDWP agent's port
317+
* A debugger attaches to the port in order to debug the process.
318+
*/
319+
var springAnalyzerProcessDebugPort by getIntProperty(5007)
320+
321+
/**
322+
* Value of the suspend mode for the JDWP agent of the spring analyzer process.
323+
* If the value is true, the spring analyzer process will suspend until a debugger attaches to it.
324+
*/
325+
var suspendSpringAnalyzerProcessExecutionInDebugMode by getBooleanProperty(true)
326+
327+
// endregion
328+
299329
// region instrumented process debug
300330
/**
301331
* The instrumented process JDWP agent's port of the instrumented process.

utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ object OpenModulesContainer {
1111
init {
1212
modulesContainer = buildList {
1313
openPackage("java.base", "sun.security.util")
14+
openPackage("java.base", "sun.reflect.annotation")
1415
openPackage("java.base", "java.text")
1516
openPackage("java.base", "java.lang.invoke")
1617
openPackage("java.base", "jdk.internal.misc")

utbot-intellij/build.gradle.kts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ repositories {
133133
maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies")
134134
}
135135

136+
val fetchSpringAnalyzerJar: Configuration by configurations.creating {
137+
isCanBeResolved = true
138+
isCanBeConsumed = false
139+
}
140+
136141
dependencies {
137142
implementation(group ="com.jetbrains.rd", name = "rd-framework", version = rdVersion)
138143
implementation(group ="com.jetbrains.rd", name = "rd-core", version = rdVersion)
@@ -144,6 +149,7 @@ dependencies {
144149

145150
implementation(project(":utbot-framework")) { exclude(group = "org.slf4j", module = "slf4j-api") }
146151
implementation(project(":utbot-fuzzers"))
152+
implementation(project(":utbot-spring-analyzer-model"))
147153
//api(project(":utbot-analytics"))
148154
testImplementation("org.mock-server:mockserver-netty:5.4.1")
149155
testApi(project(":utbot-framework"))
@@ -168,6 +174,8 @@ dependencies {
168174

169175
implementation(project(":utbot-android-studio"))
170176

177+
fetchSpringAnalyzerJar(project(":utbot-spring-analyzer", "springAnalyzerJar"))
178+
171179
testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion")
172180
testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion")
173181

@@ -183,4 +191,10 @@ dependencies {
183191
testRuntimeOnly("org.junit.platform:junit-platform-launcher:$junit4PlatformVersion")
184192
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junit5Version")
185193
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:$junit5Version")
186-
}
194+
}
195+
196+
tasks.processResources {
197+
from(fetchSpringAnalyzerJar) {
198+
into("lib")
199+
}
200+
}

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

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import com.intellij.openapi.roots.ProjectFileIndex
1313
import com.intellij.openapi.ui.Messages
1414
import com.intellij.openapi.util.Computable
1515
import com.intellij.openapi.util.text.StringUtil
16+
import com.intellij.psi.JavaPsiFacade
1617
import com.intellij.openapi.vfs.VirtualFile
1718
import com.intellij.psi.PsiClass
1819
import com.intellij.psi.PsiMethod
20+
import com.intellij.psi.search.GlobalSearchScope
1921
import com.intellij.refactoring.util.classMembers.MemberInfo
2022
import com.intellij.task.ProjectTask
2123
import com.intellij.task.ProjectTaskManager
@@ -38,7 +40,7 @@ import kotlin.io.path.pathString
3840
import mu.KotlinLogging
3941
import org.jetbrains.concurrency.Promise
4042
import org.jetbrains.idea.maven.project.MavenProjectsManager
41-
import org.jetbrains.kotlin.idea.util.module
43+
import org.jetbrains.kotlin.idea.base.util.module
4244
import org.utbot.framework.CancellationStrategyType.CANCEL_EVERYTHING
4345
import org.utbot.framework.CancellationStrategyType.NONE
4446
import org.utbot.framework.CancellationStrategyType.SAVE_PROCESSED_RESULTS
@@ -58,6 +60,7 @@ import org.utbot.intellij.plugin.models.GenerateTestsModel
5860
import org.utbot.intellij.plugin.models.packageName
5961
import org.utbot.intellij.plugin.process.EngineProcess
6062
import org.utbot.intellij.plugin.process.RdTestGenerationResult
63+
import org.utbot.intellij.plugin.process.SpringAnalyzerProcess
6164
import org.utbot.intellij.plugin.settings.Settings
6265
import org.utbot.intellij.plugin.ui.GenerateTestsDialogWindow
6366
import org.utbot.intellij.plugin.ui.utils.isBuildWithGradle
@@ -156,7 +159,15 @@ object UtTestsDialogProcessor {
156159
}
157160

158161
private fun createTests(project: Project, model: GenerateTestsModel) {
159-
val promise = compile(project, model.srcClasses.map { it.containingFile.virtualFile }.toTypedArray())
162+
val springConfigClass = when (val approach = model.typeReplacementApproach) {
163+
TypeReplacementApproach.DoNotReplace -> null
164+
is TypeReplacementApproach.ReplaceIfPossible ->
165+
approach.configFqn.takeUnless { it.endsWith(".xml") }?.let {
166+
JavaPsiFacade.getInstance(project).findClass(it, GlobalSearchScope.projectScope(project)) ?:
167+
error("Can't find configuration class $it")
168+
}
169+
}
170+
val promise = compile(project, (model.srcClasses + listOfNotNull(springConfigClass)).map { it.containingFile.virtualFile }.toTypedArray())
160171
promise.onSuccess {
161172
if (it.hasErrors() || it.isAborted)
162173
return@onSuccess
@@ -183,7 +194,7 @@ object UtTestsDialogProcessor {
183194
updateIndicator(indicator, ProgressRange.SOLVING, "Generate tests: read classes", 0.0)
184195

185196
val buildPaths = ReadAction
186-
.nonBlocking<BuildPaths?> { findPaths(model.srcClasses) }
197+
.nonBlocking<BuildPaths?> { findPaths(model.srcClasses, springConfigClass) }
187198
.executeSynchronously()
188199
?: return
189200

@@ -204,23 +215,39 @@ object UtTestsDialogProcessor {
204215

205216
val applicationContext = when (model.projectType) {
206217
Spring -> {
207-
val shouldUseImplementors = when (model.typeReplacementApproach) {
208-
TypeReplacementApproach.DoNotReplace -> false
209-
is TypeReplacementApproach.ReplaceIfPossible -> true
218+
val beanQualifiedNames =
219+
if (!model.useSpringAnalyzer) emptyList()
220+
else when (val approach = model.typeReplacementApproach) {
221+
TypeReplacementApproach.DoNotReplace -> emptyList()
222+
is TypeReplacementApproach.ReplaceIfPossible -> {
223+
val springAnalyzerProcess = SpringAnalyzerProcess.createBlocking(Unit)
224+
225+
springAnalyzerProcess.terminateOnException { _ ->
226+
val beans = springAnalyzerProcess.getBeanQualifiedNames(
227+
classpathList,
228+
approach.configFqn,
229+
emptyList(),
230+
emptyList()
231+
)
232+
invokeLater {
233+
springAnalyzerProcess.terminate()
234+
}
235+
beans
236+
}
237+
}
210238
}
211239

212240
SpringApplicationContext(
213241
mockFrameworkInstalled,
214242
staticMockingConfigured,
215-
// TODO: obtain bean definitions and other info from `utbot-spring-analyzer`
216-
beanQualifiedNames = emptyList(),
217-
shouldUseImplementors = shouldUseImplementors,
243+
beanQualifiedNames,
244+
shouldUseImplementors = beanQualifiedNames.isNotEmpty(),
218245
)
219246
}
220247
else -> ApplicationContext(mockFrameworkInstalled, staticMockingConfigured)
221248
}
222249

223-
val process = EngineProcess.createBlocking(project, classNameToPath)
250+
val process = EngineProcess.createBlocking(EngineProcess.Params(project, classNameToPath))
224251

225252
process.terminateOnException { _ ->
226253
process.setupUtContext(buildDirs + classpathList)
@@ -409,16 +436,23 @@ object UtTestsDialogProcessor {
409436
}
410437
}
411438

412-
private fun findPaths(srcClasses: Set<PsiClass>): BuildPaths? {
439+
private fun findPaths(srcClasses: Set<PsiClass>, springConfigPsiClass: PsiClass?): BuildPaths? {
413440
val srcModule = findSrcModule(srcClasses)
441+
val springConfigModule = springConfigPsiClass?.let { it.module ?: error("Module for spring configuration class not found") }
414442

415-
val buildDirs = CompilerPaths.getOutputPaths(arrayOf(srcModule))
443+
val buildDirs = CompilerPaths.getOutputPaths(setOfNotNull(
444+
srcModule, springConfigModule
445+
).toTypedArray())
416446
.toList()
417447
.filter { Paths.get(it).exists() }
418448
.nullize() ?: return null
419449

420450
val pathsList = OrderEnumerator.orderEntries(srcModule).recursively().pathsList
421451

452+
springConfigModule?.takeIf { it != srcModule }?.let { module ->
453+
pathsList.addAll(OrderEnumerator.orderEntries(module).recursively().pathsList.pathList)
454+
}
455+
422456
val (classpath, classpathList) = if (IntelliJApiHelper.isAndroidStudio()) {
423457
// Filter out manifests from classpath.
424458
val filterPredicate = { it: String ->
@@ -442,6 +476,6 @@ object UtTestsDialogProcessor {
442476
val classpath: String,
443477
val classpathList: List<String>,
444478
val pluginJarsPath: List<String>
445-
// ^ TODO: Now we collect ALL dependent libs and pass them to the instrumented process. Most of them are redundant.
479+
// ^ TODO: Now we collect ALL dependent libs and pass them to the instrumented and spring analyzer processes. Most of them are redundant.
446480
)
447481
}

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/models/GenerateTestsModel.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ class GenerateTestsModel(
5656

5757
lateinit var typeReplacementApproach: TypeReplacementApproach
5858

59+
// TODO remove once issue with spring-analyzer expecting .properties and .xml file paths is resolved
60+
var useSpringAnalyzer = false
61+
5962
val conflictTriggers: ConflictTriggers = ConflictTriggers()
6063

6164
var runGeneratedTestsWithCoverage : Boolean = false

0 commit comments

Comments
 (0)