Skip to content

Initial implementations of the adapter from JcExecution to UtExecution #2677

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 31 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6d4f05c
Some awful attempts
EgorkaKulikov Oct 28, 2023
4aa8a10
Initially implemented a converter of UTestInst to UtModel
EgorkaKulikov Oct 30, 2023
ddcf870
Refactor JcToUtModelConverter basing on UTestInst2UtModel converter
EgorkaKulikov Oct 31, 2023
9532b8a
Some steps to implement JcToUtExecutionConverter
EgorkaKulikov Oct 31, 2023
d79ea8b
Implement minimalistic jc to ut execution conversion, enable codegen
IlyaMuravjov Nov 1, 2023
8e30b30
Some improvements
EgorkaKulikov Nov 1, 2023
5935d7b
DeepMapper for models is used
EgorkaKulikov Nov 3, 2023
535abb0
Corrections
EgorkaKulikov Nov 3, 2023
509cb73
Some improvements to JcToUtModelConverter
EgorkaKulikov Nov 3, 2023
21bde03
Further improvements to JcToUtModelConverter
EgorkaKulikov Nov 3, 2023
6471f07
Another converter little improvement
EgorkaKulikov Nov 3, 2023
c14067f
Improve `UtExecutionFailure` creation
IlyaMuravjov Nov 3, 2023
20967a9
Finish implementing `JcToUtModelConverter`
IlyaMuravjov Nov 5, 2023
bf6d497
Refactor nullability in `JcToUtModelConverter` and `JcToUtExecutionCo…
IlyaMuravjov Nov 5, 2023
1a893ad
First version of JC to UT converters without overusing `Descriptor2Va…
IlyaMuravjov Nov 5, 2023
1717235
Processed forgotten call method expression
EgorkaKulikov Nov 7, 2023
f2debe4
Make conversion more class-friendly (do not fail if one method cause …
EgorkaKulikov Nov 7, 2023
c191ed3
Make it possible to use samples in ContestEstimator
EgorkaKulikov Nov 7, 2023
e61cef4
Tested on all primitives
EgorkaKulikov Nov 7, 2023
bb82a6b
contrflow tests added
EgorkaKulikov Nov 7, 2023
13ee51e
More test classes added
EgorkaKulikov Nov 7, 2023
b01d6b3
Add `build/output/test/samples` to `utbot-junit-contest` test projects
IlyaMuravjov Nov 8, 2023
61c1608
Steps to avoid duplicating statements
EgorkaKulikov Nov 8, 2023
f455f99
Merge remote-tracking branch 'origin/egor/jc_to_ut_models_converter' …
EgorkaKulikov Nov 8, 2023
a31d6b1
Make it working correct on IntExamples.max
EgorkaKulikov Nov 8, 2023
98b4398
Remove OptimizeImportsProcessor (seems it was not called, but a sourc…
EgorkaKulikov Nov 8, 2023
0ca848a
Process UTestStaticMethodCall
EgorkaKulikov Nov 8, 2023
9442c72
Comment out includes for IDE related projects in `settings.gradle.kts`
IlyaMuravjov Nov 8, 2023
f37c1eb
Avoid using burningwave to export modules on Java 8
IlyaMuravjov Nov 8, 2023
cea02dd
Fix review comments
EgorkaKulikov Nov 8, 2023
813c2d6
Fix review comments
EgorkaKulikov Nov 9, 2023
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
24 changes: 12 additions & 12 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ include("utbot-summary-tests")
include("utbot-framework-test")
include("utbot-testing")
include("utbot-rd")
include("utbot-android-studio")
//include("utbot-android-studio")

if (includeRiderInBuild.toBoolean()) {
include("utbot-rider")
}
//if (includeRiderInBuild.toBoolean()) {
// include("utbot-rider")
//}

include("utbot-ui-commons")
//include("utbot-ui-commons")

include("utbot-spring-framework")
include("utbot-spring-commons-api")
Expand All @@ -63,14 +63,14 @@ include("utbot-spring-analyzer")
include("utbot-spring-sample")
include("utbot-spring-test")

if (javaIde.split(",").contains(ideType)) {
include("utbot-intellij")
}
//if (javaIde.split(",").contains(ideType)) {
// include("utbot-intellij")
//}

if (pythonIde.split(",").contains(ideType)) {
include("utbot-python")
include("utbot-cli-python")
include("utbot-intellij-python")
// include("utbot-intellij-python")
include("utbot-python-parser")
include("utbot-python-types")
include("utbot-python-executor")
Expand All @@ -80,19 +80,19 @@ if (projectType == ultimateEdition) {
if (jsBuild == buildType || jsIde.split(",").contains(ideType)) {
include("utbot-js")
include("utbot-cli-js")
include("utbot-intellij-js")
// include("utbot-intellij-js")
}

if (goIde.split(",").contains(ideType)) {
include("utbot-go")
include("utbot-cli-go")
include("utbot-intellij-go")
// include("utbot-intellij-go")
}
}

include("utbot-light")

include("utbot-intellij-main")
//include("utbot-intellij-main")

// TODO usvm-sbft-merge: add if here if we want merge contest it into main
includeBuild("../usvm")
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import org.utbot.framework.plugin.api.UtDirectGetFieldModel
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
import org.utbot.framework.plugin.api.UtExecutableCallModel
import org.utbot.framework.plugin.api.UtExecution
import org.utbot.framework.plugin.api.UtExecutionResult
import org.utbot.framework.plugin.api.UtExecutionSuccess
import org.utbot.framework.plugin.api.UtInstrumentation
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation
import org.utbot.framework.plugin.api.UtReferenceModel
import org.utbot.framework.plugin.api.UtStatementCallModel
import org.utbot.framework.plugin.api.UtStatementModel
import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation
import org.utbot.framework.plugin.api.isSuccess

inline fun <reified T : UtModel> T.mapPreservingType(mapper: UtModelMapper): T =
mapper.map(this, T::class.java)
Expand Down Expand Up @@ -54,6 +57,14 @@ fun EnvironmentModels.mapModels(mapper: UtModelMapper) = EnvironmentModels(
executableToCall = executableToCall,
)

fun UtExecutionResult.mapModelIfExists(mapper: UtModelMapper) = if (this.isSuccess) {
val successResult = this as UtExecutionSuccess
UtExecutionSuccess(successResult.model.map(mapper))
} else {
this
}


fun UtInstrumentation.mapModels(mapper: UtModelMapper) = when (this) {
is UtNewInstanceInstrumentation -> copy(instances = instances.mapModels(mapper))
is UtStaticMethodInstrumentation -> copy(values = values.mapModels(mapper))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,6 @@ object CodeGenerationController {
}

DumbService.getInstance(model.project).runWhenSmart {
OptimizeImportsProcessor(project, file).run()
codeStyleManager.reformat(file)
when (model.codegenLanguage) {
CodegenLanguage.JAVA -> {
Expand Down
3 changes: 3 additions & 0 deletions utbot-junit-contest/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def testProjects = [
'build/output/test/seata-core-0.5.0',
'build/output/test/spoon',
'build/output/test/spoon-core-7.0.0',
'build/output/test/samples',
]

sourceSets {
Expand Down Expand Up @@ -140,6 +141,8 @@ dependencies {
implementation group: 'org.mockito', name: 'mockito-inline', version: mockitoInlineVersion
implementation 'junit:junit:4.13.2'

implementation "org.burningwave:core:12.62.7"

implementation('org.usvm:usvm-core')
implementation('org.usvm:usvm-jvm')
implementation('org.usvm:usvm-jvm-instrumentation')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ interface Tool {
}

fun main(args: Array<String>) {
// See https://dzone.com/articles/how-to-export-all-modules-to-all-modules-at-runtime-in-java?preview=true
// `Modules` is `null` on JDK 8 (see comment to StaticComponentContainer.Modules)
org.burningwave.core.assembler.StaticComponentContainer.Modules?.exportAllToAll()

val estimatorArgs: Array<String>
val methodFilter: String?
val projectFilter: List<String>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import org.objectweb.asm.Type
import org.usvm.UMachineOptions
import org.usvm.instrumentation.util.toJcdbSignature
import org.usvm.machine.JcMachine
import org.usvm.machine.state.JcState
import org.utbot.common.ThreadBasedExecutor
import org.utbot.common.debug
import org.utbot.common.info
import org.utbot.common.measureTime
import org.utbot.contest.*
Expand All @@ -24,14 +27,18 @@ import org.utbot.framework.codegen.domain.junitByVersion
import org.utbot.framework.codegen.generator.CodeGenerator
import org.utbot.framework.codegen.generator.CodeGeneratorParams
import org.utbot.framework.codegen.services.language.CgLanguageAssistant
import org.utbot.framework.minimization.minimizeExecutions
import org.utbot.framework.plugin.api.*
import org.utbot.framework.plugin.api.util.constructor
import org.utbot.framework.plugin.api.util.jClass
import org.utbot.framework.plugin.api.util.method
import org.utbot.framework.plugin.services.JdkInfoService
import org.utbot.fuzzer.ReferencePreservingIntIdGenerator
import org.utbot.fuzzer.UtFuzzedExecution
import org.utbot.summary.summarizeAll
import java.io.File
import java.net.URLClassLoader
import java.util.*
import kotlin.math.max

private val logger = KotlinLogging.logger {}
Expand Down Expand Up @@ -80,6 +87,8 @@ fun runUsvmGeneration(

val resolver by lazy { JcTestExecutor(jcDbContainer.cp) }

val idGenerator = ReferencePreservingIntIdGenerator()

val instructionIds = mutableMapOf<Pair<String, Int>, Long>()
val instructionIdProvider = InstructionIdProvider { methodSignature, instrIndex ->
instructionIds.getOrPut(methodSignature to instrIndex) { instructionIds.size.toLong() }
Expand All @@ -98,6 +107,7 @@ fun runUsvmGeneration(

//remaining budget
val startTime = System.currentTimeMillis()
logger.debug { "STARTED COUNTING BUDGET FOR ${cut.classId.name}" }
fun remainingBudgetMillisWithoutCodegen() =
max(0, generationTimeoutMillisWithoutCodegen - (System.currentTimeMillis() - startTime))

Expand Down Expand Up @@ -139,14 +149,16 @@ fun runUsvmGeneration(
val totalBudgetPerMethod = remainingBudgetMillisWithoutCodegen() / filteredMethods.size
val concreteBudgetMsPerMethod = 500L
.coerceIn((totalBudgetPerMethod / 10L).. (totalBudgetPerMethod / 2L))
val symbolicBudgetPerMethod = totalBudgetPerMethod - concreteBudgetMsPerMethod
logger.debug { "Symbolic budget per method: $symbolicBudgetPerMethod" }

// TODO usvm-sbft: reuse same machine for different classes,
// right now I can't do that, because `timeoutMs` can't be changed after machine creation
logger.info().measureTime({ "preparation: creating JcMachine" }) {
JcMachine(
cp = jcDbContainer.cp,
// TODO usvm-sbft: we may want to tune UMachineOptions for contest
options = UMachineOptions(timeoutMs = totalBudgetPerMethod - concreteBudgetMsPerMethod)
options = UMachineOptions(timeoutMs = symbolicBudgetPerMethod)
)
}.use { machine ->
val jcClass = jcDbContainer.cp.findClass(cut.fqn)
Expand All @@ -167,7 +179,17 @@ fun runUsvmGeneration(
logger.error { "Method [$method] not found in jcClass [$jcClass]" }
continue
}
val states = machine.analyze(jcTypedMethod.method)
val states = logger.debug().measureTime({ "machine.analyze(${method.classId}.${method.signature})" }) {
((ThreadBasedExecutor.threadLocal.invokeWithTimeout(10 * symbolicBudgetPerMethod) {
machine.analyze(jcTypedMethod.method)
} as? Result<List<JcState>>) ?: run {
logger.error { "machine.analyze(${jcTypedMethod.method}) timed out" }
Result.success(emptyList())
}).getOrElse { e ->
logger.error("JcMachine failed", e)
emptyList()
}
}
val jcExecutions = states.mapNotNull {
// TODO usvm-sbft: if we have less than `runner.timeout` budget we should only let resolver run
// for `remainingBudgetMillisWithoutCodegen()` ms, right now last resolver call may exceed budget,
Expand All @@ -176,11 +198,13 @@ fun runUsvmGeneration(
// TODO usvm-sbft: right now this call fails unless you set:
// - "usvm-jvm-instrumentation-jar" environment variable to something like "/home/ilya/IdeaProjects/usvm/usvm-jvm-instrumentation/build/libs/usvm-jvm-instrumentation-1.0.jar"
// - "usvm-jvm-collectors-jar" environment variable to something like "/home/ilya/IdeaProjects/usvm/usvm-jvm-instrumentation/build/libs/usvm-jvm-instrumentation-collectors.jar"
runCatching {
resolver.resolve(jcTypedMethod, it)
}.getOrElse { e ->
logger.error(e) { "Resolver failed" }
null
logger.debug().measureTime({ "resolver.resolve(${method.classId}.${method.signature}, ...)" }) {
runCatching {
resolver.resolve(jcTypedMethod, it)
}.getOrElse { e ->
logger.error(e) { "Resolver failed" }
null
}
}
else null
}
Expand All @@ -193,7 +217,21 @@ fun runUsvmGeneration(
statsForClass.statsForMethods.add(statsForMethod)

val utExecutions: List<UtExecution> = jcExecutions.mapNotNull {
JcToUtExecutionConverter(instructionIdProvider).convert(it)
logger.debug().measureTime({ "Convert JcExecution" }) {
try {
JcToUtExecutionConverter(
jcExecution = it,
idGenerator = idGenerator,
instructionIdProvider = instructionIdProvider,
utilMethodProvider = codeGenerator.context.utilMethodProvider
).convert()
} catch (e: Exception) {
logger.error(e) {
"Can't convert execution for method ${method.name}, exception is ${e.message}"
}
null
}
}
}

utExecutions.forEach { result ->
Expand All @@ -220,19 +258,20 @@ fun runUsvmGeneration(
logger.error(e) { "Test generation failed during stats update" }
}
}
logger.debug { "Finished $method" }
}
}
}

// TODO usvm-sbft: codegen, requires proper UtUsvmExecution creation (not just coverage)
val testSets = testsByMethod.map { (method, executions) ->
UtMethodTestSet(method, minimizeExecutions(executions), jimpleBody = null)
}.summarizeAll(cut.classfileDir.toPath(), sourceFile = null)

logger.info().measureTime({ "Flushing tests for [${cut.simpleName}] on disk" }) {
writeTestClass(cut, codeGenerator.generateAsString(testSets))
}

// val testSets = testsByMethod.map { (method, executions) ->
// UtMethodTestSet(method, minimizeExecutions(executions), jimpleBody(method))
// }.summarizeAll(cut.classfileDir.toPath(), sourceFile = null)
//
// logger.info().measureTime({ "Flushing tests for [${cut.simpleName}] on disk" }) {
// writeTestClass(cut, codeGenerator.generateAsString(testSets))
// }
logger.debug { "STOPPED COUNTING BUDGET FOR ${cut.classId.name}" }

statsForClass
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.utbot.contest.usvm

import org.jacodb.analysis.library.analyzers.thisInstance
import org.jacodb.api.JcClassOrInterface
import org.jacodb.api.JcField
import org.jacodb.api.JcMethod
import org.jacodb.api.JcType
import org.jacodb.api.TypeName
import org.usvm.instrumentation.testcase.api.UTestInst
import org.usvm.instrumentation.testcase.descriptor.UTestObjectDescriptor
import org.usvm.instrumentation.testcase.descriptor.UTestValueDescriptor
import org.usvm.instrumentation.util.toJavaClass
import org.usvm.instrumentation.util.toJavaField
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.ConstructorId
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.util.fieldId
import org.utbot.framework.plugin.api.util.id
import org.utbot.framework.plugin.api.util.objectClassId
import org.utbot.framework.plugin.api.util.utContext

fun JcMethod.toExecutableId(): ExecutableId {
val type = this.thisInstance.type.classId
val parameters = this.parameters.map { it.type.classId }

if (isConstructor) {
return ConstructorId(type, parameters)
}

return MethodId(type, this.name, this.returnType.classId, parameters)
}

val JcType?.classId: ClassId
get() = this?.toJavaClass(utContext.classLoader)?.id
?: error("Can not construct classId for $this")

val JcClassOrInterface.classId: ClassId
get() = this.toJavaClass(utContext.classLoader).id

//TODO usvm-sbft: incorrectly converts types of com.google.common.util.concurrent.AtomicDoubleArray.<init> parameters
val TypeName.classId: ClassId
get() = ClassId(this.typeName)
Comment on lines +43 to +44
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least add TODO usvm-sbft: incorrectly converts types of com.google.common.util.concurrent.AtomicDoubleArray.<init> parameters


val JcField.fieldId: FieldId
get() = toJavaField(utContext.classLoader)!!.fieldId

val UTestValueDescriptor.origin: UTestInst?
get() = (this as? UTestObjectDescriptor)?.originUTestExpr
Loading