Skip to content

Commit b0d671e

Browse files
authored
Introduce Tool.USVM in ContestEstimator, implement runUsvmGeneration (#2666)
* Add `Tool.USVM` in `ContestEstimator`, implement `runUsvmGeneration` * Remove redundant TODO * Bump UtBot dependencies version to match USVM dependencies * Use `UTestExecutionResult.trace` instead of `JcCoverage`
1 parent f6bc749 commit b0d671e

File tree

19 files changed

+921
-34
lines changed

19 files changed

+921
-34
lines changed

gradle.properties

+10-4
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,15 @@ junit4PlatformVersion=1.9.0
4141
# NOTE: Mockito versions 5+ are not compatible with Java 8: https://www.davidvlijmincx.com/posts/upgrade-to-mockito-5
4242
mockitoVersion=4.11.0
4343
mockitoInlineVersion=4.11.0
44-
ksmtVersion=0.4.3
44+
# TODO usvm-sbft-merge: UtBot engine uses ksmt 0.4.3, while USVM uses ksmt 0.5.7
45+
ksmtVersion=0.5.7
4546
sootVersion=4.4.0-FORK-2
4647
kotlinVersion=1.8.0
4748
log4j2Version=2.13.3
48-
coroutinesVersion=1.6.3
49-
collectionsVersion=0.3.4
49+
# TODO usvm-sbft-merge: UtBot uses kotlinx.coroutines 1.6.3, while USVM uses kotlinx.coroutines 1.6.4
50+
coroutinesVersion=1.6.4
51+
# TODO usvm-sbft-merge: UtBot uses kotlinx.collections 0.3.4, while USVM uses kotlinx.collections 0.3.5
52+
collectionsVersion=0.3.5
5053
# after updating plugin version you should manually bump corresponding versions in plugin
5154
# as they cannot be set from properties
5255
# utbot-intellij/build.gradle.kts
@@ -56,7 +59,8 @@ intellijPluginVersion=1.13.1
5659
# every time you bump rd version:
5760
# 1. regenerate all models
5861
# 2. check if rider plugin works
59-
rdVersion=2023.1.2
62+
# TODO usvm-sbft-merge: UtBot engine uses RD 2023.1.2, while USVM uses RD 2023.2.0
63+
rdVersion=2023.2.0
6064
# to enable - add -PincludeRiderInBuild=true in build CLI
6165
includeRiderInBuild=false
6266
jacocoVersion=0.8.8
@@ -77,6 +81,7 @@ testNgVersion=7.6.0
7781
kamlVersion=0.51.0
7882
jacksonVersion=2.12.3
7983
kotlinxSerializationVersion=1.5.0
84+
# TODO usvm-sbft: USVM uses slf4j 1.6.1
8085
slf4jVersion=1.7.36
8186
eclipseAetherVersion=1.1.0
8287
mavenWagonVersion=3.5.1
@@ -96,6 +101,7 @@ commonsLoggingVersion=1.2
96101
commonsIOVersion=2.11.0
97102
javaxVersion=2.2
98103
jakartaVersion=3.1.0
104+
jacoDbVersion=1.3.0
99105

100106
# use latest Java 8 compaitable Spring and Spring Boot versions
101107
springVersion=5.3.28

settings.gradle.kts

+4
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,8 @@ if (projectType == ultimateEdition) {
9191
}
9292

9393
include("utbot-light")
94+
9495
include("utbot-intellij-main")
96+
97+
// TODO usvm-sbft-merge: add if here if we want merge contest it into main
98+
includeBuild("../usvm")

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ abstract class UtExecution(
148148
val executableToCall get() = stateBefore.executableToCall
149149
}
150150

151+
interface UtExecutionWithInstrumentation {
152+
val instrumentation: List<UtInstrumentation>
153+
}
154+
151155
/**
152156
* Symbolic execution.
153157
*
@@ -163,15 +167,15 @@ class UtSymbolicExecution(
163167
stateBefore: EnvironmentModels,
164168
stateAfter: EnvironmentModels,
165169
result: UtExecutionResult,
166-
val instrumentation: List<UtInstrumentation>,
170+
override val instrumentation: List<UtInstrumentation>,
167171
val path: MutableList<Step>,
168172
val fullPath: List<Step>,
169173
coverage: Coverage? = null,
170174
summary: List<DocStatement>? = null,
171175
testMethodName: String? = null,
172176
displayName: String? = null,
173177
/** Convenient view of the full symbolic path */ val symbolicSteps: List<SymbolicStep> = listOf(),
174-
) : UtExecution(stateBefore, stateAfter, result, coverage, summary, testMethodName, displayName) {
178+
) : UtExecution(stateBefore, stateAfter, result, coverage, summary, testMethodName, displayName), UtExecutionWithInstrumentation {
175179
/**
176180
* By design the 'before' and 'after' states contain info about the same fields.
177181
* It means that it is not possible for a field to be present at 'before' and to be absent at 'after'.

utbot-framework-test/build.gradle

+3-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ dependencies {
4242
testImplementation group: 'org.mockito', name: 'mockito-inline', version: mockitoInlineVersion
4343
testImplementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: log4j2Version
4444

45-
implementation group: 'com.github.UnitTestBot.ksmt', name: 'ksmt-core', version: ksmtVersion
46-
implementation group: 'com.github.UnitTestBot.ksmt', name: 'ksmt-z3', version: ksmtVersion
45+
// TODO sbft-usvm-merge: UtBot engine expects `com.github.UnitTestBot.ksmt` here
46+
implementation group: 'io.ksmt', name: 'ksmt-core', version: ksmtVersion
47+
implementation group: 'io.ksmt', name: 'ksmt-z3', version: ksmtVersion
4748
}
4849

4950
// This is required to avoid conflict between SpringBoot standard logger and the logger of our project.

utbot-framework/build.gradle

+3-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ dependencies {
4242
implementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.8.1'
4343
implementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.8.1'
4444

45-
implementation group: 'com.github.UnitTestBot.ksmt', name: 'ksmt-core', version: ksmtVersion
46-
implementation group: 'com.github.UnitTestBot.ksmt', name: 'ksmt-z3', version: ksmtVersion
45+
// TODO sbft-usvm-merge: UtBot engine expects `com.github.UnitTestBot.ksmt` here
46+
implementation group: 'io.ksmt', name: 'ksmt-core', version: ksmtVersion
47+
implementation group: 'io.ksmt', name: 'ksmt-z3', version: ksmtVersion
4748

4849
fetchInstrumentationJar project(path: ':utbot-instrumentation', configuration: 'instrumentationArchive')
4950
}

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ import org.utbot.framework.plugin.api.UtExecution
9999
import org.utbot.framework.plugin.api.UtExecutionFailure
100100
import org.utbot.framework.plugin.api.UtExecutionResult
101101
import org.utbot.framework.plugin.api.UtExecutionSuccess
102+
import org.utbot.framework.plugin.api.UtExecutionWithInstrumentation
102103
import org.utbot.framework.plugin.api.UtExplicitlyThrownException
103104
import org.utbot.framework.plugin.api.UtLambdaModel
104105
import org.utbot.framework.plugin.api.UtModel
@@ -201,8 +202,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
201202

202203
protected fun setupInstrumentation() {
203204
val instrumentation = when (val execution = currentExecution) {
204-
is UtSymbolicExecution -> execution.instrumentation
205-
is UtFuzzedExecution -> execution.instrumentation
205+
is UtExecutionWithInstrumentation -> execution.instrumentation
206206
else -> return
207207
}
208208
if (instrumentation.isEmpty()) return

utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzer/UtFuzzedExecution.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ class UtFuzzedExecution(
2222
displayName: String? = null,
2323
val fuzzingValues: List<FuzzedValue>? = null,
2424
val fuzzedMethodDescription: FuzzedMethodDescription? = null,
25-
val instrumentation: List<UtInstrumentation> = emptyList(),
26-
) : UtExecution(stateBefore, stateAfter, result, coverage, summary, testMethodName, displayName) {
25+
override val instrumentation: List<UtInstrumentation> = emptyList(),
26+
) : UtExecution(stateBefore, stateAfter, result, coverage, summary, testMethodName, displayName), UtExecutionWithInstrumentation {
2727
/**
2828
* By design the 'before' and 'after' states contain info about the same fields.
2929
* It means that it is not possible for a field to be present at 'before' and to be absent at 'after'.

utbot-junit-contest/build.gradle

+13
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,19 @@ dependencies {
139139
implementation group: 'org.mockito', name: 'mockito-core', version: mockitoVersion
140140
implementation group: 'org.mockito', name: 'mockito-inline', version: mockitoInlineVersion
141141
implementation 'junit:junit:4.13.2'
142+
143+
implementation('org.usvm:usvm-core')
144+
implementation('org.usvm:usvm-jvm')
145+
implementation('org.usvm:usvm-jvm-instrumentation')
146+
147+
implementation group: "org.jacodb", name: "jacodb-core", version: jacoDbVersion
148+
implementation group: "org.jacodb", name: "jacodb-analysis", version: jacoDbVersion
149+
implementation group: "org.jacodb", name: "jacodb-approximations", version: jacoDbVersion
150+
151+
// TODO uvms-sbft-hack: UtBot has `fastutil:8.3.0` on the classpath that overrides classes from
152+
// `fastutil-core:8.5.11` that USVM adds. Solution: bump `fastutil` version to `8.5.11`
153+
runtimeOnly("it.unimi.dsi:fastutil:8.5.11")
154+
142155
testImplementation fileTree(dir: 'src/main/resources/projects/', include: '*/*.jar')
143156
testImplementation files('src/main/resources/evosuite/evosuite-1.2.0.jar')
144157
testImplementation files('src/main/resources/evosuite/evosuite-standalone-runtime-1.2.0.jar')

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ fun runGeneration(
417417
statsForClass
418418
}
419419

420-
private fun prepareClass(javaClazz: Class<*>, methodNameFilter: String?): List<ExecutableId> {
420+
fun prepareClass(javaClazz: Class<*>, methodNameFilter: String?): List<ExecutableId> {
421421
//1. all methods from cut
422422
val methods = javaClazz.declaredMethods
423423
.filterNot { it.isAbstract }
@@ -491,11 +491,13 @@ internal fun File.toUrl(): URL = toURI().toURL()
491491

492492
internal fun testMethodName(name: String, num: Int): String = "test${name.capitalize()}$num"
493493

494+
// TODO usvm-sbft: does SBFT allow to generate tests for private methods and constructors
495+
// If no, add more filtering here
494496
internal val Method.isVisibleFromGeneratedTest: Boolean
495497
get() = (this.modifiers and Modifier.ABSTRACT) == 0
496498
&& (this.modifiers and Modifier.NATIVE) == 0
497499

498-
private fun StatsForClass.updateCoverage(newCoverage: Coverage, isNewClass: Boolean, fromFuzzing: Boolean) {
500+
fun StatsForClass.updateCoverage(newCoverage: Coverage, isNewClass: Boolean, fromFuzzing: Boolean) {
499501
coverage.update(newCoverage, isNewClass)
500502
// other coverage type updates by empty coverage to respect new class
501503
val emptyCoverage = newCoverage.copy(

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

+84-16
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import org.utbot.contest.Paths.evosuiteReportFile
2626
import org.utbot.contest.Paths.jarsDir
2727
import org.utbot.contest.Paths.moduleTestDir
2828
import org.utbot.contest.Paths.outputDir
29+
import org.utbot.contest.usvm.runUsvmGeneration
2930
import org.utbot.features.FeatureExtractorFactoryImpl
3031
import org.utbot.features.FeatureProcessorWithStatesRepetitionFactory
3132
import org.utbot.framework.PathSelectorType
@@ -125,10 +126,20 @@ object Paths {
125126
}
126127

127128
@Suppress("unused")
128-
enum class Tool {
129-
UtBot {
130-
@OptIn(ObsoleteCoroutinesApi::class)
131-
@Suppress("EXPERIMENTAL_API_USAGE")
129+
interface Tool {
130+
abstract class UtBotBasedTool : Tool {
131+
abstract fun runGeneration(
132+
project: ProjectToEstimate,
133+
cut: ClassUnderTest,
134+
timeLimit: Long,
135+
fuzzingRatio: Double,
136+
methodNameFilter: String?,
137+
statsForProject: StatsForProject,
138+
compiledTestDir: File,
139+
classFqn: String,
140+
expectedExceptions: ExpectedExceptionsForClass
141+
) : StatsForClass
142+
132143
override fun run(
133144
project: ProjectToEstimate,
134145
cut: ClassUnderTest,
@@ -142,19 +153,21 @@ enum class Tool {
142153
) = withUtContext(ContextManager.createNewContext(project.classloader)) {
143154
val classStats: StatsForClass = try {
144155
runGeneration(
145-
project.name,
156+
project,
146157
cut,
147158
timeLimit,
148159
fuzzingRatio,
149-
project.sootClasspathString,
150-
runFromEstimator = true,
151-
expectedExceptions,
152-
methodNameFilter
160+
methodNameFilter,
161+
statsForProject,
162+
compiledTestDir,
163+
classFqn,
164+
expectedExceptions
153165
)
154166
} catch (e: CancellationException) {
155167
logger.info { "[$classFqn] finished with CancellationException" }
156168
return
157169
} catch (e: Throwable) {
170+
logger.error(e) { "ISOLATION: $e" }
158171
logger.info { "ISOLATION: $e" }
159172
logger.info { "continue without compilation" }
160173
return
@@ -198,8 +211,61 @@ enum class Tool {
198211
override fun moveProducedFilesIfNeeded() {
199212
// don't do anything
200213
}
201-
},
202-
EvoSuite {
214+
}
215+
216+
object UtBot : UtBotBasedTool() {
217+
@OptIn(ObsoleteCoroutinesApi::class)
218+
@Suppress("EXPERIMENTAL_API_USAGE")
219+
override fun runGeneration(
220+
project: ProjectToEstimate,
221+
cut: ClassUnderTest,
222+
timeLimit: Long,
223+
fuzzingRatio: Double,
224+
methodNameFilter: String?,
225+
statsForProject: StatsForProject,
226+
compiledTestDir: File,
227+
classFqn: String,
228+
expectedExceptions: ExpectedExceptionsForClass
229+
): StatsForClass {
230+
return runGeneration(
231+
project.name,
232+
cut,
233+
timeLimit,
234+
fuzzingRatio,
235+
project.sootClasspathString,
236+
runFromEstimator = true,
237+
expectedExceptions,
238+
methodNameFilter
239+
)
240+
}
241+
}
242+
243+
object USVM : UtBotBasedTool() {
244+
@OptIn(ObsoleteCoroutinesApi::class)
245+
@Suppress("EXPERIMENTAL_API_USAGE")
246+
override fun runGeneration(
247+
project: ProjectToEstimate,
248+
cut: ClassUnderTest,
249+
timeLimit: Long,
250+
fuzzingRatio: Double,
251+
methodNameFilter: String?,
252+
statsForProject: StatsForProject,
253+
compiledTestDir: File,
254+
classFqn: String,
255+
expectedExceptions: ExpectedExceptionsForClass
256+
): StatsForClass = runUsvmGeneration(
257+
project.name,
258+
cut,
259+
timeLimit,
260+
fuzzingRatio,
261+
project.sootClasspathString,
262+
runFromEstimator = true,
263+
expectedExceptions,
264+
methodNameFilter
265+
)
266+
}
267+
268+
object EvoSuite : Tool {
203269
override fun run(
204270
project: ProjectToEstimate,
205271
cut: ClassUnderTest,
@@ -272,7 +338,7 @@ enum class Tool {
272338
}
273339
};
274340

275-
abstract fun run(
341+
fun run(
276342
project: ProjectToEstimate,
277343
cut: ClassUnderTest,
278344
timeLimit: Long,
@@ -284,7 +350,7 @@ enum class Tool {
284350
expectedExceptions: ExpectedExceptionsForClass
285351
)
286352

287-
abstract fun moveProducedFilesIfNeeded()
353+
fun moveProducedFilesIfNeeded()
288354
}
289355

290356
fun main(args: Array<String>) {
@@ -295,7 +361,7 @@ fun main(args: Array<String>) {
295361
val tools: List<Tool>
296362

297363
// very special case when you run your project directly from IntellijIDEA omitting command line arguments
298-
if (args.isEmpty() && System.getProperty("os.name")?.run { contains("win", ignoreCase = true) } == true) {
364+
if (args.isEmpty()) {
299365
processedClassesThreshold = 9999 //change to change number of classes to run
300366
val timeLimit = 20 // increase if you want to debug something
301367
val fuzzingRatio = 0.1 // sets fuzzing ratio to total test generation
@@ -319,7 +385,8 @@ fun main(args: Array<String>) {
319385
// config for SBST 2022
320386
methodFilter = null
321387
projectFilter = listOf("fastjson-1.2.50", "guava-26.0", "seata-core-0.5.0", "spoon-core-7.0.0")
322-
tools = listOf(Tool.UtBot)
388+
// TODO usvm-sbft-merge: add if here if we want merge contest it into main
389+
tools = listOf(Tool.USVM)
323390

324391
estimatorArgs = arrayOf(
325392
classesLists,
@@ -339,7 +406,8 @@ fun main(args: Array<String>) {
339406
processedClassesThreshold = 9999
340407
methodFilter = null
341408
projectFilter = null
342-
tools = listOf(Tool.UtBot)
409+
// TODO usvm-sbft-merge: add if here if we want merge contest it into main
410+
tools = listOf(Tool.USVM)
343411
}
344412

345413
JdkInfoService.jdkInfoProvider = ContestEstimatorJdkInfoProvider(javaHome)

0 commit comments

Comments
 (0)