Skip to content

Commit 98682a8

Browse files
Fix StackOverflow errors in models with mocks processing #2152 (#2157)
1 parent 43a9760 commit 98682a8

File tree

2 files changed

+43
-107
lines changed

2 files changed

+43
-107
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import org.utbot.framework.plugin.api.util.intClassId
7070
import org.utbot.framework.plugin.api.util.longClassId
7171
import org.utbot.framework.plugin.api.util.shortClassId
7272
import org.utbot.framework.plugin.api.util.voidClassId
73+
import java.util.*
7374

7475
abstract class CgVariableConstructorComponent(val context: CgContext) :
7576
CgContextOwner by context,
@@ -180,8 +181,10 @@ private abstract class StaticMocker(
180181
}
181182

182183
private class MockitoMocker(context: CgContext) : ObjectMocker(context) {
184+
185+
private val alreadyMockedModels: MutableSet<UtCompositeModel> = Collections.newSetFromMap(IdentityHashMap())
186+
183187
override fun createMock(model: UtCompositeModel, baseName: String): CgVariable {
184-
// create mock object
185188
val modelClass = getClassOf(model.classId)
186189
val mockObject = newVar(model.classId, baseName = baseName, isMock = true) { mock(modelClass) }
187190

@@ -191,6 +194,10 @@ private class MockitoMocker(context: CgContext) : ObjectMocker(context) {
191194
}
192195

193196
fun mockForVariable(model: UtCompositeModel, mockObject: CgVariable) {
197+
if (!alreadyMockedModels.add(model)) {
198+
return
199+
}
200+
194201
for ((executable, values) in model.mocks) {
195202
val matchers = mockitoArgumentMatchersFor(executable)
196203

@@ -223,8 +230,6 @@ private class MockitoMocker(context: CgContext) : ObjectMocker(context) {
223230
else -> error("Only MethodId was expected to appear in simple mocker but got $executable")
224231
}
225232
}
226-
227-
228233
}
229234
}
230235

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

Lines changed: 35 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,15 @@
11
package org.utbot.instrumentation.instrumentation.execution.constructors
22

3-
import java.lang.reflect.Modifier
4-
import java.util.IdentityHashMap
5-
import kotlin.reflect.KClass
63
import org.mockito.Mockito
74
import org.mockito.stubbing.Answer
85
import org.objectweb.asm.Type
96
import org.utbot.common.Reflection
107
import org.utbot.common.invokeCatching
11-
import org.utbot.framework.plugin.api.util.constructor.CapturedArgument
12-
import org.utbot.framework.plugin.api.util.constructor.constructLambda
13-
import org.utbot.framework.plugin.api.util.constructor.constructStaticLambda
14-
import org.utbot.instrumentation.instrumentation.execution.mock.InstanceMockController
15-
import org.utbot.instrumentation.instrumentation.execution.mock.InstrumentationContext
16-
import org.utbot.instrumentation.instrumentation.execution.mock.MethodMockController
17-
import org.utbot.instrumentation.instrumentation.execution.mock.MockController
188
import org.utbot.framework.plugin.api.ClassId
199
import org.utbot.framework.plugin.api.ConstructorId
2010
import org.utbot.framework.plugin.api.ExecutableId
2111
import org.utbot.framework.plugin.api.FieldId
22-
import org.utbot.framework.plugin.api.FieldMockTarget
2312
import org.utbot.framework.plugin.api.MethodId
24-
import org.utbot.framework.plugin.api.MockId
25-
import org.utbot.framework.plugin.api.MockInfo
26-
import org.utbot.framework.plugin.api.MockTarget
27-
import org.utbot.framework.plugin.api.ObjectMockTarget
28-
import org.utbot.framework.plugin.api.ParameterMockTarget
2913
import org.utbot.framework.plugin.api.UtArrayModel
3014
import org.utbot.framework.plugin.api.UtAssembleModel
3115
import org.utbot.framework.plugin.api.UtClassRefModel
@@ -35,24 +19,32 @@ import org.utbot.framework.plugin.api.UtDirectSetFieldModel
3519
import org.utbot.framework.plugin.api.UtEnumConstantModel
3620
import org.utbot.framework.plugin.api.UtExecutableCallModel
3721
import org.utbot.framework.plugin.api.UtLambdaModel
38-
import org.utbot.framework.plugin.api.UtMockValue
3922
import org.utbot.framework.plugin.api.UtModel
4023
import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation
4124
import org.utbot.framework.plugin.api.UtNullModel
4225
import org.utbot.framework.plugin.api.UtPrimitiveModel
4326
import org.utbot.framework.plugin.api.UtReferenceModel
4427
import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation
4528
import org.utbot.framework.plugin.api.UtVoidModel
46-
import org.utbot.framework.plugin.api.isMockModel
29+
import org.utbot.framework.plugin.api.util.anyInstance
4730
import org.utbot.framework.plugin.api.util.constructor
31+
import org.utbot.framework.plugin.api.util.constructor.CapturedArgument
32+
import org.utbot.framework.plugin.api.util.constructor.constructLambda
33+
import org.utbot.framework.plugin.api.util.constructor.constructStaticLambda
4834
import org.utbot.framework.plugin.api.util.executableId
4935
import org.utbot.framework.plugin.api.util.isStatic
5036
import org.utbot.framework.plugin.api.util.jClass
5137
import org.utbot.framework.plugin.api.util.jField
5238
import org.utbot.framework.plugin.api.util.method
5339
import org.utbot.framework.plugin.api.util.utContext
54-
import org.utbot.framework.plugin.api.util.anyInstance
40+
import org.utbot.instrumentation.instrumentation.execution.mock.InstanceMockController
41+
import org.utbot.instrumentation.instrumentation.execution.mock.InstrumentationContext
42+
import org.utbot.instrumentation.instrumentation.execution.mock.MethodMockController
43+
import org.utbot.instrumentation.instrumentation.execution.mock.MockController
5544
import org.utbot.instrumentation.process.runSandbox
45+
import java.lang.reflect.Modifier
46+
import java.util.*
47+
import kotlin.reflect.KClass
5648

5749
/**
5850
* Constructs values (including mocks) from models.
@@ -82,39 +74,17 @@ class MockValueConstructor(
8274

8375
// TODO: JIRA:1379 -- replace UtReferenceModel with Int
8476
private val constructedObjects = HashMap<UtReferenceModel, Any>()
85-
private val mockInfo = mutableListOf<MockInfo>()
86-
private var mockTarget: MockTarget? = null
87-
private var mockCounter = 0
8877

8978
/**
9079
* Controllers contain info about mocked methods and have to be closed to restore initial state.
9180
*/
9281
private val controllers = mutableListOf<MockController>()
9382

94-
/**
95-
* Sets mock context (possible mock target) before block execution and restores previous one after block execution.
96-
*/
97-
private inline fun <T> withMockTarget(target: MockTarget?, block: () -> T): T {
98-
val old = mockTarget
99-
try {
100-
mockTarget = target
101-
return block()
102-
} finally {
103-
mockTarget = old
104-
}
105-
}
106-
10783
fun constructMethodParameters(models: List<UtModel>): List<UtConcreteValue<*>> =
108-
models.mapIndexed { index, model ->
109-
val target = mockTarget(model) { ParameterMockTarget(model.classId.name, index) }
110-
construct(model, target)
111-
}
84+
models.mapIndexed { _, model -> construct(model) }
11285

11386
fun constructStatics(staticsBefore: Map<FieldId, UtModel>): Map<FieldId, UtConcreteValue<*>> =
114-
staticsBefore.mapValues { (field, model) -> // TODO: refactor this
115-
val target = FieldMockTarget(model.classId.name, field.declaringClass.name, owner = null, field.name)
116-
construct(model, target)
117-
}
87+
staticsBefore.mapValues { (_, model) -> construct(model) }
11888

11989
/**
12090
* Main construction method.
@@ -124,7 +94,7 @@ class MockValueConstructor(
12494
*
12595
* Takes mock creation context (possible mock target) to create mock if required.
12696
*/
127-
private fun construct(model: UtModel, target: MockTarget?): UtConcreteValue<*> = withMockTarget(target) {
97+
private fun construct(model: UtModel): UtConcreteValue<*> =
12898
when (model) {
12999
is UtNullModel -> UtConcreteValue(null, model.classId.jClass)
130100
is UtPrimitiveModel -> UtConcreteValue(model.value, model.classId.jClass)
@@ -138,7 +108,6 @@ class MockValueConstructor(
138108
// PythonModel, JsUtModel may be here
139109
else -> throw UnsupportedOperationException()
140110
}
141-
}
142111

143112
/**
144113
* Constructs an Enum<*> instance by model, uses reference-equality cache.
@@ -157,22 +126,6 @@ class MockValueConstructor(
157126
private fun constructObject(model: UtCompositeModel): Any {
158127
constructedObjects[model]?.let { return it }
159128

160-
this.mockTarget?.let { mockTarget ->
161-
model.mocks.forEach { (methodId, models) ->
162-
mockInfo += MockInfo(mockTarget, methodId, models.map { model ->
163-
if (model.isMockModel()) {
164-
val mockId = MockId("mock${++mockCounter}")
165-
// Call to "construct" method still required to collect mock interaction
166-
construct(model, ObjectMockTarget(model.classId.name, mockId))
167-
UtMockValue(mockId, model.classId.name)
168-
} else {
169-
construct(model, target = null)
170-
}
171-
})
172-
}
173-
}
174-
175-
176129
val javaClass = javaClass(model.classId)
177130

178131
val classInstance = if (!model.isMock) {
@@ -181,9 +134,18 @@ class MockValueConstructor(
181134
constructedObjects[model] = notMockInstance
182135
notMockInstance
183136
} else {
184-
val mockInstance = generateMockitoMock(javaClass, model.mocks)
137+
val concreteValues = model.mocks.mapValues { mutableListOf<Any?>() }
138+
val mockInstance = generateMockitoMock(javaClass, concreteValues)
185139

186140
constructedObjects[model] = mockInstance
141+
142+
concreteValues.forEach { (executableId, valuesList) ->
143+
val mockModels = model.mocks.getValue(executableId)
144+
// If model is unit, then null should be returned (this model has to be already constructed).
145+
val constructedValues = mockModels.map { model -> construct(model).value.takeIf { it != Unit } }
146+
valuesList.addAll(constructedValues)
147+
}
148+
187149
mockInstance
188150
}
189151

@@ -194,10 +156,7 @@ class MockValueConstructor(
194156

195157
check(Reflection.isModifiersAccessible())
196158

197-
val target = mockTarget(fieldModel) {
198-
FieldMockTarget(fieldModel.classId.name, model.classId.name, UtConcreteValue(classInstance), fieldId.name)
199-
}
200-
val value = construct(fieldModel, target).value
159+
val value = construct(fieldModel).value
201160
val instance = if (Modifier.isStatic(declaredField.modifiers)) null else classInstance
202161
declaredField.set(instance, value)
203162
declaredField.isAccessible = accessible
@@ -206,16 +165,8 @@ class MockValueConstructor(
206165
return classInstance
207166
}
208167

209-
private fun generateMockitoAnswer(methodToValues: Map<in ExecutableId, List<UtModel>>): Answer<*> {
210-
val pointers = methodToValues.mapValues { (_, _) -> 0 }.toMutableMap()
211-
val concreteValues = methodToValues.mapValues { (_, models) ->
212-
models.map { model ->
213-
val mockId = MockId("mock${++mockCounter}")
214-
val target = mockTarget(model) { ObjectMockTarget(model.classId.name, mockId) }
215-
construct(model, target).value.takeIf { it != Unit } // if it is unit, then null should be returned
216-
// This model has to be already constructed, so it is OK to pass null as a target
217-
}
218-
}
168+
private fun generateMockitoAnswer(concreteValues: Map<ExecutableId, List<Any?>>): Answer<*> {
169+
val pointers = concreteValues.mapValues { (_, _) -> 0 }.toMutableMap()
219170
return Answer { invocation ->
220171
with(invocation.method) {
221172
pointers[executableId].let { pointer ->
@@ -232,8 +183,9 @@ class MockValueConstructor(
232183
}
233184
}
234185

235-
private fun generateMockitoMock(clazz: Class<*>, mocks: Map<ExecutableId, List<UtModel>>): Any {
236-
return Mockito.mock(clazz, generateMockitoAnswer(mocks))
186+
private fun generateMockitoMock(clazz: Class<*>, concreteValues: Map<ExecutableId, List<Any?>>): Any {
187+
val answer = generateMockitoAnswer(concreteValues)
188+
return Mockito.mock(clazz, answer)
237189
}
238190

239191
private fun computeConcreteValuesForMethods(
@@ -341,7 +293,7 @@ class MockValueConstructor(
341293
constructedObjects[model] = instance
342294
for (i in instance.indices) {
343295
val elementModel = stores[i] ?: constModel
344-
val value = construct(elementModel, null).value
296+
val value = construct(elementModel).value
345297
try {
346298
java.lang.reflect.Array.set(instance, i, value)
347299
} catch (iae:IllegalArgumentException) {
@@ -435,7 +387,6 @@ class MockValueConstructor(
435387
val instanceModel = directSetterModel.instance
436388
val instance = value(instanceModel)
437389

438-
val instanceClassId = instanceModel.classId
439390
val fieldModel = directSetterModel.fieldModel
440391

441392
val field = directSetterModel.fieldId.jField
@@ -445,18 +396,8 @@ class MockValueConstructor(
445396
//set field accessible to support protected or package-private direct setters
446397
field.isAccessible = true
447398

448-
//prepare mockTarget for field if it is a mock
449-
val mockTarget = mockTarget(fieldModel) {
450-
FieldMockTarget(
451-
fieldModel.classId.name,
452-
instanceClassId.name,
453-
UtConcreteValue(javaClass(instanceClassId).anyInstance),
454-
field.name
455-
)
456-
}
457-
458399
//construct and set the value
459-
val fieldValue = construct(fieldModel, mockTarget).value
400+
val fieldValue = construct(fieldModel).value
460401
field.set(instance, fieldValue)
461402
} finally {
462403
//restore accessibility property of the field
@@ -467,14 +408,10 @@ class MockValueConstructor(
467408
/**
468409
* Constructs value from [UtModel].
469410
*/
470-
private fun value(model: UtModel) = construct(model, null).value
411+
private fun value(model: UtModel) = construct(model).value
471412

472413
private fun mockAndGet(model: UtModel): Any? {
473-
val target = mockTarget(model) { // won't be called if model is not mockModel
474-
val mockId = MockId("mock${++mockCounter}")
475-
ObjectMockTarget(model.classId.name, mockId)
476-
}
477-
return construct(model, target).value
414+
return construct(model).value
478415
}
479416

480417
private fun MethodId.call(args: List<Any?>, instance: Any?): Any? =
@@ -535,10 +472,4 @@ class MockValueConstructor(
535472
fun resetMockedMethods() {
536473
controllers.forEach { it.close() }
537474
}
538-
}
539-
540-
/**
541-
* Creates mock target using init lambda if model represents mock or null otherwise.
542-
*/
543-
private fun mockTarget(model: UtModel, init: () -> MockTarget): MockTarget? =
544-
if (model.isMockModel()) init() else null
475+
}

0 commit comments

Comments
 (0)