Skip to content

Commit 092a27a

Browse files
Initial implementations of the adapter from JcExecution to UtExecution (#2677)
* Some awful attempts * Initially implemented a converter of UTestInst to UtModel * Refactor JcToUtModelConverter basing on UTestInst2UtModel converter * Some steps to implement JcToUtExecutionConverter * Implement minimalistic jc to ut execution conversion, enable codegen * Some improvements * DeepMapper for models is used * Corrections * Some improvements to JcToUtModelConverter * Further improvements to JcToUtModelConverter * Another converter little improvement * Improve `UtExecutionFailure` creation * Finish implementing `JcToUtModelConverter` * Refactor nullability in `JcToUtModelConverter` and `JcToUtExecutionConverter` * First version of JC to UT converters without overusing `Descriptor2ValueConverter` * Processed forgotten call method expression * Make conversion more class-friendly (do not fail if one method cause failure) * Make it possible to use samples in ContestEstimator * Tested on all primitives * contrflow tests added * More test classes added * Add `build/output/test/samples` to `utbot-junit-contest` test projects * Steps to avoid duplicating statements * Make it working correct on IntExamples.max * Remove OptimizeImportsProcessor (seems it was not called, but a source of bugs) * Process UTestStaticMethodCall * Comment out includes for IDE related projects in `settings.gradle.kts` * Avoid using burningwave to export modules on Java 8 * Fix review comments * Fix review comments --------- Co-authored-by: IlyaMuravjov <muravjovilya@gmail.com>
1 parent d5cdd8a commit 092a27a

File tree

15 files changed

+843
-87
lines changed

15 files changed

+843
-87
lines changed

settings.gradle.kts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ include("utbot-summary-tests")
4848
include("utbot-framework-test")
4949
include("utbot-testing")
5050
include("utbot-rd")
51-
include("utbot-android-studio")
51+
//include("utbot-android-studio")
5252

53-
if (includeRiderInBuild.toBoolean()) {
54-
include("utbot-rider")
55-
}
53+
//if (includeRiderInBuild.toBoolean()) {
54+
// include("utbot-rider")
55+
//}
5656

57-
include("utbot-ui-commons")
57+
//include("utbot-ui-commons")
5858

5959
include("utbot-spring-framework")
6060
include("utbot-spring-commons-api")
@@ -63,14 +63,14 @@ include("utbot-spring-analyzer")
6363
include("utbot-spring-sample")
6464
include("utbot-spring-test")
6565

66-
if (javaIde.split(",").contains(ideType)) {
67-
include("utbot-intellij")
68-
}
66+
//if (javaIde.split(",").contains(ideType)) {
67+
// include("utbot-intellij")
68+
//}
6969

7070
if (pythonIde.split(",").contains(ideType)) {
7171
include("utbot-python")
7272
include("utbot-cli-python")
73-
include("utbot-intellij-python")
73+
// include("utbot-intellij-python")
7474
include("utbot-python-parser")
7575
include("utbot-python-types")
7676
include("utbot-python-executor")
@@ -80,19 +80,19 @@ if (projectType == ultimateEdition) {
8080
if (jsBuild == buildType || jsIde.split(",").contains(ideType)) {
8181
include("utbot-js")
8282
include("utbot-cli-js")
83-
include("utbot-intellij-js")
83+
// include("utbot-intellij-js")
8484
}
8585

8686
if (goIde.split(",").contains(ideType)) {
8787
include("utbot-go")
8888
include("utbot-cli-go")
89-
include("utbot-intellij-go")
89+
// include("utbot-intellij-go")
9090
}
9191
}
9292

9393
include("utbot-light")
9494

95-
include("utbot-intellij-main")
95+
//include("utbot-intellij-main")
9696

9797
// TODO usvm-sbft-merge: add if here if we want merge contest it into main
9898
includeBuild("../usvm")

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/mapper/Utils.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ import org.utbot.framework.plugin.api.UtDirectGetFieldModel
55
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
66
import org.utbot.framework.plugin.api.UtExecutableCallModel
77
import org.utbot.framework.plugin.api.UtExecution
8+
import org.utbot.framework.plugin.api.UtExecutionResult
9+
import org.utbot.framework.plugin.api.UtExecutionSuccess
810
import org.utbot.framework.plugin.api.UtInstrumentation
911
import org.utbot.framework.plugin.api.UtModel
1012
import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation
1113
import org.utbot.framework.plugin.api.UtReferenceModel
1214
import org.utbot.framework.plugin.api.UtStatementCallModel
1315
import org.utbot.framework.plugin.api.UtStatementModel
1416
import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation
17+
import org.utbot.framework.plugin.api.isSuccess
1518

1619
inline fun <reified T : UtModel> T.mapPreservingType(mapper: UtModelMapper): T =
1720
mapper.map(this, T::class.java)
@@ -54,6 +57,14 @@ fun EnvironmentModels.mapModels(mapper: UtModelMapper) = EnvironmentModels(
5457
executableToCall = executableToCall,
5558
)
5659

60+
fun UtExecutionResult.mapModelIfExists(mapper: UtModelMapper) = if (this.isSuccess) {
61+
val successResult = this as UtExecutionSuccess
62+
UtExecutionSuccess(successResult.model.map(mapper))
63+
} else {
64+
this
65+
}
66+
67+
5768
fun UtInstrumentation.mapModels(mapper: UtModelMapper) = when (this) {
5869
is UtNewInstanceInstrumentation -> copy(instances = instances.mapModels(mapper))
5970
is UtStaticMethodInstrumentation -> copy(values = values.mapModels(mapper))

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,6 @@ object CodeGenerationController {
776776
}
777777

778778
DumbService.getInstance(model.project).runWhenSmart {
779-
OptimizeImportsProcessor(project, file).run()
780779
codeStyleManager.reformat(file)
781780
when (model.codegenLanguage) {
782781
CodegenLanguage.JAVA -> {

utbot-junit-contest/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def testProjects = [
2929
'build/output/test/seata-core-0.5.0',
3030
'build/output/test/spoon',
3131
'build/output/test/spoon-core-7.0.0',
32+
'build/output/test/samples',
3233
]
3334

3435
sourceSets {
@@ -140,6 +141,8 @@ dependencies {
140141
implementation group: 'org.mockito', name: 'mockito-inline', version: mockitoInlineVersion
141142
implementation 'junit:junit:4.13.2'
142143

144+
implementation "org.burningwave:core:12.62.7"
145+
143146
implementation('org.usvm:usvm-core')
144147
implementation('org.usvm:usvm-jvm')
145148
implementation('org.usvm:usvm-jvm-instrumentation')

utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimator.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,10 @@ interface Tool {
354354
}
355355

356356
fun main(args: Array<String>) {
357+
// See https://dzone.com/articles/how-to-export-all-modules-to-all-modules-at-runtime-in-java?preview=true
358+
// `Modules` is `null` on JDK 8 (see comment to StaticComponentContainer.Modules)
359+
org.burningwave.core.assembler.StaticComponentContainer.Modules?.exportAllToAll()
360+
357361
val estimatorArgs: Array<String>
358362
val methodFilter: String?
359363
val projectFilter: List<String>?

utbot-junit-contest/src/main/kotlin/org/utbot/contest/usvm/ContestUsvm.kt

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import org.objectweb.asm.Type
1111
import org.usvm.UMachineOptions
1212
import org.usvm.instrumentation.util.toJcdbSignature
1313
import org.usvm.machine.JcMachine
14+
import org.usvm.machine.state.JcState
15+
import org.utbot.common.ThreadBasedExecutor
16+
import org.utbot.common.debug
1417
import org.utbot.common.info
1518
import org.utbot.common.measureTime
1619
import org.utbot.contest.*
@@ -24,14 +27,18 @@ import org.utbot.framework.codegen.domain.junitByVersion
2427
import org.utbot.framework.codegen.generator.CodeGenerator
2528
import org.utbot.framework.codegen.generator.CodeGeneratorParams
2629
import org.utbot.framework.codegen.services.language.CgLanguageAssistant
30+
import org.utbot.framework.minimization.minimizeExecutions
2731
import org.utbot.framework.plugin.api.*
2832
import org.utbot.framework.plugin.api.util.constructor
2933
import org.utbot.framework.plugin.api.util.jClass
3034
import org.utbot.framework.plugin.api.util.method
3135
import org.utbot.framework.plugin.services.JdkInfoService
36+
import org.utbot.fuzzer.ReferencePreservingIntIdGenerator
3237
import org.utbot.fuzzer.UtFuzzedExecution
38+
import org.utbot.summary.summarizeAll
3339
import java.io.File
3440
import java.net.URLClassLoader
41+
import java.util.*
3542
import kotlin.math.max
3643

3744
private val logger = KotlinLogging.logger {}
@@ -80,6 +87,8 @@ fun runUsvmGeneration(
8087

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

90+
val idGenerator = ReferencePreservingIntIdGenerator()
91+
8392
val instructionIds = mutableMapOf<Pair<String, Int>, Long>()
8493
val instructionIdProvider = InstructionIdProvider { methodSignature, instrIndex ->
8594
instructionIds.getOrPut(methodSignature to instrIndex) { instructionIds.size.toLong() }
@@ -98,6 +107,7 @@ fun runUsvmGeneration(
98107

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

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

143155
// TODO usvm-sbft: reuse same machine for different classes,
144156
// right now I can't do that, because `timeoutMs` can't be changed after machine creation
145157
logger.info().measureTime({ "preparation: creating JcMachine" }) {
146158
JcMachine(
147159
cp = jcDbContainer.cp,
148160
// TODO usvm-sbft: we may want to tune UMachineOptions for contest
149-
options = UMachineOptions(timeoutMs = totalBudgetPerMethod - concreteBudgetMsPerMethod)
161+
options = UMachineOptions(timeoutMs = symbolicBudgetPerMethod)
150162
)
151163
}.use { machine ->
152164
val jcClass = jcDbContainer.cp.findClass(cut.fqn)
@@ -167,7 +179,17 @@ fun runUsvmGeneration(
167179
logger.error { "Method [$method] not found in jcClass [$jcClass]" }
168180
continue
169181
}
170-
val states = machine.analyze(jcTypedMethod.method)
182+
val states = logger.debug().measureTime({ "machine.analyze(${method.classId}.${method.signature})" }) {
183+
((ThreadBasedExecutor.threadLocal.invokeWithTimeout(10 * symbolicBudgetPerMethod) {
184+
machine.analyze(jcTypedMethod.method)
185+
} as? Result<List<JcState>>) ?: run {
186+
logger.error { "machine.analyze(${jcTypedMethod.method}) timed out" }
187+
Result.success(emptyList())
188+
}).getOrElse { e ->
189+
logger.error("JcMachine failed", e)
190+
emptyList()
191+
}
192+
}
171193
val jcExecutions = states.mapNotNull {
172194
// TODO usvm-sbft: if we have less than `runner.timeout` budget we should only let resolver run
173195
// for `remainingBudgetMillisWithoutCodegen()` ms, right now last resolver call may exceed budget,
@@ -176,11 +198,13 @@ fun runUsvmGeneration(
176198
// TODO usvm-sbft: right now this call fails unless you set:
177199
// - "usvm-jvm-instrumentation-jar" environment variable to something like "/home/ilya/IdeaProjects/usvm/usvm-jvm-instrumentation/build/libs/usvm-jvm-instrumentation-1.0.jar"
178200
// - "usvm-jvm-collectors-jar" environment variable to something like "/home/ilya/IdeaProjects/usvm/usvm-jvm-instrumentation/build/libs/usvm-jvm-instrumentation-collectors.jar"
179-
runCatching {
180-
resolver.resolve(jcTypedMethod, it)
181-
}.getOrElse { e ->
182-
logger.error(e) { "Resolver failed" }
183-
null
201+
logger.debug().measureTime({ "resolver.resolve(${method.classId}.${method.signature}, ...)" }) {
202+
runCatching {
203+
resolver.resolve(jcTypedMethod, it)
204+
}.getOrElse { e ->
205+
logger.error(e) { "Resolver failed" }
206+
null
207+
}
184208
}
185209
else null
186210
}
@@ -193,7 +217,21 @@ fun runUsvmGeneration(
193217
statsForClass.statsForMethods.add(statsForMethod)
194218

195219
val utExecutions: List<UtExecution> = jcExecutions.mapNotNull {
196-
JcToUtExecutionConverter(instructionIdProvider).convert(it)
220+
logger.debug().measureTime({ "Convert JcExecution" }) {
221+
try {
222+
JcToUtExecutionConverter(
223+
jcExecution = it,
224+
idGenerator = idGenerator,
225+
instructionIdProvider = instructionIdProvider,
226+
utilMethodProvider = codeGenerator.context.utilMethodProvider
227+
).convert()
228+
} catch (e: Exception) {
229+
logger.error(e) {
230+
"Can't convert execution for method ${method.name}, exception is ${e.message}"
231+
}
232+
null
233+
}
234+
}
197235
}
198236

199237
utExecutions.forEach { result ->
@@ -220,19 +258,20 @@ fun runUsvmGeneration(
220258
logger.error(e) { "Test generation failed during stats update" }
221259
}
222260
}
261+
logger.debug { "Finished $method" }
223262
}
224263
}
225264
}
226265

227-
// TODO usvm-sbft: codegen, requires proper UtUsvmExecution creation (not just coverage)
266+
val testSets = testsByMethod.map { (method, executions) ->
267+
UtMethodTestSet(method, minimizeExecutions(executions), jimpleBody = null)
268+
}.summarizeAll(cut.classfileDir.toPath(), sourceFile = null)
269+
270+
logger.info().measureTime({ "Flushing tests for [${cut.simpleName}] on disk" }) {
271+
writeTestClass(cut, codeGenerator.generateAsString(testSets))
272+
}
228273

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

237276
statsForClass
238277
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.utbot.contest.usvm
2+
3+
import org.jacodb.analysis.library.analyzers.thisInstance
4+
import org.jacodb.api.JcClassOrInterface
5+
import org.jacodb.api.JcField
6+
import org.jacodb.api.JcMethod
7+
import org.jacodb.api.JcType
8+
import org.jacodb.api.TypeName
9+
import org.usvm.instrumentation.testcase.api.UTestInst
10+
import org.usvm.instrumentation.testcase.descriptor.UTestObjectDescriptor
11+
import org.usvm.instrumentation.testcase.descriptor.UTestValueDescriptor
12+
import org.usvm.instrumentation.util.toJavaClass
13+
import org.usvm.instrumentation.util.toJavaField
14+
import org.utbot.framework.plugin.api.ClassId
15+
import org.utbot.framework.plugin.api.ConstructorId
16+
import org.utbot.framework.plugin.api.ExecutableId
17+
import org.utbot.framework.plugin.api.FieldId
18+
import org.utbot.framework.plugin.api.MethodId
19+
import org.utbot.framework.plugin.api.util.fieldId
20+
import org.utbot.framework.plugin.api.util.id
21+
import org.utbot.framework.plugin.api.util.objectClassId
22+
import org.utbot.framework.plugin.api.util.utContext
23+
24+
fun JcMethod.toExecutableId(): ExecutableId {
25+
val type = this.thisInstance.type.classId
26+
val parameters = this.parameters.map { it.type.classId }
27+
28+
if (isConstructor) {
29+
return ConstructorId(type, parameters)
30+
}
31+
32+
return MethodId(type, this.name, this.returnType.classId, parameters)
33+
}
34+
35+
val JcType?.classId: ClassId
36+
get() = this?.toJavaClass(utContext.classLoader)?.id
37+
?: error("Can not construct classId for $this")
38+
39+
val JcClassOrInterface.classId: ClassId
40+
get() = this.toJavaClass(utContext.classLoader).id
41+
42+
//TODO usvm-sbft: incorrectly converts types of com.google.common.util.concurrent.AtomicDoubleArray.<init> parameters
43+
val TypeName.classId: ClassId
44+
get() = ClassId(this.typeName)
45+
46+
val JcField.fieldId: FieldId
47+
get() = toJavaField(utContext.classLoader)!!.fieldId
48+
49+
val UTestValueDescriptor.origin: UTestInst?
50+
get() = (this as? UTestObjectDescriptor)?.originUTestExpr

0 commit comments

Comments
 (0)