Skip to content

Commit a0e145f

Browse files
authored
Initilial implementation of Spring test classes code generation (#1788)
1 parent 765da9a commit a0e145f

File tree

23 files changed

+351
-101
lines changed

23 files changed

+351
-101
lines changed

utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,7 +1502,7 @@ class Traverser(
15021502
return createMockedObject(addr, type, mockInfoGenerator, nullEqualityConstraint)
15031503
}
15041504

1505-
val concreteImplementation = when (applicationContext.typeReplacementMode) {
1505+
val concreteImplementation: Concrete? = when (applicationContext.typeReplacementMode) {
15061506
AnyImplementor -> findConcreteImplementation(addr, type, typeHardConstraint, nullEqualityConstraint)
15071507

15081508
// If our type is not abstract, both in `KnownImplementors` and `NoImplementors` mode,
@@ -1520,14 +1520,14 @@ class Traverser(
15201520
NoImplementors -> {
15211521
if (!type.isAbstractType) {
15221522
findConcreteImplementation(addr, type, typeHardConstraint, nullEqualityConstraint)
1523-
}
1523+
} else {
1524+
mockInfoGenerator?.let {
1525+
return createMockedObject(addr, type, it, nullEqualityConstraint)
1526+
}
15241527

1525-
mockInfoGenerator?.let {
1526-
return createMockedObject(addr, type, it, nullEqualityConstraint)
1528+
queuedSymbolicStateUpdates += mkFalse().asHardConstraint()
1529+
null
15271530
}
1528-
1529-
queuedSymbolicStateUpdates += mkFalse().asHardConstraint()
1530-
null
15311531
}
15321532
}
15331533

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ open class CodeGenerator(
9090

9191
private fun generateForSimpleClass(testSets: List<CgMethodTestSet>): CodeGeneratorResult {
9292
val astConstructor = CgSimpleTestClassConstructor(context)
93-
val testClassModel = SimpleTestClassModelBuilder().createTestClassModel(classUnderTest, testSets)
93+
val testClassModel = SimpleTestClassModelBuilder(context).createTestClassModel(classUnderTest, testSets)
9494

9595
logger.info { "Code generation phase started at ${now()}" }
9696
val testClassFile = astConstructor.construct(testClassModel)
@@ -107,7 +107,7 @@ open class CodeGenerator(
107107

108108
private fun generateForSpringClass(testSets: List<CgMethodTestSet>): CodeGeneratorResult {
109109
val astConstructor = CgSpringTestClassConstructor(context)
110-
val testClassModel = SpringTestClassModelBuilder().createTestClassModel(classUnderTest, testSets)
110+
val testClassModel = SpringTestClassModelBuilder(context).createTestClassModel(classUnderTest, testSets)
111111

112112
logger.info { "Code generation phase started at ${now()}" }
113113
val testClassFile = astConstructor.construct(testClassModel)

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.utbot.framework.codegen.domain
33
import org.utbot.framework.DEFAULT_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS
44
import org.utbot.framework.codegen.domain.builtin.mockitoClassId
55
import org.utbot.framework.codegen.domain.builtin.ongoingStubbingClassId
6+
import org.utbot.framework.codegen.domain.context.CgContext
67
import org.utbot.framework.codegen.domain.models.CgClassId
78
import org.utbot.framework.codegen.tree.argumentsClassId
89
import org.utbot.framework.plugin.api.BuiltinClassId
@@ -11,6 +12,8 @@ import org.utbot.framework.plugin.api.CodeGenerationSettingBox
1112
import org.utbot.framework.plugin.api.CodeGenerationSettingItem
1213
import org.utbot.framework.plugin.api.MethodId
1314
import org.utbot.framework.plugin.api.TypeParameters
15+
import org.utbot.framework.plugin.api.UtModel
16+
import org.utbot.framework.plugin.api.idOrNull
1417
import org.utbot.framework.plugin.api.isolateCommandLineArgumentsToArgumentFile
1518
import org.utbot.framework.plugin.api.util.booleanArrayClassId
1619
import org.utbot.framework.plugin.api.util.booleanClassId
@@ -773,3 +776,17 @@ object SpringBoot : DependencyInjectionFramework(
773776
id = "spring-boot",
774777
displayName = "Spring Boot"
775778
)
779+
780+
/**
781+
* Extended id of [UtModel], unique for whole test set.
782+
*
783+
* Allows to distinguish models from different executions,
784+
* even if they have the same value of `UtModel.id`.
785+
*/
786+
data class ModelId(
787+
private val id: Int?,
788+
private val executionId: Int,
789+
)
790+
791+
fun UtModel.withExecutionId(executionId: Int = -1) = ModelId(this.idOrNull(), executionId)
792+

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/context/CgContext.kt

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ import kotlinx.collections.immutable.PersistentSet
2222
import kotlinx.collections.immutable.persistentListOf
2323
import kotlinx.collections.immutable.persistentMapOf
2424
import kotlinx.collections.immutable.persistentSetOf
25+
import org.utbot.framework.codegen.domain.ModelId
2526
import org.utbot.framework.codegen.domain.ProjectType
2627
import org.utbot.framework.codegen.domain.models.CgMethodTestSet
2728
import org.utbot.framework.codegen.domain.builtin.TestClassUtilMethodProvider
2829
import org.utbot.framework.codegen.domain.builtin.UtilClassFileMethodProvider
2930
import org.utbot.framework.codegen.domain.builtin.UtilMethodProvider
3031
import org.utbot.framework.codegen.domain.models.SimpleTestClassModel
3132
import org.utbot.framework.codegen.domain.models.CgParameterKind
33+
import org.utbot.framework.codegen.domain.withExecutionId
3234
import org.utbot.framework.codegen.services.access.Block
3335
import org.utbot.framework.codegen.tree.EnvironmentFieldStateCache
3436
import org.utbot.framework.codegen.tree.importIfNeeded
@@ -197,7 +199,7 @@ interface CgContextOwner {
197199
var valueByModel: IdentityHashMap<UtModel, CgValue>
198200

199201
// use it to compare stateBefore and result variables - in case of equality do not create new variable
200-
var valueByModelId: MutableMap<Int?, CgValue>
202+
var valueByModelId: MutableMap<ModelId, CgValue>
201203

202204
// parameters of the method currently being generated
203205
val currentMethodParameters: MutableMap<CgParameterKind, CgVariable>
@@ -226,6 +228,12 @@ interface CgContextOwner {
226228
*/
227229
var successfulExecutionsModels: List<UtModel>
228230

231+
/**
232+
* Gives a unique identifier to model in test set.
233+
* Determines which execution current model belongs to.
234+
*/
235+
var modelIds: MutableMap<UtModel, ModelId>
236+
229237
fun block(init: () -> Unit): Block {
230238
val prevBlock = currentBlock
231239
return try {
@@ -311,7 +319,10 @@ interface CgContextOwner {
311319
model?.let {
312320
valueByModel[it] = variable
313321
(model as UtReferenceModel).let { refModel ->
314-
refModel.id.let { id -> valueByModelId[id] = variable }
322+
refModel.id.let {
323+
val modelId = getIdByModel(model)
324+
valueByModelId[modelId] = variable
325+
}
315326
}
316327
}
317328
}
@@ -428,6 +439,8 @@ interface CgContextOwner {
428439

429440
val getLambdaMethod: MethodId
430441
get() = utilMethodProvider.getLambdaMethodMethodId
442+
443+
fun getIdByModel(model: UtModel): ModelId
431444
}
432445

433446
/**
@@ -479,6 +492,8 @@ data class CgContext(
479492
override lateinit var actual: CgVariable
480493
override lateinit var successfulExecutionsModels: List<UtModel>
481494

495+
override var modelIds: MutableMap<UtModel, ModelId> = mutableMapOf()
496+
482497
/**
483498
* This property cannot be accessed outside of test class file scope
484499
* (i.e. outside of [CgContextOwner.withTestClassFileScope]).
@@ -556,6 +571,8 @@ data class CgContext(
556571
}
557572
}
558573

574+
override fun getIdByModel(model: UtModel): ModelId = modelIds.getOrPut(model) { model.withExecutionId() }
575+
559576
private fun createClassIdForNestedClass(testClassModel: SimpleTestClassModel): ClassId {
560577
val simpleName = "${testClassModel.classUnderTest.simpleName}Test"
561578
return BuiltinClassId(
@@ -574,13 +591,14 @@ data class CgContext(
574591
requiredUtilMethods.clear()
575592
valueByModel.clear()
576593
valueByModelId.clear()
594+
modelIds.clear()
577595
mockFrameworkUsed = false
578596
}
579597

580598

581599
override var valueByModel: IdentityHashMap<UtModel, CgValue> = IdentityHashMap()
582600

583-
override var valueByModelId: MutableMap<Int?, CgValue> = mutableMapOf()
601+
override var valueByModelId: MutableMap<ModelId, CgValue> = mutableMapOf()
584602

585603
override val currentMethodParameters: MutableMap<CgParameterKind, CgVariable> = mutableMapOf()
586604

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import org.utbot.framework.codegen.renderer.CgRendererContext
77
import org.utbot.framework.codegen.renderer.CgVisitor
88
import org.utbot.framework.codegen.renderer.auxiliaryClassTextById
99
import org.utbot.framework.codegen.renderer.utilMethodTextById
10+
import org.utbot.framework.codegen.tree.VisibilityModifier
1011
import org.utbot.framework.plugin.api.BuiltinClassId
1112
import org.utbot.framework.plugin.api.ClassId
1213
import org.utbot.framework.plugin.api.ConstructorId
@@ -82,6 +83,7 @@ interface CgElement {
8283
is CgBreakStatement -> visit(element)
8384
is CgContinueStatement -> visit(element)
8485
is CgDeclaration -> visit(element)
86+
is CgFieldDeclaration -> visit(element)
8587
is CgAssignment -> visit(element)
8688
is CgTypeCast -> visit(element)
8789
is CgIsInstance -> visit(element)
@@ -134,6 +136,7 @@ class CgClass(
134136
val body: CgClassBody,
135137
val isStatic: Boolean,
136138
val isNested: Boolean,
139+
val visibility: VisibilityModifier = VisibilityModifier.PUBLIC,
137140
): CgElement {
138141
val packageName
139142
get() = id.packageName
@@ -156,8 +159,21 @@ class CgClassBody(
156159
val methodRegions: List<CgMethodsCluster>,
157160
val staticDeclarationRegions: List<CgStaticsRegion>,
158161
val nestedClassRegions: List<CgNestedClassesRegion<*>>,
159-
//TODO: use [CgFieldDeclaration] after PR-1788 merge
160-
val fields: List<CgDeclaration> = emptyList(),
162+
val fields: Set<CgFieldDeclaration> = emptySet(),
163+
) : CgElement
164+
165+
/**
166+
* Field of a class.
167+
* @property ownerClassId [ClassId] of the field owner class.
168+
* @property declaration declaration itself.
169+
* @property annotation optional annotation.
170+
* @property visibility field visibility.
171+
*/
172+
class CgFieldDeclaration(
173+
val ownerClassId: ClassId,
174+
val declaration: CgDeclaration,
175+
val annotation: CgAnnotation? = null,
176+
val visibility: VisibilityModifier = VisibilityModifier.PUBLIC,
161177
) : CgElement
162178

163179
/**
@@ -272,6 +288,7 @@ sealed class CgMethod(open val isStatic: Boolean) : CgElement {
272288
abstract val annotations: List<CgAnnotation>
273289
abstract val documentation: CgDocumentationComment
274290
abstract val requiredFields: List<CgParameterDeclaration>
291+
abstract val visibility: VisibilityModifier
275292
}
276293

277294
class CgTestMethod(
@@ -281,6 +298,7 @@ class CgTestMethod(
281298
override val statements: List<CgStatement>,
282299
override val exceptions: Set<ClassId>,
283300
override val annotations: List<CgAnnotation>,
301+
override val visibility: VisibilityModifier = VisibilityModifier.PUBLIC,
284302
val type: CgTestMethodType,
285303
override val documentation: CgDocumentationComment = CgDocumentationComment(emptyList()),
286304
override val requiredFields: List<CgParameterDeclaration> = emptyList(),
@@ -291,6 +309,7 @@ class CgFrameworkUtilMethod(
291309
override val statements: List<CgStatement>,
292310
override val exceptions: Set<ClassId>,
293311
override val annotations: List<CgAnnotation>,
312+
override val visibility: VisibilityModifier = VisibilityModifier.PUBLIC,
294313
) : CgMethod(isStatic = false) {
295314
override val returnType: ClassId = voidClassId
296315
override val parameters: List<CgParameterDeclaration> = emptyList()
@@ -301,7 +320,8 @@ class CgFrameworkUtilMethod(
301320
class CgErrorTestMethod(
302321
override val name: String,
303322
override val statements: List<CgStatement>,
304-
override val documentation: CgDocumentationComment = CgDocumentationComment(emptyList())
323+
override val documentation: CgDocumentationComment = CgDocumentationComment(emptyList()),
324+
override val visibility: VisibilityModifier = VisibilityModifier.PUBLIC,
305325
) : CgMethod(isStatic = false) {
306326
override val exceptions: Set<ClassId> = emptySet()
307327
override val returnType: ClassId = voidClassId
@@ -316,6 +336,7 @@ class CgParameterizedTestDataProviderMethod(
316336
override val returnType: ClassId,
317337
override val annotations: List<CgAnnotation>,
318338
override val exceptions: Set<ClassId>,
339+
override val visibility: VisibilityModifier = VisibilityModifier.PUBLIC,
319340
) : CgMethod(isStatic = true) {
320341
override val parameters: List<CgParameterDeclaration> = emptyList()
321342
override val documentation: CgDocumentationComment = CgDocumentationComment(emptyList())

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
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
5+
6+
typealias ClassModels = Map<ClassId, Set<UtModel>>
47

58
/**
69
* Stores method test sets in a structure that replicates structure of their methods in [classUnderTest].
@@ -13,7 +16,7 @@ abstract class TestClassModel(
1316
val nestedClasses: List<SimpleTestClassModel>,
1417
)
1518

16-
open class SimpleTestClassModel(
19+
class SimpleTestClassModel(
1720
classUnderTest: ClassId,
1821
methodTestSets: List<CgMethodTestSet>,
1922
nestedClasses: List<SimpleTestClassModel> = listOf(),
@@ -29,7 +32,7 @@ class SpringTestClassModel(
2932
classUnderTest: ClassId,
3033
methodTestSets: List<CgMethodTestSet>,
3134
nestedClasses: List<SimpleTestClassModel>,
32-
val injectingMocksClass: ClassId? = null,
33-
val mockedClasses: Set<ClassId> = setOf(),
35+
val injectedMockModels: ClassModels = mapOf(),
36+
val mockedModels: ClassModels = mapOf(),
3437
): TestClassModel(classUnderTest, methodTestSets, nestedClasses)
3538

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package org.utbot.framework.codegen.domain.models.builders
22

3+
import org.utbot.framework.codegen.domain.context.CgContext
34
import org.utbot.framework.codegen.domain.models.CgMethodTestSet
45
import org.utbot.framework.codegen.domain.models.SimpleTestClassModel
56
import org.utbot.framework.plugin.api.ClassId
67
import org.utbot.framework.plugin.api.util.enclosingClass
78

8-
open class SimpleTestClassModelBuilder: TestClassModelBuilder() {
9-
override fun createTestClassModel(classUnderTest: ClassId, testSets: List<CgMethodTestSet>): SimpleTestClassModel {
9+
open class SimpleTestClassModelBuilder(context: CgContext): TestClassModelBuilder() {
10+
override fun createTestClassModel(
11+
classUnderTest: ClassId,
12+
testSets: List<CgMethodTestSet>,
13+
): SimpleTestClassModel {
1014
// For each class stores list of methods declared in this class (methods from nested classes are excluded)
1115
val class2methodTestSets = testSets.groupBy { it.executableId.classId }
1216

0 commit comments

Comments
 (0)