Skip to content

Commit dbd5d6b

Browse files
committed
Add support for class fields rendering with annotations
1 parent a3c7648 commit dbd5d6b

File tree

15 files changed

+211
-33
lines changed

15 files changed

+211
-33
lines changed

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/builtin/MockitoBuiltins.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ internal val mockitoClassId = BuiltinClassId(
2020
simpleName = "Mockito",
2121
)
2222

23+
internal val mockClassId = BuiltinClassId(
24+
canonicalName = "org.mockito.Mock",
25+
simpleName = "Mock",
26+
)
27+
28+
internal val injectMocksClassId = BuiltinClassId(
29+
canonicalName = "org.mockito.InjectMocks",
30+
simpleName = "InjectMocks",
31+
)
32+
2333
internal val ongoingStubbingClassId = BuiltinClassId(
2434
canonicalName = "org.mockito.stubbing.OngoingStubbing",
2535
simpleName = "OngoingStubbing",

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/CgElement.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ interface CgElement {
8282
is CgBreakStatement -> visit(element)
8383
is CgContinueStatement -> visit(element)
8484
is CgDeclaration -> visit(element)
85+
is CgFieldDeclaration -> visit(element)
8586
is CgAssignment -> visit(element)
8687
is CgTypeCast -> visit(element)
8788
is CgIsInstance -> visit(element)
@@ -156,8 +157,19 @@ class CgClassBody(
156157
val methodRegions: List<CgMethodsCluster>,
157158
val staticDeclarationRegions: List<CgStaticsRegion>,
158159
val nestedClassRegions: List<CgNestedClassesRegion<*>>,
159-
//TODO: use [CgFieldDeclaration] after PR-1788 merge
160-
val fields: List<CgDeclaration> = emptyList(),
160+
val fields: List<CgFieldDeclaration> = emptyList(),
161+
) : CgElement
162+
163+
/**
164+
* Field of a class.
165+
* @property classId [ClassId] of the field owner class.
166+
* @property declaration declaration itself.
167+
* @property annotation optional annotation.
168+
*/
169+
class CgFieldDeclaration(
170+
val declaration: CgDeclaration,
171+
val annotation: CgAnnotation? = null,
172+
val classId: ClassId? = null,
161173
) : CgElement
162174

163175
/**

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/TestClassModel.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.utbot.framework.codegen.domain.models
22

33
import org.utbot.framework.plugin.api.ClassId
4+
import org.utbot.framework.plugin.api.UtModel
45

56
/**
67
* Stores method test sets in a structure that replicates structure of their methods in [classUnderTest].
@@ -13,7 +14,7 @@ abstract class TestClassModel(
1314
val nestedClasses: List<SimpleTestClassModel>,
1415
)
1516

16-
open class SimpleTestClassModel(
17+
class SimpleTestClassModel(
1718
classUnderTest: ClassId,
1819
methodTestSets: List<CgMethodTestSet>,
1920
nestedClasses: List<SimpleTestClassModel> = listOf(),
@@ -29,7 +30,7 @@ class SpringTestClassModel(
2930
classUnderTest: ClassId,
3031
methodTestSets: List<CgMethodTestSet>,
3132
nestedClasses: List<SimpleTestClassModel>,
32-
val injectingMocksClass: ClassId? = null,
33-
val mockedClasses: Set<ClassId> = setOf(),
33+
val injectedMockModels: Map<ClassId, List<UtModel>> = mapOf(),
34+
val mockedModels: Map<ClassId, List<UtModel>> = mapOf(),
3435
): TestClassModel(classUnderTest, methodTestSets, nestedClasses)
3536

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,38 @@ class SpringTestClassModelBuilder: TestClassModelBuilder() {
2121

2222
override fun createTestClassModel(classUnderTest: ClassId, testSets: List<CgMethodTestSet>): SpringTestClassModel {
2323
val baseModel = SimpleTestClassModelBuilder().createTestClassModel(classUnderTest, testSets)
24-
val mockedClasses = collectMockedClassIds(classUnderTest, testSets)
24+
val (injectedModels, mockedModels) = collectInjectedAndMockedModels(testSets)
2525

2626
return SpringTestClassModel(
2727
baseModel.classUnderTest,
2828
baseModel.methodTestSets,
2929
baseModel.nestedClasses,
30-
classUnderTest,
31-
mockedClasses,
30+
groupUtModelsByClassId(injectedModels),
31+
groupUtModelsByClassId(mockedModels),
3232
)
3333
}
3434

35-
private fun collectMockedClassIds(
36-
classUnderTest: ClassId,
35+
private fun groupUtModelsByClassId(models: List<UtModel>): Map<ClassId, List<UtModel>> {
36+
return models.groupBy { it.classId }
37+
}
38+
39+
private fun collectInjectedAndMockedModels(
3740
testSets: List<CgMethodTestSet>,
38-
): Set<ClassId> {
41+
): Pair<List<UtModel>, List<UtModel>> {
42+
val thisInstances = mutableListOf<UtModel>()
3943
val allModelsInExecution = mutableListOf<UtModel>()
4044

4145
for (testSet in testSets) {
4246
for (execution in testSet.executions) {
43-
execution.stateBefore.thisInstance?.let { allModelsInExecution += it }
44-
execution.stateAfter.thisInstance?.let { allModelsInExecution += it }
47+
execution.stateBefore.thisInstance?.let {
48+
allModelsInExecution += it
49+
thisInstances += it
50+
}
51+
52+
execution.stateAfter.thisInstance?.let {
53+
allModelsInExecution += it
54+
thisInstances += it
55+
}
4556

4657
allModelsInExecution += execution.stateBefore.parameters
4758
allModelsInExecution += execution.stateAfter.parameters
@@ -53,16 +64,17 @@ class SpringTestClassModelBuilder: TestClassModelBuilder() {
5364
val allConstructedModels = mutableSetOf<UtModel>()
5465
allModelsInExecution.forEach { model -> collectRecursively(model, allConstructedModels) }
5566

56-
return allConstructedModels
67+
val injectedModels = thisInstances.filterIsInstance<UtCompositeModel>()
68+
val mockedModels = allConstructedModels
5769
.filter { it.isMockComposite() || it.isMockAssemble() }
58-
.map { it.classId }
59-
.filter { it != classUnderTest }
60-
.toSet()
70+
.filter { it !in injectedModels }
71+
.distinct()
6172

73+
return injectedModels to mockedModels
6274
}
6375

6476
private fun collectRecursively(currentModel: UtModel, allModels: MutableSet<UtModel>) {
65-
if (currentModel in allModels) {
77+
if (allModels.any { it === currentModel }) {
6678
return
6779
}
6880

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/renderer/CgAbstractRenderer.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import org.utbot.framework.codegen.domain.models.CgExecutableCall
4141
import org.utbot.framework.codegen.domain.models.CgMethodsCluster
4242
import org.utbot.framework.codegen.domain.models.CgExpression
4343
import org.utbot.framework.codegen.domain.models.CgFieldAccess
44+
import org.utbot.framework.codegen.domain.models.CgFieldDeclaration
4445
import org.utbot.framework.codegen.domain.models.CgForEachLoop
4546
import org.utbot.framework.codegen.domain.models.CgForLoop
4647
import org.utbot.framework.codegen.domain.models.CgFrameworkUtilMethod
@@ -571,6 +572,14 @@ abstract class CgAbstractRenderer(
571572
println(statementEnding)
572573
}
573574

575+
// Class field declaration
576+
577+
override fun visit(element: CgFieldDeclaration) {
578+
element.annotation?.accept(this)
579+
renderVisibility(element.declaration.variableType)
580+
element.declaration.accept(this)
581+
}
582+
574583
// Variable assignment
575584

576585
override fun visit(element: CgAssignment) {
@@ -888,7 +897,7 @@ abstract class CgAbstractRenderer(
888897
}
889898
}
890899

891-
protected abstract fun renderClassVisibility(classId: ClassId)
900+
protected abstract fun renderVisibility(classId: ClassId)
892901

893902
protected abstract fun renderClassModality(aClass: CgClass)
894903

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/renderer/CgJavaRenderer.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import org.utbot.framework.codegen.domain.models.CgSwitchCase
3232
import org.utbot.framework.codegen.domain.models.CgSwitchCaseLabel
3333
import org.utbot.framework.codegen.domain.models.CgClass
3434
import org.utbot.framework.codegen.domain.models.CgClassBody
35+
import org.utbot.framework.codegen.domain.models.CgFieldDeclaration
3536
import org.utbot.framework.codegen.domain.models.CgFormattedString
3637
import org.utbot.framework.codegen.domain.models.CgFrameworkUtilMethod
3738
import org.utbot.framework.codegen.domain.models.CgLiteral
@@ -70,7 +71,7 @@ internal class CgJavaRenderer(context: CgRendererContext, printer: CgPrinter = C
7071
annotation.accept(this)
7172
}
7273

73-
renderClassVisibility(element.id)
74+
renderVisibility(element.id)
7475
renderClassModality(element)
7576
if (element.isStatic) {
7677
print("static ")
@@ -92,6 +93,12 @@ internal class CgJavaRenderer(context: CgRendererContext, printer: CgPrinter = C
9293
}
9394

9495
override fun visit(element: CgClassBody) {
96+
// render class fields
97+
for (field in element.fields) {
98+
field.accept(this)
99+
println()
100+
}
101+
95102
// render regions for test methods and utils
96103
val allRegions = element.methodRegions + element.nestedClassRegions + element.staticDeclarationRegions
97104
for ((i, region) in allRegions.withIndex()) {
@@ -101,6 +108,12 @@ internal class CgJavaRenderer(context: CgRendererContext, printer: CgPrinter = C
101108
}
102109
}
103110

111+
override fun visit(element: CgFieldDeclaration) {
112+
element.annotation?.accept(this)
113+
renderVisibility(element.declaration.variableType)
114+
element.declaration.accept(this)
115+
}
116+
104117
override fun visit(element: CgArrayAnnotationArgument) {
105118
print("{")
106119
element.values.renderSeparated()
@@ -386,7 +399,7 @@ internal class CgJavaRenderer(context: CgRendererContext, printer: CgPrinter = C
386399

387400
override fun escapeNamePossibleKeywordImpl(s: String): String = s
388401

389-
override fun renderClassVisibility(classId: ClassId) {
402+
override fun renderVisibility(classId: ClassId) {
390403
when {
391404
classId.isPublic -> print("public ")
392405
classId.isProtected -> print("protected ")

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/renderer/CgKotlinRenderer.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import org.utbot.framework.codegen.domain.models.CgSwitchCase
3838
import org.utbot.framework.codegen.domain.models.CgSwitchCaseLabel
3939
import org.utbot.framework.codegen.domain.models.CgClass
4040
import org.utbot.framework.codegen.domain.models.CgClassBody
41+
import org.utbot.framework.codegen.domain.models.CgFieldDeclaration
4142
import org.utbot.framework.codegen.domain.models.CgFormattedString
4243
import org.utbot.framework.codegen.domain.models.CgFrameworkUtilMethod
4344
import org.utbot.framework.codegen.domain.models.CgLiteral
@@ -85,7 +86,7 @@ internal class CgKotlinRenderer(context: CgRendererContext, printer: CgPrinter =
8586
annotation.accept(this)
8687
}
8788

88-
renderClassVisibility(element.id)
89+
renderVisibility(element.id)
8990
renderClassModality(element)
9091
if (!element.isStatic && element.isNested) {
9192
print("inner ")
@@ -199,6 +200,12 @@ internal class CgKotlinRenderer(context: CgRendererContext, printer: CgPrinter =
199200
println(regionEnd)
200201
}
201202

203+
override fun visit(element: CgFieldDeclaration) {
204+
element.annotation?.accept(this)
205+
renderVisibility(element.declaration.variableType)
206+
element.declaration.accept(this)
207+
}
208+
202209

203210
// Property access
204211

@@ -580,7 +587,7 @@ internal class CgKotlinRenderer(context: CgRendererContext, printer: CgPrinter =
580587
override fun escapeNamePossibleKeywordImpl(s: String): String =
581588
if (isLanguageKeyword(s, context.cgLanguageAssistant)) "`$s`" else s
582589

583-
override fun renderClassVisibility(classId: ClassId) {
590+
override fun renderVisibility(classId: ClassId) {
584591
when {
585592
// Kotlin classes are public by default
586593
classId.isPublic -> Unit

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/renderer/CgVisitor.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import org.utbot.framework.codegen.domain.models.CgSwitchCaseLabel
7676
import org.utbot.framework.codegen.domain.models.CgClass
7777
import org.utbot.framework.codegen.domain.models.CgClassBody
7878
import org.utbot.framework.codegen.domain.models.CgDocRegularLineStmt
79+
import org.utbot.framework.codegen.domain.models.CgFieldDeclaration
7980
import org.utbot.framework.codegen.domain.models.CgFormattedString
8081
import org.utbot.framework.codegen.domain.models.CgFrameworkUtilMethod
8182
import org.utbot.framework.codegen.domain.models.CgNestedClassesRegion
@@ -185,6 +186,9 @@ interface CgVisitor<R> {
185186
// Variable declaration
186187
fun visit(element: CgDeclaration): R
187188

189+
// Field declaration
190+
fun visit(element: CgFieldDeclaration): R
191+
188192
// Variable assignment
189193
fun visit(element: CgAssignment): R
190194

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/framework/MockFrameworkManager.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ class MockFrameworkManager(context: CgContext) : CgVariableConstructorComponent(
138138
objectMocker.createMock(model, baseName)
139139
}
140140

141+
fun createMockForVariable(model: UtCompositeModel, variable: CgVariable) =
142+
withMockFramework {
143+
require(model.isMock) { "Mock model is expected in MockObjectConstructor" }
144+
objectMocker.mockForVariable(model, variable)
145+
}
146+
141147
fun mockNewInstance(mock: UtNewInstanceInstrumentation) {
142148
staticMocker?.mockNewInstance(mock)
143149
}
@@ -177,6 +183,12 @@ private class MockitoMocker(context: CgContext) : ObjectMocker(context) {
177183
val modelClass = getClassOf(model.classId)
178184
val mockObject = newVar(model.classId, baseName = baseName, isMock = true) { mock(modelClass) }
179185

186+
mockForVariable(model, mockObject)
187+
188+
return mockObject
189+
}
190+
191+
fun mockForVariable(model: UtCompositeModel, mockObject: CgVariable) {
180192
for ((executable, values) in model.mocks) {
181193
// void method
182194
if (executable.returnType == voidClassId) {
@@ -201,8 +213,6 @@ private class MockitoMocker(context: CgContext) : ObjectMocker(context) {
201213
else -> error("ConstructorId was not expected to appear in simple mocker but got $executable")
202214
}
203215
}
204-
205-
return mockObject
206216
}
207217

208218
override fun mock(clazz: CgExpression): CgMethodCall =

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/CgLanguageAssistant.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import org.utbot.framework.codegen.tree.CgMethodConstructor
1515
import org.utbot.framework.codegen.tree.CgStatementConstructor
1616
import org.utbot.framework.codegen.tree.CgStatementConstructorImpl
1717
import org.utbot.framework.codegen.tree.CgVariableConstructor
18+
import org.utbot.framework.codegen.tree.CgSpringVariableConstructor
1819
import org.utbot.framework.plugin.api.ClassId
1920
import org.utbot.framework.plugin.api.CodegenLanguage
2021

@@ -44,7 +45,8 @@ abstract class CgLanguageAssistant {
4445
open fun getCallableAccessManagerBy(context: CgContext): CgCallableAccessManager =
4546
CgCallableAccessManagerImpl(context)
4647
open fun getStatementConstructorBy(context: CgContext): CgStatementConstructor = CgStatementConstructorImpl(context)
47-
open fun getVariableConstructorBy(context: CgContext): CgVariableConstructor = CgVariableConstructor(context)
48+
open fun getVariableConstructorBy(context: CgContext): CgVariableConstructor =
49+
if (context.isSpringClass) CgSpringVariableConstructor(context) else CgVariableConstructor(context)
4850
open fun getMethodConstructorBy(context: CgContext): CgMethodConstructor = CgMethodConstructor(context)
4951
open fun getCgFieldStateManager(context: CgContext): CgFieldStateManager = CgFieldStateManagerImpl(context)
5052

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/Builders.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import org.utbot.framework.codegen.domain.models.CgElement
1313
import org.utbot.framework.codegen.domain.models.CgErrorTestMethod
1414
import org.utbot.framework.codegen.domain.models.CgExceptionHandler
1515
import org.utbot.framework.codegen.domain.models.CgExpression
16+
import org.utbot.framework.codegen.domain.models.CgFieldDeclaration
1617
import org.utbot.framework.codegen.domain.models.CgForEachLoop
1718
import org.utbot.framework.codegen.domain.models.CgForLoop
1819
import org.utbot.framework.codegen.domain.models.CgLoop
@@ -66,7 +67,7 @@ class CgClassBodyBuilder(val classId: ClassId) : CgBuilder<CgClassBody> {
6667
val methodRegions: MutableList<CgMethodsCluster> = mutableListOf()
6768
val staticDeclarationRegions: MutableList<CgStaticsRegion> = mutableListOf()
6869
val nestedClassRegions: MutableList<CgNestedClassesRegion<*>> = mutableListOf()
69-
val fields: MutableList<CgDeclaration> = mutableListOf()
70+
val fields: MutableList<CgFieldDeclaration> = mutableListOf()
7071

7172
override fun build() = CgClassBody(classId, methodRegions, staticDeclarationRegions, nestedClassRegions, fields)
7273
}

0 commit comments

Comments
 (0)