Skip to content

Commit 9740202

Browse files
committed
Properly handle UtCustomModel
1 parent 76ad646 commit 9740202

File tree

10 files changed

+109
-71
lines changed

10 files changed

+109
-71
lines changed

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,10 @@ data class UtArrayModel(
574574
override fun hashCode(): Int = id
575575
}
576576

577+
interface UtModelWithOrigin {
578+
val origin: UtCompositeModel?
579+
}
580+
577581
/**
578582
* Model for complex objects with assemble instructions.
579583
*
@@ -588,8 +592,8 @@ data class UtAssembleModel private constructor(
588592
override val modelName: String,
589593
val instantiationCall: UtStatementCallModel,
590594
val modificationsChain: List<UtStatementModel>,
591-
val origin: UtCompositeModel?
592-
) : UtReferenceModel(id, classId, modelName) {
595+
override val origin: UtCompositeModel?
596+
) : UtReferenceModel(id, classId, modelName), UtModelWithOrigin {
593597

594598
/**
595599
* Creates a new [UtAssembleModel].
@@ -731,11 +735,11 @@ class UtLambdaModel(
731735
* Common parent of all framework-specific models (e.g. Spring-specific models)
732736
*/
733737
abstract class UtCustomModel(
734-
val origin: UtCompositeModel? = null,
738+
override val origin: UtCompositeModel? = null,
735739
id: Int?,
736740
classId: ClassId,
737741
modelName: String = id.toString()
738-
) : UtReferenceModel(id, classId, modelName)
742+
) : UtReferenceModel(id, classId, modelName), UtModelWithOrigin
739743

740744
object UtSpringContextModel : UtCustomModel(
741745
id = null,

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/constructor/ValueConstructor.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import org.utbot.framework.plugin.api.UtAssembleModel
2020
import org.utbot.framework.plugin.api.UtClassRefModel
2121
import org.utbot.framework.plugin.api.UtCompositeModel
2222
import org.utbot.framework.plugin.api.UtConcreteValue
23+
import org.utbot.framework.plugin.api.UtCustomModel
2324
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
2425
import org.utbot.framework.plugin.api.UtDirectGetFieldModel
2526
import org.utbot.framework.plugin.api.UtEnumConstantModel
@@ -193,6 +194,10 @@ class ValueConstructor {
193194
is UtAssembleModel -> UtConcreteValue(constructFromAssembleModel(model))
194195
is UtLambdaModel -> UtConcreteValue(constructFromLambdaModel(model))
195196
is UtVoidModel -> UtConcreteValue(Unit)
197+
is UtCustomModel -> UtConcreteValue(
198+
constructObject(model.origin ?: error("Can't construct value for custom model without origin [$model]")),
199+
model.classId.jClass
200+
)
196201
// Python, JavaScript are supposed to be here as well
197202
else -> throw UnsupportedOperationException("UtModel $model cannot construct UtConcreteValue")
198203
}

utbot-framework-test/src/test/kotlin/org/utbot/examples/models/CompositeModelMinimizationChecker.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.utbot.framework.plugin.api.UtCompositeModel
66
import org.utbot.framework.plugin.api.UtModel
77
import org.utbot.framework.plugin.api.UtReferenceModel
88
import org.junit.Test
9+
import org.utbot.framework.plugin.api.UtCustomModel
910
import org.utbot.testcheckers.eq
1011
import org.utbot.testing.UtModelTestCaseChecker
1112

@@ -17,6 +18,7 @@ internal class CompositeModelMinimizationChecker : UtModelTestCaseChecker(
1718
private fun UtModel.getFieldsOrNull(): Map<FieldId, UtModel>? = when(this) {
1819
is UtAssembleModel -> origin?.fields
1920
is UtCompositeModel -> fields
21+
is UtCustomModel -> origin?.fields
2022
else -> null
2123
}
2224

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.utbot.framework.plugin.api.UtArrayModel
2121
import org.utbot.framework.plugin.api.UtAssembleModel
2222
import org.utbot.framework.plugin.api.UtClassRefModel
2323
import org.utbot.framework.plugin.api.UtCompositeModel
24+
import org.utbot.framework.plugin.api.UtCustomModel
2425
import org.utbot.framework.plugin.api.UtDirectGetFieldModel
2526
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
2627
import org.utbot.framework.plugin.api.UtEnumConstantModel
@@ -199,7 +200,8 @@ class AssembleModelGenerator(private val basePackageName: String) {
199200
is UtPrimitiveModel,
200201
is UtClassRefModel,
201202
is UtVoidModel,
202-
is UtEnumConstantModel -> utModel
203+
is UtEnumConstantModel,
204+
is UtCustomModel -> utModel
203205
is UtLambdaModel -> assembleLambdaModel(utModel)
204206
is UtArrayModel -> assembleArrayModel(utModel)
205207
is UtCompositeModel -> assembleCompositeModel(utModel)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.utbot.framework.plugin.api.ClassId
1111
import org.utbot.framework.plugin.api.UtAssembleModel
1212
import org.utbot.framework.plugin.api.UtCompositeModel
1313
import org.utbot.framework.plugin.api.UtModel
14+
import org.utbot.framework.plugin.api.UtModelWithOrigin
1415
import org.utbot.framework.plugin.api.isMockModel
1516
import org.utbot.framework.plugin.api.util.SpringModelUtils.autowiredClassId
1617
import org.utbot.framework.plugin.api.util.SpringModelUtils.isAutowiredFromContext
@@ -47,7 +48,7 @@ class CgInjectingMocksFieldsManager(val context: CgContext) : CgClassFieldManage
4748
override fun constructVariableForField(model: UtModel, modelVariable: CgValue): CgValue {
4849
val modelFields = when (model) {
4950
is UtCompositeModel -> model.fields
50-
is UtAssembleModel -> model.origin?.fields
51+
is UtModelWithOrigin -> model.origin?.fields
5152
else -> null
5253
}
5354

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

Lines changed: 75 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ import org.utbot.framework.plugin.api.UtAssembleModel
8989
import org.utbot.framework.plugin.api.UtClassRefModel
9090
import org.utbot.framework.plugin.api.UtCompositeModel
9191
import org.utbot.framework.plugin.api.UtConcreteExecutionFailure
92+
import org.utbot.framework.plugin.api.UtCustomModel
9293
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
9394
import org.utbot.framework.plugin.api.UtEnumConstantModel
9495
import org.utbot.framework.plugin.api.UtExecution
@@ -98,6 +99,7 @@ import org.utbot.framework.plugin.api.UtExecutionSuccess
9899
import org.utbot.framework.plugin.api.UtExplicitlyThrownException
99100
import org.utbot.framework.plugin.api.UtLambdaModel
100101
import org.utbot.framework.plugin.api.UtModel
102+
import org.utbot.framework.plugin.api.UtModelWithOrigin
101103
import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation
102104
import org.utbot.framework.plugin.api.UtNullModel
103105
import org.utbot.framework.plugin.api.UtOverflowFailure
@@ -321,6 +323,8 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
321323
+thisInstance[executable](*methodArguments.toTypedArray())
322324
} else {
323325
this.resultModel = resultModel
326+
327+
// TODO support custom way of rendering asserts when `resultModel` is `UtCustomModel`
324328
val expected = variableConstructor.getOrCreateVariable(resultModel, "expected")
325329
assertEquality(expected, actual)
326330
}
@@ -754,43 +758,21 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
754758
)
755759
}
756760
}
757-
is UtCompositeModel -> {
758-
// Basically, to compare two iterables or maps, we need to iterate over them and compare each entry.
759-
// But it leads to a lot of trash code in each test method, and it is more clear to use
760-
// outer deep equals here
761-
if (expected.isIterableOrMap()) {
762-
currentBlock += CgSingleLineComment(
763-
"${expected.type.canonicalName} is iterable or Map, use outer deep equals to iterate over"
764-
)
765-
currentBlock += getDeepEqualsAssertion(expected, actual).toStatement()
766-
767-
return
768-
}
769-
770-
// We can use overridden equals if we have one, but not for mocks.
771-
if (expected.hasNotParametrizedCustomEquals() && !expectedModel.isMock) {
772-
// We rely on already existing equals
773-
currentBlock += CgSingleLineComment("${expected.type.canonicalName} has overridden equals method")
774-
currentBlock += assertions[assertEquals](expected, actual).toStatement()
775-
776-
return
777-
}
778-
779-
for ((fieldId, fieldModel) in expectedModel.fields) {
780-
// we should not process enclosing class
781-
// (actually, we do not do it properly anyway)
782-
if (fieldId.isInnerClassEnclosingClassReference) continue
783-
784-
traverseFieldRecursively(
785-
fieldId,
786-
fieldModel,
787-
expected,
788-
actual,
789-
depth,
790-
visitedModels
791-
)
792-
}
793-
}
761+
is UtCompositeModel -> assertDeepEqualsForComposite(
762+
expected = expected,
763+
actual = actual,
764+
expectedModel = expectedModel,
765+
depth = depth,
766+
visitedModels = visitedModels
767+
)
768+
is UtCustomModel -> assertDeepEqualsForComposite(
769+
expected = expected,
770+
actual = actual,
771+
expectedModel = expectedModel.origin
772+
?: error("Can't generate equals assertion for custom expected model without origin [$expectedModel]"),
773+
depth = depth,
774+
visitedModels = visitedModels
775+
)
794776
is UtLambdaModel -> Unit // we do not check equality of lambdas
795777
is UtVoidModel -> {
796778
// Unit result is considered in generateResultAssertions method
@@ -801,6 +783,50 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
801783
}
802784
}
803785

786+
private fun TestFrameworkManager.assertDeepEqualsForComposite(
787+
expected: CgVariable,
788+
actual: CgVariable,
789+
expectedModel: UtCompositeModel,
790+
depth: Int,
791+
visitedModels: MutableSet<ModelWithField>
792+
) {
793+
// Basically, to compare two iterables or maps, we need to iterate over them and compare each entry.
794+
// But it leads to a lot of trash code in each test method, and it is more clear to use
795+
// outer deep equals here
796+
if (expected.isIterableOrMap()) {
797+
currentBlock += CgSingleLineComment(
798+
"${expected.type.canonicalName} is iterable or Map, use outer deep equals to iterate over"
799+
)
800+
currentBlock += getDeepEqualsAssertion(expected, actual).toStatement()
801+
802+
return
803+
}
804+
805+
// We can use overridden equals if we have one, but not for mocks.
806+
if (expected.hasNotParametrizedCustomEquals() && !expectedModel.isMock) {
807+
// We rely on already existing equals
808+
currentBlock += CgSingleLineComment("${expected.type.canonicalName} has overridden equals method")
809+
currentBlock += assertions[assertEquals](expected, actual).toStatement()
810+
811+
return
812+
}
813+
814+
for ((fieldId, fieldModel) in expectedModel.fields) {
815+
// we should not process enclosing class
816+
// (actually, we do not do it properly anyway)
817+
if (fieldId.isInnerClassEnclosingClassReference) continue
818+
819+
traverseFieldRecursively(
820+
fieldId,
821+
fieldModel,
822+
expected,
823+
actual,
824+
depth,
825+
visitedModels
826+
)
827+
}
828+
}
829+
804830
private fun TestFrameworkManager.addArraysLengthAssertion(
805831
expected: CgVariable,
806832
actual: CgVariable,
@@ -1040,18 +1066,10 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
10401066
private fun collectExecutionsResultFields() {
10411067
for (model in successfulExecutionsModels) {
10421068
when (model) {
1043-
is UtCompositeModel -> {
1044-
for ((fieldId, fieldModel) in model.fields) {
1045-
collectExecutionsResultFieldsRecursively(fieldId, fieldModel, 0)
1046-
}
1047-
}
1069+
is UtCompositeModel -> collectExecutionsResultFieldsRecursively(model, 0)
10481070

1049-
is UtAssembleModel -> {
1050-
model.origin?.let {
1051-
for ((fieldId, fieldModel) in it.fields) {
1052-
collectExecutionsResultFieldsRecursively(fieldId, fieldModel, 0)
1053-
}
1054-
}
1071+
is UtModelWithOrigin -> model.origin?.let {
1072+
collectExecutionsResultFieldsRecursively(it, 0)
10551073
}
10561074

10571075
// Lambdas do not have fields. They have captured values, but we do not consider them here.
@@ -1069,6 +1087,12 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
10691087
}
10701088
}
10711089

1090+
private fun collectExecutionsResultFieldsRecursively(model: UtCompositeModel, depth: Int) {
1091+
for ((fieldId, fieldModel) in model.fields) {
1092+
collectExecutionsResultFieldsRecursively(fieldId, fieldModel, depth)
1093+
}
1094+
}
1095+
10721096
private fun collectExecutionsResultFieldsRecursively(
10731097
fieldId: FieldId,
10741098
fieldModel: UtModel,
@@ -1082,18 +1106,10 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
10821106
fieldsOfExecutionResults.getOrPut(fieldKey) { mutableListOf() } += fieldModel
10831107

10841108
when (fieldModel) {
1085-
is UtCompositeModel -> {
1086-
for ((id, model) in fieldModel.fields) {
1087-
collectExecutionsResultFieldsRecursively(id, model, depth + 1)
1088-
}
1089-
}
1109+
is UtCompositeModel -> collectExecutionsResultFieldsRecursively(fieldModel, depth + 1)
10901110

1091-
is UtAssembleModel -> {
1092-
fieldModel.origin?.let {
1093-
for ((id, model) in it.fields) {
1094-
collectExecutionsResultFieldsRecursively(id, model, depth + 1)
1095-
}
1096-
}
1111+
is UtModelWithOrigin -> fieldModel.origin?.let {
1112+
collectExecutionsResultFieldsRecursively(it, depth + 1)
10971113
}
10981114

10991115
// Lambdas do not have fields. They have captured values, but we do not consider them here.

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import org.utbot.framework.plugin.api.UtArrayModel
3030
import org.utbot.framework.plugin.api.UtAssembleModel
3131
import org.utbot.framework.plugin.api.UtClassRefModel
3232
import org.utbot.framework.plugin.api.UtCompositeModel
33+
import org.utbot.framework.plugin.api.UtCustomModel
3334
import org.utbot.framework.plugin.api.UtDirectGetFieldModel
3435
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
3536
import org.utbot.framework.plugin.api.UtEnumConstantModel
@@ -107,6 +108,7 @@ open class CgVariableConstructor(val context: CgContext) :
107108
is UtLambdaModel -> constructLambda(model, baseName)
108109
is UtNullModel -> nullLiteral()
109110
is UtPrimitiveModel -> CgLiteral(model.classId, model.value)
111+
is UtCustomModel -> constructValueByModel(model.origin ?: error("Can't construct value for custom model without origin [$model]"), name)
110112
is UtReferenceModel -> error("Unexpected UtReferenceModel: ${model::class}")
111113
is UtVoidModel -> error("Unexpected UtVoidModel: ${model::class}")
112114
else -> error("Unexpected UtModel: ${model::class}")

utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import org.utbot.framework.plugin.api.UtEnumConstantModel
1515
import org.utbot.framework.plugin.api.UtExecution
1616
import org.utbot.framework.plugin.api.UtLambdaModel
1717
import org.utbot.framework.plugin.api.UtModel
18+
import org.utbot.framework.plugin.api.UtModelWithOrigin
1819
import org.utbot.framework.plugin.api.UtNullModel
1920
import org.utbot.framework.plugin.api.UtPrimitiveModel
2021
import org.utbot.framework.plugin.api.UtReferenceModel
@@ -105,16 +106,16 @@ class ExecutionStateAnalyzer(val execution: UtExecution) {
105106
var modelBefore = before
106107

107108
if (before::class != after::class) {
108-
if (before is UtAssembleModel && after is UtCompositeModel && before.origin != null) {
109+
if (before is UtModelWithOrigin && after is UtModelWithOrigin && before.origin != null) {
109110
modelBefore = before.origin ?: unreachableBranch("We have already checked the origin for a null value")
110111
} else {
111112
doNotRun {
112113
// it is ok because we might have modelBefore with some absent fields (i.e. statics), but
113114
// modelAfter (constructed by concrete executor) will consist all these fields,
114115
// therefore, AssembleModelGenerator won't be able to transform the given composite model
115116

116-
val reason = if (before is UtAssembleModel && after is UtCompositeModel) {
117-
"ModelBefore is an AssembleModel and ModelAfter " +
117+
val reason = if (before is UtModelWithOrigin && after is UtCompositeModel) {
118+
"ModelBefore is an UtModelWithOrigin and ModelAfter " +
118119
"is a CompositeModel, but modelBefore doesn't have an origin model."
119120
} else {
120121
"The model before and the model after have different types: " +

utbot-framework/src/main/kotlin/org/utbot/framework/util/SizeUtils.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import org.utbot.framework.plugin.api.UtArrayModel
55
import org.utbot.framework.plugin.api.UtAssembleModel
66
import org.utbot.framework.plugin.api.UtClassRefModel
77
import org.utbot.framework.plugin.api.UtCompositeModel
8+
import org.utbot.framework.plugin.api.UtCustomModel
89
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
910
import org.utbot.framework.plugin.api.UtEnumConstantModel
1011
import org.utbot.framework.plugin.api.UtLambdaModel
@@ -37,7 +38,7 @@ private fun UtModel.calculateSize(used: MutableSet<UtModel> = mutableSetOf()): I
3738

3839
return when (this) {
3940
is UtNullModel, is UtPrimitiveModel, UtVoidModel -> 0
40-
is UtClassRefModel, is UtEnumConstantModel, is UtArrayModel -> 1
41+
is UtClassRefModel, is UtEnumConstantModel, is UtArrayModel, is UtCustomModel -> 1
4142
is UtAssembleModel -> {
4243
1 + instantiationCall.calculateSize(used) + modificationsChain.sumOf { it.calculateSize(used) }
4344
}

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/InstrumentationContextAwareValueConstructor.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import org.utbot.framework.plugin.api.UtAssembleModel
1616
import org.utbot.framework.plugin.api.UtClassRefModel
1717
import org.utbot.framework.plugin.api.UtCompositeModel
1818
import org.utbot.framework.plugin.api.UtConcreteValue
19+
import org.utbot.framework.plugin.api.UtCustomModel
1920
import org.utbot.framework.plugin.api.UtDirectGetFieldModel
2021
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
2122
import org.utbot.framework.plugin.api.UtEnumConstantModel
@@ -110,8 +111,11 @@ class InstrumentationContextAwareValueConstructor(
110111
is UtVoidModel -> UtConcreteValue(Unit)
111112
else -> {
112113
instrumentationContext.constructContextDependentValue(model) ?:
113-
// PythonModel, JsUtModel may be here
114-
throw UnsupportedOperationException("UtModel $model cannot construct UtConcreteValue")
114+
if (model is UtCustomModel)
115+
construct(model.origin ?: error("Can't construct value for custom model without origin [$model]"))
116+
else
117+
// PythonModel, JsUtModel may be here
118+
throw UnsupportedOperationException("UtModel $model cannot construct UtConcreteValue")
115119
}
116120
}
117121

0 commit comments

Comments
 (0)