Skip to content

Update Soot and choose an analyzable JRE #373 #890

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 3 commits into from
Sep 14, 2022
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
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ junit4_platform_version=1.9.0
mockito_version=3.5.13
z3_version=4.8.9.1
z3_java_api_version=4.8.9
soot_commit_hash=c6c78d9
soot_commit_hash=1f34746
# previous soot_commit_hash=c6c78d9
kotlin_version=1.7.10
log4j2_version=2.13.3
coroutines_version=1.6.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import org.utbot.framework.plugin.api.MockStrategyApi
import org.utbot.framework.plugin.api.TestCaseGenerator
import org.utbot.framework.plugin.api.TreatOverflowAsError
import org.utbot.framework.plugin.api.UtMethodTestSet
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
import org.utbot.summary.summarize
import java.io.File
import java.net.URLClassLoader
Expand All @@ -46,7 +47,7 @@ private val logger = KotlinLogging.logger {}
abstract class GenerateTestsAbstractCommand(name: String, help: String) :
CliktCommand(name = name, help = help) {

abstract val classPath:String?
abstract val classPath: String?

private val mockStrategy by option("-m", "--mock-strategy", help = "Defines the mock strategy")
.choice(
Expand Down Expand Up @@ -179,7 +180,11 @@ abstract class GenerateTestsAbstractCommand(name: String, help: String) :
}
}

protected fun generateTest(classUnderTest: ClassId, testClassname: String, testSets: List<UtMethodTestSet>): String =
protected fun generateTest(
classUnderTest: ClassId,
testClassname: String,
testSets: List<UtMethodTestSet>
): String =
initializeCodeGenerator(
testFramework,
classUnderTest
Expand All @@ -191,7 +196,12 @@ abstract class GenerateTestsAbstractCommand(name: String, help: String) :
// TODO: SAT-1566 Set UtSettings parameters.
UtSettings.treatOverflowAsError = treatOverflowAsError == TreatOverflowAsError.AS_ERROR

return TestCaseGenerator(workingDirectory, classPathNormalized, System.getProperty("java.class.path"))
return TestCaseGenerator(
workingDirectory,
classPathNormalized,
System.getProperty("java.class.path"),
JdkInfoDefaultProvider().info
)
}

private fun initializeCodeGenerator(testFramework: String, classUnderTest: ClassId): CodeGenerator {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import java.nio.file.Paths

data class JdkInfo(
val path: Path,
@Suppress("unused")
val version: Int
)

Expand All @@ -26,6 +25,9 @@ interface JdkInfoProvider {
val info: JdkInfo
}

/**
* Gets [JdkInfo] from the current process.
*/
open class JdkInfoDefaultProvider : JdkInfoProvider {
override val info: JdkInfo =
JdkInfo(Paths.get(System.getProperty("java.home")), fetchJavaVersion(System.getProperty("java.version")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.utbot.framework.codegen.MockitoStaticMocking;
import org.utbot.framework.plugin.api.*;
import org.utbot.framework.plugin.api.util.UtContext;
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider;
import org.utbot.framework.util.Snippet;
import org.utbot.framework.util.SootUtils;

Expand Down Expand Up @@ -1226,7 +1227,7 @@ public void testOnObjectWithArrayOfComplexArrays() {

@Test
public void testFuzzingSimple() {
SootUtils.INSTANCE.runSoot(StringSwitchExample.class, false);
SootUtils.INSTANCE.runSoot(StringSwitchExample.class, false, new JdkInfoDefaultProvider().getInfo());
UtBotJavaApi.setStopConcreteExecutorOnExit(false);

String classpath = getClassPath(StringSwitchExample.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.utbot.bytecode.versions

import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.utbot.examples.objects.SimpleDataClass
import org.utbot.framework.util.SootUtils.runSoot
import soot.Scene

@Suppress("UNREACHABLE_CODE")
@Disabled("TODO: https://github.com/UnitTestBot/UTBotJava/issues/891")
class SootTest {
@Test
fun `no method isBlank in JDK 8`() {
runSoot(
SimpleDataClass::class.java,
forceReload = true,
TODO("Get JDK 8")
)

val stringClass = Scene.v().getSootClass("java.lang.String")
assertFalse(stringClass.isPhantomClass)

val isBlankMethod = stringClass.getMethodByNameUnsafe("isBlank") // no such method in JDK 8
assertNull(isBlankMethod)
}

@Test
fun `method isBlank exists in JDK 11`() {
runSoot(
SimpleDataClass::class.java,
forceReload = true,
TODO("Get JDK 11")
)

val stringClass = Scene.v().getSootClass("java.lang.String")
assertFalse(stringClass.isPhantomClass)

val isBlankMethod = stringClass.getMethodByNameUnsafe("isBlank") // there is such method in JDK 11
assertNotNull(isBlankMethod)
}

@Test
fun `no records in JDK 11`() {
runSoot(
SimpleDataClass::class.java,
forceReload = true,
TODO("Get JDK 11")
)

val stringClass = Scene.v().getSootClass("java.lang.Record") // must not exist in JDK 11
assertTrue(stringClass.isPhantomClass)
}

@Test
fun `records exists in JDK 17`() {
runSoot(
SimpleDataClass::class.java,
forceReload = true,
TODO("Get JDK 17")
)

val stringClass = Scene.v().getSootClass("java.lang.Record") // must exist in JDK 17
assertFalse(stringClass.isPhantomClass)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package org.utbot.framework.assemble

import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.utbot.examples.assemble.AssembleTestUtils
import org.utbot.examples.assemble.ComplexField
import org.utbot.examples.assemble.DirectAccess
Expand All @@ -23,15 +29,16 @@ import org.utbot.examples.assemble.constructors.InheritComplexConstructor
import org.utbot.examples.assemble.constructors.InheritPrimitiveConstructor
import org.utbot.examples.assemble.constructors.PrimitiveConstructor
import org.utbot.examples.assemble.constructors.PrimitiveConstructorWithDefaultField
import org.utbot.examples.assemble.constructors.PrivateConstructor
import org.utbot.examples.assemble.constructors.PseudoComplexConstructor
import org.utbot.examples.assemble.defaults.DefaultField
import org.utbot.examples.assemble.defaults.DefaultFieldModifiedInConstructor
import org.utbot.examples.assemble.defaults.DefaultFieldWithDirectAccessor
import org.utbot.examples.assemble.constructors.PrivateConstructor
import org.utbot.examples.assemble.defaults.DefaultFieldWithSetter
import org.utbot.examples.assemble.defaults.DefaultPackagePrivateField
import org.utbot.examples.assemble.statics.StaticField
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.FieldId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtArrayModel
Expand All @@ -42,22 +49,15 @@ import org.utbot.framework.plugin.api.UtNullModel
import org.utbot.framework.plugin.api.UtPrimitiveModel
import org.utbot.framework.plugin.api.util.UtContext
import org.utbot.framework.plugin.api.util.UtContext.Companion.setUtContext
import org.utbot.framework.plugin.api.util.executableId
import org.utbot.framework.plugin.api.util.id
import org.utbot.framework.plugin.api.util.intArrayClassId
import org.utbot.framework.plugin.api.util.intClassId
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
import org.utbot.framework.util.SootUtils
import org.utbot.framework.util.instanceCounter
import org.utbot.framework.util.modelIdCounter
import kotlin.reflect.full.functions
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.util.executableId
import org.utbot.framework.util.SootUtils
import org.utbot.framework.util.sootMethod

/**
* Test classes must be located in the same folder as [AssembleTestUtils] class.
Expand All @@ -72,7 +72,7 @@ class AssembleModelGeneratorTests {
instanceCounter.set(0)
modelIdCounter.set(0)
statementsChain = mutableListOf()
SootUtils.runSoot(AssembleTestUtils::class.java, forceReload = false)
SootUtils.runSoot(AssembleTestUtils::class.java, forceReload = false, jdkInfo = JdkInfoDefaultProvider().info)
context = setUtContext(UtContext(AssembleTestUtils::class.java.classLoader))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
import org.utbot.framework.util.SootUtils

internal class UtBotFieldModificatorsTest {
Expand Down Expand Up @@ -169,7 +170,11 @@ internal class UtBotFieldModificatorsTest {
}

private fun initAnalysis() {
SootUtils.runSoot(PrimitiveModifications::class.java, forceReload = false)
SootUtils.runSoot(
PrimitiveModifications::class.java,
forceReload = false,
jdkInfo = JdkInfoDefaultProvider().info
)
fieldsModificatorsSearcher = UtBotFieldsModificatorsSearcher()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import org.utbot.framework.plugin.api.util.primitiveByWrapper
import org.utbot.framework.plugin.api.util.stringClassId
import org.utbot.framework.plugin.api.util.withUtContext
import org.utbot.framework.plugin.api.util.wrapperByPrimitive
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
import org.utbot.fuzzer.FuzzedValue
import org.utbot.fuzzer.ModelProvider
import org.utbot.fuzzer.ModelProvider.Companion.yieldValue
Expand Down Expand Up @@ -112,7 +113,7 @@ object UtBotJavaApi {

testSets.addAll(withUtContext(utContext) {
val buildPath = FileUtil.isolateClassFiles(classUnderTest).toPath()
TestCaseGenerator(buildPath, classpath, dependencyClassPath)
TestCaseGenerator(buildPath, classpath, dependencyClassPath, jdkInfo = JdkInfoDefaultProvider().info)
.generate(
methodsForAutomaticGeneration.map {
it.methodToBeTestedFromUserInput.executableId
Expand Down Expand Up @@ -172,7 +173,7 @@ object UtBotJavaApi {

return withUtContext(UtContext(classUnderTest.classLoader)) {
val buildPath = FileUtil.isolateClassFiles(classUnderTest).toPath()
TestCaseGenerator(buildPath, classpath, dependencyClassPath)
TestCaseGenerator(buildPath, classpath, dependencyClassPath, jdkInfo = JdkInfoDefaultProvider().info)
.generate(
methodsForAutomaticGeneration.map {
it.methodToBeTestedFromUserInput.executableId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class ConstructorAnalyzer {
private fun hasSuspiciousInstructions(jimpleBody: JimpleBody): Boolean =
jimpleBody.units.any {
it !is JIdentityStmt
&& !(it is JAssignStmt && it.rightBox.value !is InvokeExpr)
&& !(it is JAssignStmt && it.rightOp !is InvokeExpr)
&& it !is JInvokeStmt
&& it !is JReturnStmt
&& it !is JReturnVoidStmt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import org.utbot.framework.plugin.api.util.id
import org.utbot.framework.plugin.api.util.intArrayClassId
import org.utbot.framework.plugin.api.util.utContext
import org.utbot.framework.plugin.api.util.withUtContext
import org.utbot.framework.plugin.services.JdkInfo
import org.utbot.framework.util.SootUtils
import org.utbot.framework.util.jimpleBody
import org.utbot.framework.util.toModel
Expand All @@ -53,16 +54,18 @@ import kotlin.reflect.KCallable
* Note: the instantiating of [TestCaseGenerator] may take some time,
* because it requires initializing Soot for the current [buildDir] and [classpath].
*
* @param jdkInfo specifies the JRE and the runtime library version used for analysing system classes and user's code.
* @param forceSootReload forces to reinitialize Soot even if the previous buildDir equals to [buildDir] and previous
* classpath equals to [classpath]. This is the case for plugin scenario, as the source code may be modified.
*/
open class TestCaseGenerator(
private val buildDir: Path,
private val classpath: String?,
private val dependencyPaths: String,
private val jdkInfo: JdkInfo,
val engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(),
val isCanceled: () -> Boolean = { false },
val forceSootReload: Boolean = true
val forceSootReload: Boolean = true,
) {
private val logger: KLogger = KotlinLogging.logger {}
private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout")
Expand All @@ -82,7 +85,7 @@ open class TestCaseGenerator(
}

timeoutLogger.trace().bracket("Soot initialization") {
SootUtils.runSoot(buildDir, classpath, forceSootReload)
SootUtils.runSoot(buildDir, classpath, forceSootReload, jdkInfo)
}

//warmup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import org.utbot.engine.overrides.strings.UtStringBuffer
import org.utbot.engine.overrides.strings.UtStringBuilder
import org.utbot.engine.pureJavaSignature
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.services.JdkInfo
import soot.G
import soot.PackManager
import soot.Scene
Expand All @@ -55,24 +56,28 @@ object SootUtils {
/**
* Runs Soot in tests if it hasn't already been done.
*
* @param jdkInfo specifies the JRE and the runtime library version used for analysing system classes and user's
* code.
* @param forceReload forces to reinitialize Soot even if the [previousBuildDir] equals to the class buildDir.
*/
fun runSoot(clazz: java.lang.Class<*>, forceReload: kotlin.Boolean) {
fun runSoot(clazz: java.lang.Class<*>, forceReload: kotlin.Boolean, jdkInfo: JdkInfo) {
val buildDir = FileUtil.locateClassPath(clazz) ?: FileUtil.isolateClassFiles(clazz)
val buildDirPath = buildDir.toPath()

runSoot(buildDirPath, null, forceReload)
runSoot(buildDirPath, null, forceReload, jdkInfo)
}


/**
* @param jdkInfo specifies the JRE and the runtime library version used for analysing system classes and user's
* code.
* @param forceReload forces to reinitialize Soot even if the [previousBuildDir] equals to [buildDirPath] and
* [previousClassPath] equals to [classPath].
*/
fun runSoot(buildDirPath: Path, classPath: String?, forceReload: kotlin.Boolean) {
fun runSoot(buildDirPath: Path, classPath: String?, forceReload: kotlin.Boolean, jdkInfo: JdkInfo) {
synchronized(this) {
if (buildDirPath != previousBuildDir || classPath != previousClassPath || forceReload) {
initSoot(buildDirPath, classPath)
initSoot(buildDirPath, classPath, jdkInfo)
previousBuildDir = buildDirPath
previousClassPath = classPath
}
Expand All @@ -84,12 +89,14 @@ object SootUtils {
}

/**
Convert code to Jimple
* Convert code to Jimple
*/
private fun initSoot(buildDir: Path, classpath: String?) {
private fun initSoot(buildDir: Path, classpath: String?, jdkInfo: JdkInfo) {
G.reset()
val options = Options.v()

G.v().initJdk(G.JreInfo(jdkInfo.path.toString(), jdkInfo.version)) // init Soot with the right jdk

options.apply {
set_prepend_classpath(true)
// set true to debug. Disabled because of a bug when two different variables
Expand Down
Loading