Skip to content

Commit 1a605eb

Browse files
authored
Avoid resetting repository beans #2577 (#2578)
1 parent ed7d870 commit 1a605eb

File tree

1 file changed

+26
-17
lines changed

1 file changed

+26
-17
lines changed

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class SpringUtExecutionInstrumentation(
5050
private val userSourcesClassLoader = URLClassLoader(buildDirs, null)
5151

5252
private val relatedBeansCache = mutableMapOf<Class<*>, Set<String>>()
53+
private val nonRepositoryRelatedBeansCache = mutableMapOf<Class<*>, Set<String>>()
54+
private val repositoryDescriptionsCache = mutableMapOf<Class<*>, Set<SpringRepositoryId>>()
5355

5456
private val springApi: SpringApi get() = instrumentationContext.springApi
5557

@@ -69,7 +71,7 @@ class SpringUtExecutionInstrumentation(
6971
}
7072
}
7173

72-
fun tryLoadingSpringContext(): ConcreteContextLoadingResult {
74+
private fun tryLoadingSpringContext(): ConcreteContextLoadingResult {
7375
val apiProviderResult = instrumentationContext.springApiProviderResult
7476
return ConcreteContextLoadingResult(
7577
contextLoaded = apiProviderResult.result.isSuccess,
@@ -100,21 +102,24 @@ class SpringUtExecutionInstrumentation(
100102
detectedMockingCandidates = emptySet()
101103
)
102104

103-
getRelevantBeans(clazz).forEach { beanName -> springApi.resetBean(beanName) }
104-
return delegateInstrumentation.invoke(clazz, methodSignature, arguments, parameters) { invokeBasePhases ->
105-
phasesWrapper {
106-
// NB! beforeTestMethod() and afterTestMethod() are intentionally called inside phases,
107-
// so they are executed in one thread with method under test
108-
// NB! beforeTestMethod() and afterTestMethod() are executed without timeout, because:
109-
// - if the invokeBasePhases() times out, we still want to execute afterTestMethod()
110-
// - first call to beforeTestMethod() can take significant amount of time due to class loading & transformation
111-
executePhaseWithoutTimeout(SpringBeforeTestMethodPhase) { springApi.beforeTestMethod() }
112-
try {
113-
invokeBasePhases()
114-
} finally {
115-
executePhaseWithoutTimeout(SpringAfterTestMethodPhase) { springApi.afterTestMethod() }
105+
return try {
106+
delegateInstrumentation.invoke(clazz, methodSignature, arguments, parameters) { invokeBasePhases ->
107+
phasesWrapper {
108+
// NB! beforeTestMethod() and afterTestMethod() are intentionally called inside phases,
109+
// so they are executed in one thread with method under test
110+
// NB! beforeTestMethod() and afterTestMethod() are executed without timeout, because:
111+
// - if the invokeBasePhases() times out, we still want to execute afterTestMethod()
112+
// - first call to beforeTestMethod() can take significant amount of time due to class loading & transformation
113+
executePhaseWithoutTimeout(SpringBeforeTestMethodPhase) { springApi.beforeTestMethod() }
114+
try {
115+
invokeBasePhases()
116+
} finally {
117+
executePhaseWithoutTimeout(SpringAfterTestMethodPhase) { springApi.afterTestMethod() }
118+
}
116119
}
117120
}
121+
} finally {
122+
getNonRepositoryRelevantBeans(clazz).forEach { beanName -> springApi.resetBean(beanName) }
118123
}
119124
}
120125

@@ -131,8 +136,12 @@ class SpringUtExecutionInstrumentation(
131136
.also { logger.info { "Detected relevant beans for class ${clazz.name}: $it" } }
132137
}
133138

134-
fun getRepositoryDescriptions(classId: ClassId): Set<SpringRepositoryId> {
135-
val relevantBeanNames = getRelevantBeans(classId.jClass)
139+
private fun getNonRepositoryRelevantBeans(clazz: Class<*>): Set<String> = nonRepositoryRelatedBeansCache.getOrPut(clazz) {
140+
getRelevantBeans(clazz).subtract(getRepositoryDescriptions(clazz).map { it.repositoryBeanName }.toSet())
141+
}
142+
143+
private fun getRepositoryDescriptions(clazz: Class<*>): Set<SpringRepositoryId> = repositoryDescriptionsCache.getOrPut(clazz) {
144+
val relevantBeanNames = getRelevantBeans(clazz)
136145
val repositoryDescriptions = springApi.resolveRepositories(relevantBeanNames.toSet(), userSourcesClassLoader)
137146
return repositoryDescriptions.map { repositoryDescription ->
138147
SpringRepositoryId(
@@ -168,7 +177,7 @@ class SpringUtExecutionInstrumentation(
168177
override fun InstrumentedProcessModel.setupAdditionalRdResponses(kryoHelper: KryoHelper, watchdog: IdleWatchdog) {
169178
watchdog.measureTimeForActiveCall(getRelevantSpringRepositories, "Getting Spring repositories") { params ->
170179
val classId: ClassId = kryoHelper.readObject(params.classId)
171-
val repositoryDescriptions = getRepositoryDescriptions(classId)
180+
val repositoryDescriptions = getRepositoryDescriptions(classId.jClass)
172181
GetSpringRepositoriesResult(kryoHelper.writeObject(repositoryDescriptions))
173182
}
174183
watchdog.measureTimeForActiveCall(tryLoadingSpringContext, "Trying to load Spring application context") { params ->

0 commit comments

Comments
 (0)