diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/mock/fields/ClassUsingClassWithRandomFieldTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/mock/fields/ClassUsingClassWithRandomFieldTest.kt new file mode 100644 index 0000000000..11e8146240 --- /dev/null +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/mock/fields/ClassUsingClassWithRandomFieldTest.kt @@ -0,0 +1,41 @@ +package org.utbot.examples.mock.fields + +import org.junit.jupiter.api.Test +import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation +import org.utbot.framework.plugin.api.UtPrimitiveModel +import org.utbot.framework.plugin.api.util.id +import org.utbot.testcheckers.eq +import org.utbot.testing.CodeGeneration +import org.utbot.testing.UtValueTestCaseChecker + +class ClassUsingClassWithRandomFieldTest : UtValueTestCaseChecker( + testClass = ClassUsingClassWithRandomField::class, + testCodeGeneration = true, + pipelines = listOf( + TestLastStage(CodegenLanguage.JAVA), + TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) // because of mocks + ) +) { + @Test + fun testUseClassWithRandomField() { + checkMocksAndInstrumentation( + ClassUsingClassWithRandomField::useClassWithRandomField, + eq(1), + { mocks, instrumentation, r -> + val noMocks = mocks.isEmpty() + + val constructorMock = instrumentation.single() as UtNewInstanceInstrumentation + val classIdEquality = constructorMock.classId == java.util.Random::class.id + val callSiteIdEquality = constructorMock.callSites.single() == ClassWithRandomField::class.id + val instance = constructorMock.instances.single() as UtCompositeModel + val methodMock = instance.mocks.entries.single() + val methodNameEquality = methodMock.key.name == "nextInt" + val mockValueResult = r == (methodMock.value.single() as UtPrimitiveModel).value as Int + + noMocks && classIdEquality && callSiteIdEquality && instance.isMock && methodNameEquality && mockValueResult + } + ) + } +} diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/mock/MockClassVisitor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/mock/MockClassVisitor.kt index ec108974e3..72f4e560a3 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/mock/MockClassVisitor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/mock/MockClassVisitor.kt @@ -65,8 +65,8 @@ class MockClassVisitor( exceptions: Array? ): MethodVisitor { val isNotSynthetic = access.and(Opcodes.ACC_SYNTHETIC) == 0 - // we do not want to mock or or synthetic methods - return if (name != "" && name != "" && isNotSynthetic) { + // we do not want to mock or synthetic methods + return if (name != "" && isNotSynthetic) { visitStaticMethod(access, name, descriptor, signature, exceptions) } else { cv.visitMethod(access, name, descriptor, signature, exceptions) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/JavaSerializerWrapper.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/JavaSerializerWrapper.kt index bf7e1fd310..0fe2ee0c86 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/JavaSerializerWrapper.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/JavaSerializerWrapper.kt @@ -17,7 +17,7 @@ class JavaSerializerWrapper : JavaSerializer() { val graphContext = kryo.graphContext var objectStream = graphContext.get(this) as ObjectInputStream? if (objectStream == null) { - objectStream = WrappingObjectInputStream(input, kryo) + objectStream = IgnoringUidWrappingObjectInputStream(input, kryo) graphContext.put(this, objectStream) } objectStream.readObject() @@ -27,7 +27,7 @@ class JavaSerializerWrapper : JavaSerializer() { } } -class WrappingObjectInputStream(iss : InputStream?, private val kryo: Kryo) : ObjectInputStream(iss) { +class IgnoringUidWrappingObjectInputStream(iss : InputStream?, private val kryo: Kryo) : ObjectInputStream(iss) { override fun resolveClass(type: ObjectStreamClass): Class<*>? { return try { Class.forName(type.name, false, kryo.classLoader) @@ -43,4 +43,28 @@ class WrappingObjectInputStream(iss : InputStream?, private val kryo: Kryo) : Ob } } } -} \ No newline at end of file + + // This overriding allows to ignore serialVersionUID during deserialization. + // For more info, see https://stackoverflow.com/a/1816711 + override fun readClassDescriptor(): ObjectStreamClass { + var resultClassDescriptor = super.readClassDescriptor() // initially streams descriptor + + // the class in the local JVM that this descriptor represents. + val localClass: Class<*> = try { + Class.forName(resultClassDescriptor.name) + } catch (e: ClassNotFoundException) { + return resultClassDescriptor + } + + val localClassDescriptor = ObjectStreamClass.lookup(localClass) ?: return resultClassDescriptor + + // only if class implements serializable + val localSUID = localClassDescriptor.serialVersionUID + val streamSUID = resultClassDescriptor.serialVersionUID + if (streamSUID != localSUID) { // check for serialVersionUID mismatch. + resultClassDescriptor = localClassDescriptor // Use local class descriptor for deserialization + } + + return resultClassDescriptor + } +} diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/KryoHelper.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/KryoHelper.kt index efd5045c4c..4dc6680d43 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/KryoHelper.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/KryoHelper.kt @@ -107,6 +107,7 @@ internal class TunedKryo : Kryo() { this.setOptimizedGenerics(false) + // Kryo cannot (at least, the current used version) deserialize stacktraces that are required for SARIF reports. // TODO: JIRA:1492 addDefaultSerializer(java.lang.Throwable::class.java, JavaSerializerWrapper()) diff --git a/utbot-sample/src/main/java/org/utbot/examples/mock/fields/ClassUsingClassWithRandomField.java b/utbot-sample/src/main/java/org/utbot/examples/mock/fields/ClassUsingClassWithRandomField.java new file mode 100644 index 0000000000..7e611f3b50 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/mock/fields/ClassUsingClassWithRandomField.java @@ -0,0 +1,9 @@ +package org.utbot.examples.mock.fields; + +public class ClassUsingClassWithRandomField { + public int useClassWithRandomField() { + ClassWithRandomField classWithRandomField = new ClassWithRandomField(); + + return classWithRandomField.nextInt(); + } +} diff --git a/utbot-sample/src/main/java/org/utbot/examples/mock/fields/ClassWithRandomField.java b/utbot-sample/src/main/java/org/utbot/examples/mock/fields/ClassWithRandomField.java new file mode 100644 index 0000000000..cfb9ae137d --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/mock/fields/ClassWithRandomField.java @@ -0,0 +1,11 @@ +package org.utbot.examples.mock.fields; + +import java.util.Random; + +public class ClassWithRandomField { + public Random random = new Random(); + + public int nextInt() { + return random.nextInt(); + } +}