Skip to content

Commit c4c26f3

Browse files
Introduce Spring codegen instances and collect types of mocked class variables (#1791)
1 parent a4a83f6 commit c4c26f3

File tree

12 files changed

+329
-125
lines changed

12 files changed

+329
-125
lines changed

utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,23 +358,23 @@ class AssembleModelGenerator(private val basePackageName: String) {
358358
private fun assembleMockCompositeModel(compositeModel: UtCompositeModel): UtCompositeModel {
359359
// We have to create a model before the construction of the fields to avoid
360360
// infinite recursion when some mock contains itself as a field.
361-
val assembledModel = UtCompositeModel(
361+
val assembledCompositeModel = UtCompositeModel(
362362
compositeModel.id,
363363
compositeModel.classId,
364364
isMock = true,
365365
)
366366

367-
instantiatedModels[compositeModel] = assembledModel
367+
instantiatedModels[compositeModel] = assembledCompositeModel
368368

369369
val fields = compositeModel.fields.mapValues { assembleModel(it.value) }.toMutableMap()
370370
val mockBehaviour = compositeModel.mocks
371371
.mapValues { models -> models.value.map { assembleModel(it) } }
372372
.toMutableMap()
373373

374-
assembledModel.fields += fields
375-
assembledModel.mocks += mockBehaviour
374+
assembledCompositeModel.fields += fields
375+
assembledCompositeModel.mocks += mockBehaviour
376376

377-
return assembledModel
377+
return assembledCompositeModel
378378
}
379379

380380
/**

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/CodeGenerator.kt

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour
88
import org.utbot.framework.codegen.domain.StaticsMocking
99
import org.utbot.framework.codegen.domain.TestFramework
1010
import org.utbot.framework.codegen.domain.models.CgMethodTestSet
11-
import org.utbot.framework.codegen.domain.models.TestClassModel
1211
import org.utbot.framework.codegen.domain.context.CgContext
12+
import org.utbot.framework.codegen.domain.models.CgClassFile
13+
import org.utbot.framework.codegen.domain.models.builders.SimpleTestClassModelBuilder
14+
import org.utbot.framework.codegen.domain.models.builders.SpringTestClassModelBuilder
1315
import org.utbot.framework.codegen.renderer.CgAbstractRenderer
1416
import org.utbot.framework.codegen.reports.TestsGenerationReport
1517
import org.utbot.framework.codegen.tree.CgSimpleTestClassConstructor
1618
import org.utbot.framework.codegen.tree.ututils.UtilClassKind
1719
import org.utbot.framework.codegen.services.language.CgLanguageAssistant
20+
import org.utbot.framework.codegen.tree.CgSpringTestClassConstructor
1821
import org.utbot.framework.plugin.api.ClassId
1922
import org.utbot.framework.plugin.api.CodegenLanguage
2023
import org.utbot.framework.plugin.api.ExecutableId
@@ -73,29 +76,60 @@ open class CodeGenerator(
7376
val cgTestSets = testSets.map { CgMethodTestSet(it) }.toList()
7477
return withCustomContext(testClassCustomName) {
7578
context.withTestClassFileScope {
76-
val astConstructor = CgSimpleTestClassConstructor(context)
77-
val renderer = CgAbstractRenderer.makeRenderer(context)
78-
val testClassModel = TestClassModel.fromTestSets(classUnderTest, cgTestSets)
79+
if (context.isSpringClass) {
80+
generateForSpringClass(cgTestSets)
81+
} else {
82+
generateForSimpleClass(cgTestSets)
83+
}
84+
}
85+
}
86+
}
7987

80-
fun now() = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS"))
88+
private fun generateForSimpleClass(testSets: List<CgMethodTestSet>): CodeGeneratorResult {
89+
val astConstructor = CgSimpleTestClassConstructor(context)
90+
val testClassModel = SimpleTestClassModelBuilder().createTestClassModel(classUnderTest, testSets)
8191

82-
logger.info { "Code generation phase started at ${now()}" }
83-
val testClassFile = astConstructor.construct(testClassModel)
84-
logger.info { "Code generation phase finished at ${now()}" }
92+
logger.info { "Code generation phase started at ${now()}" }
93+
val testClassFile = astConstructor.construct(testClassModel)
94+
logger.info { "Code generation phase finished at ${now()}" }
8595

86-
logger.info { "Rendering phase started at ${now()}" }
87-
testClassFile.accept(renderer)
88-
logger.info { "Rendering phase finished at ${now()}" }
96+
val generatedCode = renderToString(testClassFile)
8997

90-
CodeGeneratorResult(
91-
generatedCode = renderer.toString(),
92-
utilClassKind = UtilClassKind.fromCgContextOrNull(context),
93-
testsGenerationReport = astConstructor.testsGenerationReport
94-
)
95-
}
96-
}
98+
return CodeGeneratorResult(
99+
generatedCode = generatedCode,
100+
utilClassKind = UtilClassKind.fromCgContextOrNull(context),
101+
testsGenerationReport = astConstructor.testsGenerationReport
102+
)
103+
}
104+
105+
private fun generateForSpringClass(testSets: List<CgMethodTestSet>): CodeGeneratorResult {
106+
val astConstructor = CgSpringTestClassConstructor(context)
107+
val testClassModel = SpringTestClassModelBuilder().createTestClassModel(classUnderTest, testSets)
108+
109+
logger.info { "Code generation phase started at ${now()}" }
110+
val testClassFile = astConstructor.construct(testClassModel)
111+
logger.info { "Code generation phase finished at ${now()}" }
112+
113+
val generatedCode = renderToString(testClassFile)
114+
115+
return CodeGeneratorResult(
116+
generatedCode = generatedCode,
117+
utilClassKind = UtilClassKind.fromCgContextOrNull(context),
118+
testsGenerationReport = TestsGenerationReport()
119+
)
97120
}
98121

122+
private fun renderToString(testClassFile: CgClassFile): String {
123+
logger.info { "Rendering phase started at ${now()}" }
124+
val renderer = CgAbstractRenderer.makeRenderer(context)
125+
testClassFile.accept(renderer)
126+
logger.info { "Rendering phase finished at ${now()}" }
127+
128+
return renderer.toString()
129+
}
130+
131+
private fun now() = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS"))
132+
99133
/**
100134
* Wrapper function that configures context as needed for utbot-online:
101135
* - turns on imports optimization in code generator

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import org.utbot.framework.codegen.domain.models.CgMethodTestSet
2626
import org.utbot.framework.codegen.domain.builtin.TestClassUtilMethodProvider
2727
import org.utbot.framework.codegen.domain.builtin.UtilClassFileMethodProvider
2828
import org.utbot.framework.codegen.domain.builtin.UtilMethodProvider
29-
import org.utbot.framework.codegen.domain.models.TestClassModel
29+
import org.utbot.framework.codegen.domain.models.SimpleTestClassModel
3030
import org.utbot.framework.codegen.domain.models.CgParameterKind
3131
import org.utbot.framework.codegen.services.access.Block
3232
import org.utbot.framework.codegen.tree.EnvironmentFieldStateCache
@@ -66,6 +66,9 @@ interface CgContextOwner {
6666
// current class under test
6767
val classUnderTest: ClassId
6868

69+
// If test class is configured with Spring, we should do some extra analysis
70+
val isSpringClass: Boolean
71+
6972
// test class currently being generated (if series of nested classes is generated, it is the outermost one)
7073
val outerMostTestClass: ClassId
7174

@@ -292,7 +295,7 @@ interface CgContextOwner {
292295
* This method does almost all the same as [withTestClassScope], but for nested test classes.
293296
* The difference is that instead of working with [outerMostTestClassContext] it works with [currentTestClassContext].
294297
*/
295-
fun <R> withNestedClassScope(testClassModel: TestClassModel, block: () -> R): R
298+
fun <R> withNestedClassScope(testClassModel: SimpleTestClassModel, block: () -> R): R
296299

297300
/**
298301
* Set [mockFrameworkUsed] flag to true if the block is successfully executed
@@ -431,6 +434,7 @@ interface CgContextOwner {
431434
*/
432435
data class CgContext(
433436
override val classUnderTest: ClassId,
437+
override val isSpringClass: Boolean = false,
434438
val generateUtilClassFile: Boolean = false,
435439
override var currentExecutable: ExecutableId? = null,
436440
override val collectedExceptions: MutableSet<ClassId> = mutableSetOf(),
@@ -538,7 +542,7 @@ data class CgContext(
538542
}
539543
}
540544

541-
override fun <R> withNestedClassScope(testClassModel: TestClassModel, block: () -> R): R {
545+
override fun <R> withNestedClassScope(testClassModel: SimpleTestClassModel, block: () -> R): R {
542546
val previousCurrentTestClassInfo = currentTestClassContext
543547
val previousCurrentTestClass = currentTestClass
544548
currentTestClass = createClassIdForNestedClass(testClassModel)
@@ -551,7 +555,7 @@ data class CgContext(
551555
}
552556
}
553557

554-
private fun createClassIdForNestedClass(testClassModel: TestClassModel): ClassId {
558+
private fun createClassIdForNestedClass(testClassModel: SimpleTestClassModel): ClassId {
555559
val simpleName = "${testClassModel.classUnderTest.simpleName}Test"
556560
return BuiltinClassId(
557561
canonicalName = currentTestClass.canonicalName + "." + simpleName,
Lines changed: 22 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,35 @@
11
package org.utbot.framework.codegen.domain.models
22

33
import org.utbot.framework.plugin.api.ClassId
4-
import org.utbot.framework.plugin.api.util.enclosingClass
54

6-
// TODO: seems like this class needs to be renamed
75
/**
8-
* Stores method testsets in a structure that replicates structure of their methods in [classUnderTest].
6+
* Stores method test sets in a structure that replicates structure of their methods in [classUnderTest].
97
* I.e., if some method is declared in nested class of [classUnderTest], its testset will be put
108
* in [TestClassModel] in one of [nestedClasses]
119
*/
12-
data class TestClassModel(
10+
abstract class TestClassModel(
1311
val classUnderTest: ClassId,
1412
val methodTestSets: List<CgMethodTestSet>,
15-
val nestedClasses: List<TestClassModel> = listOf()
16-
) {
17-
companion object {
18-
fun fromTestSets(classUnderTest: ClassId, testSets: List<CgMethodTestSet>): TestClassModel {
19-
// For each class stores list of methods declared in this class (methods from nested classes are excluded)
20-
val class2methodTestSets = testSets.groupBy { it.executableId.classId }
13+
val nestedClasses: List<SimpleTestClassModel>,
14+
)
2115

22-
val classesWithMethodsUnderTest = testSets
23-
.map { it.executableId.classId }
24-
.distinct()
16+
open class SimpleTestClassModel(
17+
classUnderTest: ClassId,
18+
methodTestSets: List<CgMethodTestSet>,
19+
nestedClasses: List<SimpleTestClassModel> = listOf(),
20+
): TestClassModel(classUnderTest, methodTestSets, nestedClasses)
2521

26-
// For each class stores list of its "direct" nested classes
27-
val class2nestedClasses = mutableMapOf<ClassId, MutableSet<ClassId>>()
28-
29-
for (classId in classesWithMethodsUnderTest) {
30-
var currentClass = classId
31-
var enclosingClass = currentClass.enclosingClass
32-
// while we haven't reached the top of nested class hierarchy or the main class under test
33-
while (enclosingClass != null && currentClass != classUnderTest) {
34-
class2nestedClasses.getOrPut(enclosingClass) { mutableSetOf() } += currentClass
35-
currentClass = enclosingClass
36-
enclosingClass = enclosingClass.enclosingClass
37-
}
38-
}
39-
return constructRecursively(classUnderTest, class2methodTestSets, class2nestedClasses)
40-
}
22+
/**
23+
* Extended [SimpleTestClassModel] for Spring analysis reasons
24+
*
25+
* @param injectingMocksClass a class to inject other mocks into
26+
* @param mockedClasses variables of test class to represent mocked instances
27+
*/
28+
class SpringTestClassModel(
29+
classUnderTest: ClassId,
30+
methodTestSets: List<CgMethodTestSet>,
31+
nestedClasses: List<SimpleTestClassModel>,
32+
val injectingMocksClass: ClassId? = null,
33+
val mockedClasses: Set<ClassId> = setOf(),
34+
): TestClassModel(classUnderTest, methodTestSets, nestedClasses)
4135

42-
private fun constructRecursively(
43-
clazz: ClassId,
44-
class2methodTestSets: Map<ClassId, List<CgMethodTestSet>>,
45-
class2nestedClasses: Map<ClassId, Set<ClassId>>
46-
): TestClassModel {
47-
val currentNestedClasses = class2nestedClasses.getOrDefault(clazz, listOf())
48-
val currentMethodTestSets = class2methodTestSets.getOrDefault(clazz, listOf())
49-
return TestClassModel(
50-
clazz,
51-
currentMethodTestSets,
52-
currentNestedClasses.map {
53-
constructRecursively(it, class2methodTestSets, class2nestedClasses)
54-
}
55-
)
56-
}
57-
}
58-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.utbot.framework.codegen.domain.models.builders
2+
3+
import org.utbot.framework.codegen.domain.models.CgMethodTestSet
4+
import org.utbot.framework.codegen.domain.models.SimpleTestClassModel
5+
import org.utbot.framework.plugin.api.ClassId
6+
import org.utbot.framework.plugin.api.util.enclosingClass
7+
8+
open class SimpleTestClassModelBuilder: TestClassModelBuilder() {
9+
override fun createTestClassModel(classUnderTest: ClassId, testSets: List<CgMethodTestSet>): SimpleTestClassModel {
10+
// For each class stores list of methods declared in this class (methods from nested classes are excluded)
11+
val class2methodTestSets = testSets.groupBy { it.executableId.classId }
12+
13+
val classesWithMethodsUnderTest = testSets
14+
.map { it.executableId.classId }
15+
.distinct()
16+
17+
// For each class stores list of its "direct" nested classes
18+
val class2nestedClasses = mutableMapOf<ClassId, MutableSet<ClassId>>()
19+
20+
for (classId in classesWithMethodsUnderTest) {
21+
var currentClass = classId
22+
var enclosingClass = currentClass.enclosingClass
23+
// while we haven't reached the top of nested class hierarchy or the main class under test
24+
while (enclosingClass != null && currentClass != classUnderTest) {
25+
class2nestedClasses.getOrPut(enclosingClass) { mutableSetOf() } += currentClass
26+
currentClass = enclosingClass
27+
enclosingClass = enclosingClass.enclosingClass
28+
}
29+
}
30+
31+
return constructRecursively(classUnderTest, class2methodTestSets, class2nestedClasses)
32+
}
33+
34+
private fun constructRecursively(
35+
clazz: ClassId,
36+
class2methodTestSets: Map<ClassId, List<CgMethodTestSet>>,
37+
class2nestedClasses: Map<ClassId, Set<ClassId>>
38+
): SimpleTestClassModel {
39+
val currentNestedClasses = class2nestedClasses.getOrDefault(clazz, listOf())
40+
val currentMethodTestSets = class2methodTestSets.getOrDefault(clazz, listOf())
41+
return SimpleTestClassModel(
42+
clazz,
43+
currentMethodTestSets,
44+
currentNestedClasses.map {
45+
constructRecursively(it, class2methodTestSets, class2nestedClasses)
46+
}
47+
)
48+
}
49+
}

0 commit comments

Comments
 (0)