Skip to content

Commit ab88dce

Browse files
authored
Better Bean class name extraction #2161 (#2213)
Remove getBean() usage
1 parent 720c973 commit ab88dce

File tree

10 files changed

+122
-31
lines changed

10 files changed

+122
-31
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1386,10 +1386,10 @@ data class BeanDefinitionData(
13861386
*/
13871387
data class BeanAdditionalData(
13881388
val factoryMethodName: String,
1389+
val parameterTypes: List<String>,
13891390
val configClassFqn: String,
13901391
)
13911392

1392-
13931393
val RefType.isAbstractType
13941394
get() = this.sootClass.isAbstract || this.sootClass.isInterface
13951395

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
@@ -87,7 +87,7 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
8787
springAnalyzerProcess.terminate()
8888
val beanDefinitions = result.beanDefinitions
8989
.map { data ->
90-
val additionalData = data.additionalData?.let { BeanAdditionalData(it.factoryMethodName, it.configClassFqn) }
90+
val additionalData = data.additionalData?.let { BeanAdditionalData(it.factoryMethodName, it.parameterTypes, it.configClassFqn) }
9191
BeanDefinitionData(data.beanName, data.beanTypeFqn, additionalData)
9292
}
9393
.toTypedArray()

utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class EngineProcessModel private constructor(
7575
}
7676

7777

78-
const val serializationHash = 3867600509781920270L
78+
const val serializationHash = 3987813751020865118L
7979

8080
}
8181
override val serializersOwner: ISerializersOwner get() = EngineProcessModel
@@ -186,6 +186,7 @@ val IProtocol.engineProcessModel get() = getOrCreateExtension(EngineProcessModel
186186
*/
187187
data class BeanAdditionalData (
188188
val factoryMethodName: String,
189+
val parameterTypes: List<String>,
189190
val configClassFqn: String
190191
) : IPrintable {
191192
//companion
@@ -196,12 +197,14 @@ data class BeanAdditionalData (
196197
@Suppress("UNCHECKED_CAST")
197198
override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): BeanAdditionalData {
198199
val factoryMethodName = buffer.readString()
200+
val parameterTypes = buffer.readList { buffer.readString() }
199201
val configClassFqn = buffer.readString()
200-
return BeanAdditionalData(factoryMethodName, configClassFqn)
202+
return BeanAdditionalData(factoryMethodName, parameterTypes, configClassFqn)
201203
}
202204

203205
override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: BeanAdditionalData) {
204206
buffer.writeString(value.factoryMethodName)
207+
buffer.writeList(value.parameterTypes) { v -> buffer.writeString(v) }
205208
buffer.writeString(value.configClassFqn)
206209
}
207210

@@ -219,6 +222,7 @@ data class BeanAdditionalData (
219222
other as BeanAdditionalData
220223

221224
if (factoryMethodName != other.factoryMethodName) return false
225+
if (parameterTypes != other.parameterTypes) return false
222226
if (configClassFqn != other.configClassFqn) return false
223227

224228
return true
@@ -227,6 +231,7 @@ data class BeanAdditionalData (
227231
override fun hashCode(): Int {
228232
var __r = 0
229233
__r = __r*31 + factoryMethodName.hashCode()
234+
__r = __r*31 + parameterTypes.hashCode()
230235
__r = __r*31 + configClassFqn.hashCode()
231236
return __r
232237
}
@@ -235,6 +240,7 @@ data class BeanAdditionalData (
235240
printer.println("BeanAdditionalData (")
236241
printer.indent {
237242
print("factoryMethodName = "); factoryMethodName.print(printer); println()
243+
print("parameterTypes = "); parameterTypes.print(printer); println()
238244
print("configClassFqn = "); configClassFqn.print(printer); println()
239245
}
240246
printer.print(")")
@@ -245,7 +251,7 @@ data class BeanAdditionalData (
245251

246252

247253
/**
248-
* #### Generated from [EngineProcessModel.kt:135]
254+
* #### Generated from [EngineProcessModel.kt:136]
249255
*/
250256
data class BeanDefinitionData (
251257
val beanName: String,
@@ -1358,7 +1364,7 @@ data class SetupContextParams (
13581364

13591365

13601366
/**
1361-
* #### Generated from [EngineProcessModel.kt:140]
1367+
* #### Generated from [EngineProcessModel.kt:141]
13621368
*/
13631369
data class SpringAnalyzerResult (
13641370
val beanDefinitions: Array<BeanDefinitionData>

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

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@ import com.intellij.openapi.roots.ProjectFileIndex
1414
import com.intellij.openapi.ui.Messages
1515
import com.intellij.openapi.util.Computable
1616
import com.intellij.openapi.util.text.StringUtil
17-
import com.intellij.psi.JavaPsiFacade
1817
import com.intellij.openapi.vfs.VirtualFile
1918
import com.intellij.psi.PsiClass
2019
import com.intellij.psi.PsiMethod
21-
import com.intellij.psi.search.GlobalSearchScope
20+
import com.intellij.psi.util.PsiUtil
2221
import com.intellij.refactoring.util.classMembers.MemberInfo
2322
import com.intellij.task.ProjectTask
2423
import com.intellij.task.ProjectTaskManager
@@ -41,7 +40,6 @@ import kotlin.io.path.pathString
4140
import mu.KotlinLogging
4241
import org.jetbrains.concurrency.Promise
4342
import org.jetbrains.concurrency.all
44-
import org.jetbrains.concurrency.thenRun
4543
import org.jetbrains.idea.maven.project.MavenProjectsManager
4644
import org.jetbrains.kotlin.idea.base.util.module
4745
import org.utbot.framework.CancellationStrategyType.CANCEL_EVERYTHING
@@ -51,6 +49,7 @@ import org.utbot.framework.UtSettings
5149
import org.utbot.framework.codegen.domain.ProjectType.*
5250
import org.utbot.framework.codegen.domain.TypeReplacementApproach.*
5351
import org.utbot.framework.plugin.api.ApplicationContext
52+
import org.utbot.framework.plugin.api.BeanDefinitionData
5453
import org.utbot.framework.plugin.api.ClassId
5554
import org.utbot.framework.plugin.api.JavaDocCommentStyle
5655
import org.utbot.framework.plugin.api.SpringApplicationContext
@@ -69,6 +68,8 @@ import org.utbot.intellij.plugin.ui.utils.isBuildWithGradle
6968
import org.utbot.intellij.plugin.ui.utils.showErrorDialogLater
7069
import org.utbot.intellij.plugin.ui.utils.testModules
7170
import org.utbot.intellij.plugin.util.IntelliJApiHelper
71+
import org.utbot.intellij.plugin.util.PsiClassHelper
72+
import org.utbot.intellij.plugin.util.isAbstract
7273
import org.utbot.intellij.plugin.util.PluginJdkInfoProvider
7374
import org.utbot.intellij.plugin.util.PluginWorkingDirProvider
7475
import org.utbot.intellij.plugin.util.assertIsNonDispatchThread
@@ -174,8 +175,7 @@ object UtTestsDialogProcessor {
174175
DoNotReplace -> null
175176
is ReplaceIfPossible ->
176177
approach.config.takeUnless { it.endsWith(".xml") }?.let {
177-
JavaPsiFacade.getInstance(project).findClass(it, GlobalSearchScope.projectScope(project)) ?:
178-
error("Can't find configuration class $it")
178+
PsiClassHelper.findClass(it, project) ?: error("Cannot find configuration class $it.")
179179
}
180180
}
181181

@@ -263,10 +263,13 @@ object UtTestsDialogProcessor {
263263
}
264264
val shouldUseImplementors = beanDefinitions.isNotEmpty()
265265

266+
val clarifiedBeanDefinitions =
267+
clarifyBeanDefinitionReturnTypes(beanDefinitions, project)
268+
266269
SpringApplicationContext(
267270
mockFrameworkInstalled,
268271
staticMockingConfigured,
269-
beanDefinitions,
272+
clarifiedBeanDefinitions,
270273
shouldUseImplementors,
271274
)
272275
}
@@ -445,6 +448,60 @@ object UtTestsDialogProcessor {
445448
}
446449
}
447450

451+
private fun clarifyBeanDefinitionReturnTypes(beanDefinitions: List<BeanDefinitionData>, project: Project) =
452+
beanDefinitions.map { bean ->
453+
// Here we extract a real return type.
454+
// E.g. for a method
455+
// public Toy getToy() { return new SpecToy() }
456+
// we want not Toy but SpecToy type.
457+
// If there are more than one return type, we take type from a signature.
458+
// We process beans with present additional data only.
459+
val beanType = runReadAction {
460+
val additionalData = bean.additionalData ?: return@runReadAction null
461+
462+
val configPsiClass =
463+
PsiClassHelper.findClass(additionalData.configClassFqn, project) ?: return@runReadAction null
464+
.also {
465+
logger.warn("Cannot find configuration class ${additionalData.configClassFqn}.")
466+
}
467+
468+
val beanPsiMethod =
469+
configPsiClass
470+
.findMethodsByName(bean.beanName)
471+
.mapNotNull { jvmMethod ->
472+
(jvmMethod as PsiMethod)
473+
.takeIf { method ->
474+
!method.isAbstract && method.body?.isEmpty == false &&
475+
method.parameterList.parameters.map { it.type.canonicalText } == additionalData.parameterTypes
476+
}
477+
}
478+
// Here we try to take a single element
479+
// because we expect no or one method matching previous conditions only.
480+
// If there were two or more similar methods in one class, it would be a weird case.
481+
.singleOrNull()
482+
?: return@runReadAction null
483+
.also {
484+
logger.warn(
485+
"Several similar methods named ${bean.beanName} " +
486+
"were found in ${additionalData.configClassFqn} configuration class."
487+
)
488+
}
489+
490+
val beanTypes =
491+
PsiUtil
492+
.findReturnStatements(beanPsiMethod)
493+
.mapNotNullTo(mutableSetOf()) { stmt -> stmt.returnValue?.type?.canonicalText }
494+
495+
beanTypes.singleOrNull() ?: bean.beanTypeFqn
496+
} ?: return@map bean
497+
498+
BeanDefinitionData(
499+
beanName = bean.beanName,
500+
beanTypeFqn = beanType,
501+
additionalData = bean.additionalData
502+
)
503+
}
504+
448505
private fun errorMessage(className: String?, timeout: Long) = buildString {
449506
appendLine("UnitTestBot failed to generate any test cases for class $className.")
450507
appendLine()

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class EngineProcess private constructor(val project: Project, private val classN
160160
beanName = data.beanName,
161161
beanTypeFqn = data.beanTypeFqn,
162162
additionalData = data.additionalData
163-
?.let { BeanAdditionalData(it.factoryMethodName, it.configClassFqn) }
163+
?.let { BeanAdditionalData(it.factoryMethodName, it.parameterTypes, it.configClassFqn) }
164164
)
165165
}
166166
}

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ package org.utbot.intellij.plugin.util
33
import com.intellij.psi.PsiClass
44
import com.intellij.psi.PsiMember
55
import com.intellij.psi.PsiMethod
6-
import com.intellij.psi.PsiModifier
76
import com.intellij.psi.SyntheticElement
7+
import com.intellij.psi.JavaPsiFacade
8+
import com.intellij.psi.PsiModifier
9+
import com.intellij.psi.search.GlobalSearchScope
10+
import com.intellij.openapi.project.Project
811
import com.intellij.refactoring.util.classMembers.MemberInfo
912
import com.intellij.testIntegration.TestIntegrationUtils
1013
import org.jetbrains.kotlin.asJava.elements.KtLightMember
@@ -83,4 +86,11 @@ fun PsiClass.extractFirstLevelMembers(includeInherited: Boolean): List<MemberInf
8386
}
8487

8588
val PsiClass.isVisible: Boolean
86-
get() = generateSequence(this) { it.containingClass }.none { it.isPrivateOrProtected }
89+
get() = generateSequence(this) { it.containingClass }.none { it.isPrivateOrProtected }
90+
91+
object PsiClassHelper {
92+
fun findClass(name: String, project: Project): PsiClass? =
93+
JavaPsiFacade
94+
.getInstance(project)
95+
.findClass(name, GlobalSearchScope.projectScope(project))
96+
}

utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ object EngineProcessModel : Ext(EngineProcessRoot) {
130130
}
131131
val beanAdditionalData = structdef {
132132
field("factoryMethodName", PredefinedType.string)
133+
field("parameterTypes", immutableList(PredefinedType.string))
133134
field("configClassFqn", PredefinedType.string)
134135
}
135136
val beanDefinitionData = structdef {

utbot-rd/src/main/rdgen/org/utbot/rd/models/SpringAnalyzerModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ object SpringAnalyzerProcessModel : Ext(SpringAnalyzerRoot) {
1414

1515
val beanAdditionalData = structdef {
1616
field("factoryMethodName", PredefinedType.string)
17+
field("parameterTypes", immutableList(PredefinedType.string))
1718
field("configClassFqn", PredefinedType.string)
1819
}
1920

utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerProcessModel.Generated.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class SpringAnalyzerProcessModel private constructor(
5050
}
5151

5252

53-
const val serializationHash = -2275009816925697183L
53+
const val serializationHash = 8934866731594302609L
5454

5555
}
5656
override val serializersOwner: ISerializersOwner get() = SpringAnalyzerProcessModel
@@ -101,6 +101,7 @@ val IProtocol.springAnalyzerProcessModel get() = getOrCreateExtension(SpringAnal
101101
*/
102102
data class BeanAdditionalData (
103103
val factoryMethodName: String,
104+
val parameterTypes: List<String>,
104105
val configClassFqn: String
105106
) : IPrintable {
106107
//companion
@@ -111,12 +112,14 @@ data class BeanAdditionalData (
111112
@Suppress("UNCHECKED_CAST")
112113
override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): BeanAdditionalData {
113114
val factoryMethodName = buffer.readString()
115+
val parameterTypes = buffer.readList { buffer.readString() }
114116
val configClassFqn = buffer.readString()
115-
return BeanAdditionalData(factoryMethodName, configClassFqn)
117+
return BeanAdditionalData(factoryMethodName, parameterTypes, configClassFqn)
116118
}
117119

118120
override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: BeanAdditionalData) {
119121
buffer.writeString(value.factoryMethodName)
122+
buffer.writeList(value.parameterTypes) { v -> buffer.writeString(v) }
120123
buffer.writeString(value.configClassFqn)
121124
}
122125

@@ -134,6 +137,7 @@ data class BeanAdditionalData (
134137
other as BeanAdditionalData
135138

136139
if (factoryMethodName != other.factoryMethodName) return false
140+
if (parameterTypes != other.parameterTypes) return false
137141
if (configClassFqn != other.configClassFqn) return false
138142

139143
return true
@@ -142,6 +146,7 @@ data class BeanAdditionalData (
142146
override fun hashCode(): Int {
143147
var __r = 0
144148
__r = __r*31 + factoryMethodName.hashCode()
149+
__r = __r*31 + parameterTypes.hashCode()
145150
__r = __r*31 + configClassFqn.hashCode()
146151
return __r
147152
}
@@ -150,6 +155,7 @@ data class BeanAdditionalData (
150155
printer.println("BeanAdditionalData (")
151156
printer.indent {
152157
print("factoryMethodName = "); factoryMethodName.print(printer); println()
158+
print("parameterTypes = "); parameterTypes.print(printer); println()
153159
print("configClassFqn = "); configClassFqn.print(printer); println()
154160
}
155161
printer.print(")")
@@ -160,7 +166,7 @@ data class BeanAdditionalData (
160166

161167

162168
/**
163-
* #### Generated from [SpringAnalyzerModel.kt:20]
169+
* #### Generated from [SpringAnalyzerModel.kt:21]
164170
*/
165171
data class BeanDefinitionData (
166172
val beanName: String,
@@ -298,7 +304,7 @@ data class SpringAnalyzerParams (
298304

299305

300306
/**
301-
* #### Generated from [SpringAnalyzerModel.kt:26]
307+
* #### Generated from [SpringAnalyzerModel.kt:27]
302308
*/
303309
data class SpringAnalyzerResult (
304310
val beanDefinitions: Array<BeanDefinitionData>

0 commit comments

Comments
 (0)