Skip to content

Fixed mocking fields in another classes in concrete execution #2106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ class MockClassVisitor(
exceptions: Array<out String>?
): MethodVisitor {
val isNotSynthetic = access.and(Opcodes.ACC_SYNTHETIC) == 0
// we do not want to mock <init> or <clinit> or synthetic methods
return if (name != "<clinit>" && name != "<init>" && isNotSynthetic) {
// we do not want to mock <clinit> or synthetic methods
return if (name != "<clinit>" && isNotSynthetic) {
visitStaticMethod(access, name, descriptor, signature, exceptions)
} else {
cv.visitMethod(access, name, descriptor, signature, exceptions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class JavaSerializerWrapper : JavaSerializer() {
val graphContext = kryo.graphContext
var objectStream = graphContext.get<Any>(this) as ObjectInputStream?
if (objectStream == null) {
objectStream = WrappingObjectInputStream(input, kryo)
objectStream = IgnoringUidWrappingObjectInputStream(input, kryo)
graphContext.put(this, objectStream)
}
objectStream.readObject()
Expand All @@ -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)
Expand All @@ -43,4 +43,28 @@ class WrappingObjectInputStream(iss : InputStream?, private val kryo: Kryo) : Ob
}
}
}
}

// 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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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())

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.utbot.examples.mock.fields;

public class ClassUsingClassWithRandomField {
public int useClassWithRandomField() {
ClassWithRandomField classWithRandomField = new ClassWithRandomField();

return classWithRandomField.nextInt();
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}