Skip to content

Commit 7b5463c

Browse files
authored
Fixed mocking fields in another classes in concrete execution (#2106)
1 parent fe0b89c commit 7b5463c

File tree

6 files changed

+91
-5
lines changed

6 files changed

+91
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.utbot.examples.mock.fields
2+
3+
import org.junit.jupiter.api.Test
4+
import org.utbot.framework.plugin.api.CodegenLanguage
5+
import org.utbot.framework.plugin.api.UtCompositeModel
6+
import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation
7+
import org.utbot.framework.plugin.api.UtPrimitiveModel
8+
import org.utbot.framework.plugin.api.util.id
9+
import org.utbot.testcheckers.eq
10+
import org.utbot.testing.CodeGeneration
11+
import org.utbot.testing.UtValueTestCaseChecker
12+
13+
class ClassUsingClassWithRandomFieldTest : UtValueTestCaseChecker(
14+
testClass = ClassUsingClassWithRandomField::class,
15+
testCodeGeneration = true,
16+
pipelines = listOf(
17+
TestLastStage(CodegenLanguage.JAVA),
18+
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) // because of mocks
19+
)
20+
) {
21+
@Test
22+
fun testUseClassWithRandomField() {
23+
checkMocksAndInstrumentation(
24+
ClassUsingClassWithRandomField::useClassWithRandomField,
25+
eq(1),
26+
{ mocks, instrumentation, r ->
27+
val noMocks = mocks.isEmpty()
28+
29+
val constructorMock = instrumentation.single() as UtNewInstanceInstrumentation
30+
val classIdEquality = constructorMock.classId == java.util.Random::class.id
31+
val callSiteIdEquality = constructorMock.callSites.single() == ClassWithRandomField::class.id
32+
val instance = constructorMock.instances.single() as UtCompositeModel
33+
val methodMock = instance.mocks.entries.single()
34+
val methodNameEquality = methodMock.key.name == "nextInt"
35+
val mockValueResult = r == (methodMock.value.single() as UtPrimitiveModel).value as Int
36+
37+
noMocks && classIdEquality && callSiteIdEquality && instance.isMock && methodNameEquality && mockValueResult
38+
}
39+
)
40+
}
41+
}

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/mock/MockClassVisitor.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ class MockClassVisitor(
6565
exceptions: Array<out String>?
6666
): MethodVisitor {
6767
val isNotSynthetic = access.and(Opcodes.ACC_SYNTHETIC) == 0
68-
// we do not want to mock <init> or <clinit> or synthetic methods
69-
return if (name != "<clinit>" && name != "<init>" && isNotSynthetic) {
68+
// we do not want to mock <clinit> or synthetic methods
69+
return if (name != "<clinit>" && isNotSynthetic) {
7070
visitStaticMethod(access, name, descriptor, signature, exceptions)
7171
} else {
7272
cv.visitMethod(access, name, descriptor, signature, exceptions)

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/JavaSerializerWrapper.kt

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class JavaSerializerWrapper : JavaSerializer() {
1717
val graphContext = kryo.graphContext
1818
var objectStream = graphContext.get<Any>(this) as ObjectInputStream?
1919
if (objectStream == null) {
20-
objectStream = WrappingObjectInputStream(input, kryo)
20+
objectStream = IgnoringUidWrappingObjectInputStream(input, kryo)
2121
graphContext.put(this, objectStream)
2222
}
2323
objectStream.readObject()
@@ -27,7 +27,7 @@ class JavaSerializerWrapper : JavaSerializer() {
2727
}
2828
}
2929

30-
class WrappingObjectInputStream(iss : InputStream?, private val kryo: Kryo) : ObjectInputStream(iss) {
30+
class IgnoringUidWrappingObjectInputStream(iss : InputStream?, private val kryo: Kryo) : ObjectInputStream(iss) {
3131
override fun resolveClass(type: ObjectStreamClass): Class<*>? {
3232
return try {
3333
Class.forName(type.name, false, kryo.classLoader)
@@ -43,4 +43,28 @@ class WrappingObjectInputStream(iss : InputStream?, private val kryo: Kryo) : Ob
4343
}
4444
}
4545
}
46-
}
46+
47+
// This overriding allows to ignore serialVersionUID during deserialization.
48+
// For more info, see https://stackoverflow.com/a/1816711
49+
override fun readClassDescriptor(): ObjectStreamClass {
50+
var resultClassDescriptor = super.readClassDescriptor() // initially streams descriptor
51+
52+
// the class in the local JVM that this descriptor represents.
53+
val localClass: Class<*> = try {
54+
Class.forName(resultClassDescriptor.name)
55+
} catch (e: ClassNotFoundException) {
56+
return resultClassDescriptor
57+
}
58+
59+
val localClassDescriptor = ObjectStreamClass.lookup(localClass) ?: return resultClassDescriptor
60+
61+
// only if class implements serializable
62+
val localSUID = localClassDescriptor.serialVersionUID
63+
val streamSUID = resultClassDescriptor.serialVersionUID
64+
if (streamSUID != localSUID) { // check for serialVersionUID mismatch.
65+
resultClassDescriptor = localClassDescriptor // Use local class descriptor for deserialization
66+
}
67+
68+
return resultClassDescriptor
69+
}
70+
}

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/KryoHelper.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ internal class TunedKryo : Kryo() {
107107

108108
this.setOptimizedGenerics(false)
109109

110+
// Kryo cannot (at least, the current used version) deserialize stacktraces that are required for SARIF reports.
110111
// TODO: JIRA:1492
111112
addDefaultSerializer(java.lang.Throwable::class.java, JavaSerializerWrapper())
112113

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.utbot.examples.mock.fields;
2+
3+
public class ClassUsingClassWithRandomField {
4+
public int useClassWithRandomField() {
5+
ClassWithRandomField classWithRandomField = new ClassWithRandomField();
6+
7+
return classWithRandomField.nextInt();
8+
}
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.utbot.examples.mock.fields;
2+
3+
import java.util.Random;
4+
5+
public class ClassWithRandomField {
6+
public Random random = new Random();
7+
8+
public int nextInt() {
9+
return random.nextInt();
10+
}
11+
}

0 commit comments

Comments
 (0)