Skip to content

Commit 8846ff9

Browse files
committed
Introduce CgModel to support shared global variables
1 parent 0cceaa4 commit 8846ff9

File tree

11 files changed

+175
-149
lines changed

11 files changed

+175
-149
lines changed

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -730,18 +730,18 @@ object SpringBoot : DependencyInjectionFramework(
730730
)
731731

732732
/**
733-
* Extended id of [UtModel], unique for whole test set.
733+
* Extended [UtModel] model with testSet id and execution id.
734734
*
735-
* Allows distinguishing models from different executions and test sets,
736-
* even if they have the same value of `UtModel.id` that is allowed.
735+
* Used as a key in [valueByCgModel].
736+
* Was introduced primarily for shared among all test methods global variables.
737737
*/
738-
data class ModelId private constructor(
739-
private val id: Int?,
740-
private val executionId: Int,
741-
private val testSetId: Int,
738+
data class CgModel private constructor(
739+
val testSetId: Int,
740+
val executionId: Int,
741+
val model: UtModel
742742
) {
743743
companion object {
744-
fun create(model: UtModel, executionId: Int = -1, testSetId: Int = -1) = ModelId(model.idOrNull(), executionId, testSetId)
744+
fun create(testSetId: Int, executionId: Int, model: UtModel): CgModel =
745+
CgModel(testSetId, executionId, model)
745746
}
746747
}
747-

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

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@ import org.utbot.framework.codegen.domain.models.CgTestMethod
1515
import org.utbot.framework.codegen.domain.models.CgThisInstance
1616
import org.utbot.framework.codegen.domain.models.CgValue
1717
import org.utbot.framework.codegen.domain.models.CgVariable
18-
import java.util.IdentityHashMap
1918
import kotlinx.collections.immutable.PersistentList
2019
import kotlinx.collections.immutable.PersistentMap
2120
import kotlinx.collections.immutable.PersistentSet
2221
import kotlinx.collections.immutable.persistentListOf
2322
import kotlinx.collections.immutable.persistentMapOf
2423
import kotlinx.collections.immutable.persistentSetOf
25-
import org.utbot.framework.codegen.domain.ModelId
24+
import org.utbot.framework.codegen.domain.CgModel
2625
import org.utbot.framework.codegen.domain.ProjectType
2726
import org.utbot.framework.codegen.domain.models.CgMethodTestSet
2827
import org.utbot.framework.codegen.domain.builtin.TestClassUtilMethodProvider
@@ -195,10 +194,16 @@ interface CgContextOwner {
195194

196195
val shouldOptimizeImports: Boolean
197196

198-
var valueByModel: IdentityHashMap<UtModel, CgValue>
197+
// used for differentiating models
198+
// see withTestSetIdScope()
199+
var currentTestSetId: Int
199200

200-
// use it to compare stateBefore and result variables - in case of equality do not create new variable
201-
var valueByModelId: MutableMap<ModelId, CgValue>
201+
// used for differentiating models
202+
// see withExecutionIdScope()
203+
var currentExecutionId: Int
204+
205+
// used for comparing stateBefore and result variables -- in case of equality do not create new variable
206+
var valueByCgModel: MutableMap<CgModel, CgValue>
202207

203208
// parameters of the method currently being generated
204209
val currentMethodParameters: MutableMap<CgParameterKind, CgVariable>
@@ -227,12 +232,6 @@ interface CgContextOwner {
227232
*/
228233
var successfulExecutionsModels: List<UtModel>
229234

230-
/**
231-
* Gives a unique identifier to model in test set.
232-
* Determines which execution current model belongs to.
233-
*/
234-
var modelIds: MutableMap<UtModel, ModelId>
235-
236235
fun block(init: () -> Unit): Block {
237236
val prevBlock = currentBlock
238237
return try {
@@ -260,6 +259,26 @@ interface CgContextOwner {
260259
currentExecutable = executableId
261260
}
262261

262+
fun <R> withTestSetIdScope(testSetId: Int, block: () -> R): R {
263+
val prevTestSetId = currentTestSetId
264+
return try {
265+
currentTestSetId = testSetId
266+
block()
267+
} finally {
268+
currentTestSetId = prevTestSetId
269+
}
270+
}
271+
272+
fun <R> withExecutionIdScope(executionId: Int, block: () -> R): R {
273+
val prevExecutionId = currentExecutionId
274+
return try {
275+
currentExecutionId = executionId
276+
block()
277+
} finally {
278+
currentExecutionId = prevExecutionId
279+
}
280+
}
281+
263282
fun addExceptionIfNeeded(exception: ClassId) {
264283
if (exception !is BuiltinClassId) {
265284
require(exception isSubtypeOf Throwable::class.id) {
@@ -316,11 +335,10 @@ interface CgContextOwner {
316335

317336
fun updateVariableScope(variable: CgVariable, model: UtModel? = null) {
318337
model?.let {
319-
valueByModel[it] = variable
338+
valueByCgModel[it.toCgModel()] = variable
320339
(model as UtReferenceModel).let { refModel ->
321340
refModel.id.let {
322-
val modelId = getIdByModel(model)
323-
valueByModelId[modelId] = variable
341+
valueByCgModel[model.toCgModel()] = variable
324342
}
325343
}
326344
}
@@ -331,17 +349,15 @@ interface CgContextOwner {
331349
val prevDeclaredClassRefs = declaredClassRefs
332350
val prevDeclaredExecutableRefs = declaredExecutableRefs
333351
val prevDeclaredFieldRefs = declaredFieldRefs
334-
val prevValueByModel = IdentityHashMap(valueByModel)
335-
val prevValueByModelId = valueByModelId.toMutableMap()
352+
val prevValueByCgModel = valueByCgModel.toMutableMap()
336353
return try {
337354
block()
338355
} finally {
339356
existingVariableNames = prevVariableNames
340357
declaredClassRefs = prevDeclaredClassRefs
341358
declaredExecutableRefs = prevDeclaredExecutableRefs
342359
declaredFieldRefs = prevDeclaredFieldRefs
343-
valueByModel = prevValueByModel
344-
valueByModelId = prevValueByModelId
360+
valueByCgModel = prevValueByCgModel
345361
}
346362
}
347363

@@ -439,7 +455,7 @@ interface CgContextOwner {
439455
val getLambdaMethod: MethodId
440456
get() = utilMethodProvider.getLambdaMethodMethodId
441457

442-
fun getIdByModel(model: UtModel): ModelId
458+
fun UtModel.toCgModel(): CgModel
443459
}
444460

445461
/**
@@ -491,8 +507,6 @@ class CgContext(
491507
override lateinit var actual: CgVariable
492508
override lateinit var successfulExecutionsModels: List<UtModel>
493509

494-
override var modelIds: MutableMap<UtModel, ModelId> = mutableMapOf()
495-
496510
/**
497511
* This property cannot be accessed outside of test class file scope
498512
* (i.e. outside of [CgContextOwner.withTestClassFileScope]).
@@ -570,7 +584,12 @@ class CgContext(
570584
}
571585
}
572586

573-
override fun getIdByModel(model: UtModel): ModelId = modelIds.getOrPut(model) { ModelId.create(model) }
587+
override fun UtModel.toCgModel(): CgModel =
588+
CgModel.create(
589+
testSetId = currentTestSetId,
590+
executionId = currentExecutionId,
591+
model = this
592+
)
574593

575594
private fun createClassIdForNestedClass(testClassModel: SimpleTestClassModel): ClassId {
576595
val simpleName = "${testClassModel.classUnderTest.simpleName}Test"
@@ -588,15 +607,15 @@ class CgContext(
588607
importedClasses.clear()
589608
testMethods.clear()
590609
requiredUtilMethods.clear()
591-
valueByModel.clear()
592-
valueByModelId.clear()
593-
modelIds.clear()
610+
valueByCgModel.clear()
594611
mockFrameworkUsed = false
595612
}
596613

597-
override var valueByModel: IdentityHashMap<UtModel, CgValue> = IdentityHashMap()
614+
override var currentTestSetId: Int = -1
615+
616+
override var currentExecutionId: Int = -1
598617

599-
override var valueByModelId: MutableMap<ModelId, CgValue> = mutableMapOf()
618+
override var valueByCgModel: MutableMap<CgModel, CgValue> = mutableMapOf()
600619

601620
override val currentMethodParameters: MutableMap<CgParameterKind, CgVariable> = mutableMapOf()
602621

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

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

3+
import org.utbot.framework.codegen.domain.CgModel
34
import org.utbot.framework.plugin.api.ClassId
45
import org.utbot.framework.plugin.api.UtModel
56

6-
typealias ClassModels = Map<ClassId, Set<UtModel>>
7-
87
/**
98
* Stores method test sets in a structure that replicates structure of their methods in [classUnderTest].
109
* I.e., if some method is declared in nested class of [classUnderTest], its testset will be put
@@ -32,7 +31,7 @@ class SpringTestClassModel(
3231
classUnderTest: ClassId,
3332
methodTestSets: List<CgMethodTestSet>,
3433
nestedClasses: List<SimpleTestClassModel>,
35-
val injectedMockModels: ClassModels = mapOf(),
36-
val mockedModels: ClassModels = mapOf(),
34+
val injectedMockModels: Map<ClassId, Set<CgModel>> = mapOf(),
35+
val mockedModels: Map<ClassId, Set<CgModel>> = mapOf(),
3736
): TestClassModel(classUnderTest, methodTestSets, nestedClasses)
3837

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

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package org.utbot.framework.codegen.domain.models.builders
22

3-
import org.utbot.framework.codegen.domain.ModelId
3+
import org.utbot.framework.codegen.domain.CgModel
44
import org.utbot.framework.codegen.domain.context.CgContext
55
import org.utbot.framework.codegen.domain.models.CgMethodTestSet
6-
import org.utbot.framework.codegen.domain.models.ClassModels
76
import org.utbot.framework.codegen.domain.models.SpringTestClassModel
87
import org.utbot.framework.plugin.api.ClassId
98
import org.utbot.framework.plugin.api.UtArrayModel
@@ -27,61 +26,64 @@ class SpringTestClassModelBuilder(val context: CgContext): TestClassModelBuilder
2726
val (injectedModels, mockedModels) = collectInjectedAndMockedModels(testSets)
2827

2928
return SpringTestClassModel(
30-
baseModel.classUnderTest,
31-
baseModel.methodTestSets,
32-
baseModel.nestedClasses,
33-
injectedModels,
34-
mockedModels,
29+
classUnderTest = baseModel.classUnderTest,
30+
methodTestSets = baseModel.methodTestSets,
31+
nestedClasses = baseModel.nestedClasses,
32+
injectedMockModels = injectedModels,
33+
mockedModels = mockedModels
3534
)
3635
}
3736

38-
private fun collectInjectedAndMockedModels(testSets: List<CgMethodTestSet>): Pair<ClassModels, ClassModels> {
39-
val thisInstances = mutableSetOf<UtModel>()
40-
val thisInstancesDependentModels = mutableSetOf<UtModel>()
41-
42-
for ((testSetIndex, testSet) in testSets.withIndex()) {
43-
for ((executionIndex, execution) in testSet.executions.withIndex()) {
44-
45-
setOf(execution.stateBefore.thisInstance, execution.stateAfter.thisInstance)
46-
.filterNotNull()
47-
.forEach { model ->
48-
thisInstances += model
49-
thisInstancesDependentModels += collectByThisInstanceModel(model, executionIndex, testSetIndex)
37+
private fun collectInjectedAndMockedModels(testSets: List<CgMethodTestSet>): Pair<Map<ClassId, Set<CgModel>>, Map<ClassId, Set<CgModel>>> {
38+
val thisInstances = mutableSetOf<CgModel>()
39+
val thisInstancesDependentModels = mutableSetOf<CgModel>()
40+
41+
with(context) {
42+
for ((testSetIndex, testSet) in testSets.withIndex()) {
43+
withTestSetIdScope(testSetIndex) {
44+
for ((executionIndex, execution) in testSet.executions.withIndex()) {
45+
withExecutionIdScope(executionIndex) {
46+
setOf(execution.stateBefore.thisInstance, execution.stateAfter.thisInstance)
47+
.filterNotNull()
48+
.forEach { model ->
49+
thisInstances += model.toCgModel()
50+
thisInstancesDependentModels += collectByThisInstanceModel(model)
51+
}
52+
}
5053
}
54+
}
5155
}
5256
}
5357

5458
val dependentMockModels =
55-
thisInstancesDependentModels.filterTo(mutableSetOf()) { it.isMockModel() && it !in thisInstances }
59+
thisInstancesDependentModels
60+
.filterTo(mutableSetOf()) { cgModel ->
61+
cgModel.model.isMockModel() && cgModel !in thisInstances
62+
}
5663

5764
return thisInstances.groupByClassId() to dependentMockModels.groupByClassId()
5865
}
5966

60-
private fun collectByThisInstanceModel(model: UtModel, executionIndex: Int, testSetIndex: Int): Set<UtModel> {
61-
context.modelIds[model] = ModelId.create(model, executionIndex, testSetIndex)
62-
63-
val dependentModels = mutableSetOf<UtModel>()
67+
private fun collectByThisInstanceModel(model: UtModel): Set<CgModel> {
68+
val dependentModels = mutableSetOf<CgModel>()
6469
collectRecursively(model, dependentModels)
6570

66-
dependentModels.forEach { model ->
67-
context.modelIds[model] = ModelId.create(model, executionIndex, testSetIndex)
68-
}
69-
7071
return dependentModels
7172
}
7273

73-
private fun Set<UtModel>.groupByClassId(): ClassModels {
74-
val classModels = mutableMapOf<ClassId, Set<UtModel>>()
74+
private fun Set<CgModel>.groupByClassId(): Map<ClassId, Set<CgModel>> {
75+
val classModels = mutableMapOf<ClassId, Set<CgModel>>()
7576

76-
for (modelGroup in this.groupBy { it.classId }) {
77+
for (modelGroup in this.groupBy { it.model.classId }) {
7778
classModels[modelGroup.key] = modelGroup.value.toSet()
7879
}
7980

8081
return classModels
8182
}
8283

83-
private fun collectRecursively(currentModel: UtModel, allModels: MutableSet<UtModel>) {
84-
if (!allModels.add(currentModel)) {
84+
private fun collectRecursively(currentModel: UtModel, allModels: MutableSet<CgModel>) {
85+
val cgModel = with(context) { currentModel.toCgModel() }
86+
if (!allModels.add(cgModel)) {
8587
return
8688
}
8789

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ abstract class CgAbstractTestClassConstructor<T : TestClassModel>(val context: C
9898
}
9999

100100
for (i in checkedRange) {
101-
currentTestCaseTestMethods += methodConstructor.createTestMethod(methodUnderTest, testSet.executions[i])
101+
withExecutionIdScope(i) {
102+
currentTestCaseTestMethods += methodConstructor.createTestMethod(methodUnderTest, testSet.executions[i])
103+
}
102104
}
103105

104106
val comments = listOf("Actual number of generated tests (${executionIndices.last - executionIndices.first}) exceeds per-method limit (${UtSettings.maxTestsPerMethodInRegion})",

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

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,16 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass
4949
}
5050
}
5151

52-
for (testSet in notYetConstructedTestSets) {
52+
for ((testSetIndex, testSet) in notYetConstructedTestSets.withIndex()) {
5353
updateCurrentExecutable(testSet.executableId)
54-
val currentMethodUnderTestRegions = constructTestSet(testSet) ?: continue
55-
val executableUnderTestCluster = CgMethodsCluster(
56-
"Test suites for executable $currentExecutable",
57-
currentMethodUnderTestRegions
58-
)
59-
methodRegions += executableUnderTestCluster
54+
withTestSetIdScope(testSetIndex) {
55+
val currentMethodUnderTestRegions = constructTestSet(testSet) ?: return@withTestSetIdScope
56+
val executableUnderTestCluster = CgMethodsCluster(
57+
"Test suites for executable $currentExecutable",
58+
currentMethodUnderTestRegions
59+
)
60+
methodRegions += executableUnderTestCluster
61+
}
6062
}
6163

6264
val currentTestClassDataProviderMethods = currentTestClassContext.cgDataProviderMethods
@@ -159,8 +161,11 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass
159161

160162
testSet.executions
161163
.filterIsInstance<UtFuzzedExecution>()
162-
.forEach { execution ->
163-
testMethods += methodConstructor.createTestMethod(testSet.executableId, execution)
164+
.withIndex()
165+
.forEach { (index, execution) ->
166+
withExecutionIdScope(index) {
167+
testMethods += methodConstructor.createTestMethod(testSet.executableId, execution)
168+
}
164169
}
165170

166171
return testMethods
@@ -176,8 +181,11 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass
176181
testSet.executions
177182
.filterIsInstance<UtSymbolicExecution>()
178183
.filter { it.containsMocking }
179-
.forEach { execution ->
180-
testMethods += methodConstructor.createTestMethod(testSet.executableId, execution)
184+
.withIndex()
185+
.forEach { (index, execution) ->
186+
withExecutionIdScope(index) {
187+
testMethods += methodConstructor.createTestMethod(testSet.executableId, execution)
188+
}
181189
}
182190

183191
return testMethods

0 commit comments

Comments
 (0)