Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ object Versions {
const val clikt = "5.0.0"
const val detekt = "1.23.7"
const val ini4j = "0.5.4"
const val jacodb = "5acbadfed0"
const val jacodb = "213f9a1aee"
const val juliet = "1.3.2"
const val junit = "5.9.3"
const val kotlin = "2.1.0"
Expand Down
18 changes: 13 additions & 5 deletions buildSrc/src/main/kotlin/usvm.kotlin-conventions.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,25 @@ tasks {
}
}

tasks.named<Test>("test") {
// Use JUnit Platform for unit tests.
useJUnitPlatform()

tasks.withType<Test> {
maxHeapSize = "4G"

testLogging {
events("passed")
}
}

tasks.test {
useJUnitPlatform {
excludeTags("manual")
}
}

tasks.create("manualTest", Test::class) {
useJUnitPlatform {
includeTags("manual")
}
}

publishing {
repositories {
maven {
Expand Down
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ findProject(":usvm-python:usvm-python-runner")?.name = "usvm-python-runner"
include("usvm-python:usvm-python-commons")
findProject(":usvm-python:usvm-python-commons")?.name = "usvm-python-commons"

// Actually, `includeBuild("../jacodb")` is enough, but there is a bug in IDEA when path is a symlink.
// Actually, relative path is enough, but there is a bug in IDEA when the path is a symlink.
// As a workaround, we convert it to a real absolute path.
// See IDEA bug: https://youtrack.jetbrains.com/issue/IDEA-329756
// val jacodbPath = file("jacodb").takeIf { it.exists() }
Expand Down
1 change: 1 addition & 0 deletions usvm-ts-dataflow/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {

testFixturesImplementation(Libs.kotlin_logging)
testFixturesImplementation(Libs.junit_jupiter_api)
testFixturesImplementation(Libs.kotlinx_coroutines_core)
}

tasks.withType<Test> {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,52 +1,77 @@
package org.usvm.dataflow.ts

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import org.junit.jupiter.api.DynamicContainer
import org.junit.jupiter.api.DynamicNode
import org.junit.jupiter.api.DynamicTest
import org.junit.jupiter.api.function.Executable
import java.util.stream.Stream

private interface TestProvider {
fun test(name: String, test: () -> Unit)
}

private interface ContainerProvider {
fun container(name: String, init: TestContainerBuilder.() -> Unit)
}
@DslMarker
annotation class TestFactoryDsl

class TestContainerBuilder(var name: String) : TestProvider, ContainerProvider {
private val nodes: MutableList<DynamicNode> = mutableListOf()
@TestFactoryDsl
abstract class TestNodeBuilder {
private val nodeChannel = Channel<() -> DynamicNode>(Channel.UNLIMITED)

override fun test(name: String, test: () -> Unit) {
nodes += dynamicTest(name, test)
fun test(name: String, test: () -> Unit) {
nodeChannel.trySend { dynamicTest(name, test) }
}

override fun container(name: String, init: TestContainerBuilder.() -> Unit) {
nodes += containerBuilder(name, init)
fun container(name: String, init: TestContainerBuilder.() -> Unit) {
nodeChannel.trySend { dynamicContainer(name, init) }
}

fun build(): DynamicContainer = DynamicContainer.dynamicContainer(name, nodes)
}
protected fun createNodes(): Iterable<DynamicNode> =
Iterable { DynamicNodeIterator() }

private fun containerBuilder(name: String, init: TestContainerBuilder.() -> Unit): DynamicContainer =
TestContainerBuilder(name).apply(init).build()
private inner class DynamicNodeIterator : Iterator<DynamicNode> {
@OptIn(ExperimentalCoroutinesApi::class)
override fun hasNext(): Boolean = !nodeChannel.isEmpty

class TestFactoryBuilder : TestProvider, ContainerProvider {
private val nodes: MutableList<DynamicNode> = mutableListOf()

override fun test(name: String, test: () -> Unit) {
nodes += dynamicTest(name, test)
override fun next(): DynamicNode {
val node = nodeChannel.tryReceive().getOrThrow()
return node()
}
}
}

override fun container(name: String, init: TestContainerBuilder.() -> Unit) {
nodes += containerBuilder(name, init)
class TestContainerBuilder(var name: String) : TestNodeBuilder() {
fun build(): DynamicContainer {
return DynamicContainer.dynamicContainer(name, createNodes())
}
}

fun build(): Stream<out DynamicNode> = nodes.stream()
class TestFactoryBuilder : TestNodeBuilder() {
fun build(): Iterable<DynamicNode> {
return createNodes()
}
}

fun testFactory(init: TestFactoryBuilder.() -> Unit): Stream<out DynamicNode> =
inline fun testFactory(init: TestFactoryBuilder.() -> Unit): Iterable<DynamicNode> =
TestFactoryBuilder().apply(init).build()

private fun dynamicTest(name: String, test: () -> Unit): DynamicTest =
DynamicTest.dynamicTest(name, Executable(test))
DynamicTest.dynamicTest(name, test)

private fun dynamicContainer(name: String, init: TestContainerBuilder.() -> Unit): DynamicContainer =
TestContainerBuilder(name).apply(init).build()

inline fun <reified T> TestNodeBuilder.testForEach(
data: Iterable<T>,
crossinline nameProvider: (T) -> String = { it.toString() },
crossinline test: (T) -> Unit,
) {
data.forEach { item ->
test(nameProvider(item)) { test(item) }
}
}

inline fun <reified T> TestNodeBuilder.containerForEach(
data: Iterable<T>,
crossinline nameProvider: (T) -> String = { it.toString() },
crossinline init: TestContainerBuilder.(T) -> Unit,
) {
data.forEach { item ->
container(nameProvider(item)) { init(item) }
}
}
1 change: 1 addition & 0 deletions usvm-ts/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies {
testImplementation(Libs.mockk)
testImplementation(Libs.junit_jupiter_params)
testImplementation(Libs.logback)
testImplementation(testFixtures(project(":usvm-ts-dataflow")))

// https://mvnrepository.com/artifact/org.burningwave/core
// Use it to export all modules to all
Expand Down
86 changes: 43 additions & 43 deletions usvm-ts/src/test/kotlin/org/usvm/project/DemoCalc.kt
Original file line number Diff line number Diff line change
@@ -1,75 +1,75 @@
package org.usvm.project

import mu.KotlinLogging
import org.jacodb.ets.model.EtsScene
import org.jacodb.ets.utils.ANONYMOUS_CLASS_PREFIX
import org.jacodb.ets.utils.ANONYMOUS_METHOD_PREFIX
import org.jacodb.ets.utils.CONSTRUCTOR_NAME
import org.jacodb.ets.utils.INSTANCE_INIT_METHOD_NAME
import org.jacodb.ets.utils.STATIC_INIT_METHOD_NAME
import org.jacodb.ets.utils.loadEtsProjectFromIR
import org.junit.jupiter.api.condition.EnabledIf
import org.jacodb.ets.utils.loadEtsProjectAutoConvert
import org.junit.jupiter.api.Tag
import org.usvm.machine.TsMachine
import org.usvm.machine.TsOptions
import org.usvm.util.TsMethodTestRunner
import org.usvm.util.getResourcePath
import org.usvm.util.getResourcePathOrNull
import kotlin.test.Test

@EnabledIf("projectAvailable")
private val logger = KotlinLogging.logger {}

@Tag("manual")
class RunOnDemoCalcProject : TsMethodTestRunner() {

companion object {
private const val PROJECT_PATH = "/projects/Demo_Calc/etsir/entry"
private const val SDK_PATH = "/sdk/ohos/etsir"

@JvmStatic
private fun projectAvailable(): Boolean {
val isProjectPresent = getResourcePathOrNull(PROJECT_PATH) != null
val isSdkPreset = getResourcePathOrNull(SDK_PATH) != null
return isProjectPresent && isSdkPreset
}
private const val PROJECT_PATH = "/projects/Demo_Calc/source/entry"
private const val SDK_TS_PATH = "/sdk/typescript"
private const val SDK_OHOS_PATH = "/sdk/ohos/5.0.1.111/ets"
}

override val scene: EtsScene = run {
val projectPath = getResourcePath(PROJECT_PATH)
val sdkPath = getResourcePathOrNull(SDK_PATH)
?: error(
"Could not load SDK from resources '$SDK_PATH'. " +
"Try running './gradlew generateSdkIR' to generate it."
)
loadEtsProjectFromIR(projectPath, sdkPath)
val project = loadEtsProjectAutoConvert(getResourcePath(PROJECT_PATH))
val sdkFiles = listOf(SDK_TS_PATH, SDK_OHOS_PATH).flatMap { sdk ->
val sdkPath = getResourcePath(sdk)
val sdkProject = loadEtsProjectAutoConvert(sdkPath, useArkAnalyzerTypeInference = null)
sdkProject.projectFiles
}
EtsScene(project.projectFiles, sdkFiles, projectName = project.projectName)
}

@Test
fun `test run on each method`() {
fun `test run on each class`() {
val exceptions = mutableListOf<Throwable>()
val classes = scene.projectClasses.filterNot { it.name.startsWith(ANONYMOUS_CLASS_PREFIX) }
val classes = scene.projectClasses
.filterNot { it.name.startsWith(ANONYMOUS_CLASS_PREFIX) }

println("Total classes: ${classes.size}")

classes
.forEach { cls ->
val methods = cls.methods
.filterNot { it.cfg.stmts.isEmpty() }
.filterNot { it.isStatic }
.filterNot { it.name.startsWith(ANONYMOUS_METHOD_PREFIX) }
.filterNot { it.name == "build" }
.filterNot { it.name == INSTANCE_INIT_METHOD_NAME }
.filterNot { it.name == STATIC_INIT_METHOD_NAME }
.filterNot { it.name == CONSTRUCTOR_NAME }

if (methods.isEmpty()) return@forEach

runCatching {
val tsOptions = TsOptions()
TsMachine(scene, options, tsOptions).use { machine ->
val states = machine.analyze(methods)
states.let {}
}
}.onFailure {
exceptions += it
for (cls in classes) {
logger.info {
"Analyzing class ${cls.name} with ${cls.methods.size} methods"
}

val methods = cls.methods
.filterNot { it.cfg.stmts.isEmpty() }
.filterNot { it.isStatic }
.filterNot { it.name.startsWith(ANONYMOUS_METHOD_PREFIX) }
.filterNot { it.name == "build" }
.filterNot { it.name == INSTANCE_INIT_METHOD_NAME }
.filterNot { it.name == STATIC_INIT_METHOD_NAME }
.filterNot { it.name == CONSTRUCTOR_NAME }

if (methods.isEmpty()) continue

runCatching {
val tsOptions = TsOptions()
TsMachine(scene, options, tsOptions).use { machine ->
val states = machine.analyze(methods)
states.let {}
}
}.onFailure {
exceptions += it
}
}

val exc = exceptions.groupBy { it }
println("Total exceptions: ${exc.size}")
Expand Down
30 changes: 13 additions & 17 deletions usvm-ts/src/test/kotlin/org/usvm/project/DemoPhotos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.jacodb.ets.utils.CONSTRUCTOR_NAME
import org.jacodb.ets.utils.INSTANCE_INIT_METHOD_NAME
import org.jacodb.ets.utils.STATIC_INIT_METHOD_NAME
import org.jacodb.ets.utils.loadEtsProjectAutoConvert
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.condition.EnabledIf
import org.usvm.machine.TsMachine
import org.usvm.machine.TsOptions
Expand All @@ -18,29 +19,23 @@ import kotlin.test.Test

private val logger = KotlinLogging.logger {}

@EnabledIf("projectAvailable")
@Tag("manual")
class RunOnDemoPhotosProject : TsMethodTestRunner() {

companion object {
private const val PROJECT_PATH = "/projects/Demo_Photos/source/entry"
private const val SDK_PATH = "/sdk/ohos/etsir"

@JvmStatic
private fun projectAvailable(): Boolean {
val isProjectPresent = getResourcePathOrNull(PROJECT_PATH) != null
val isSdkPreset = getResourcePathOrNull(SDK_PATH) != null
return isProjectPresent && isSdkPreset
}
private const val SDK_TS_PATH = "/sdk/typescript"
private const val SDK_OHOS_PATH = "/sdk/ohos/5.0.1.111/ets"
}

override val scene: EtsScene = run {
val projectPath = getResourcePath(PROJECT_PATH)
val sdkPath = getResourcePathOrNull(SDK_PATH)
?: error(
"Could not load SDK from resources '$SDK_PATH'. " +
"Try running './gradlew generateSdkIR' to generate it."
)
loadEtsProjectAutoConvert(projectPath, sdkPath)
val project = loadEtsProjectAutoConvert(getResourcePath(PROJECT_PATH))
val sdkFiles = listOf(SDK_TS_PATH, SDK_OHOS_PATH).flatMap { sdk ->
val sdkPath = getResourcePath(sdk)
val sdkProject = loadEtsProjectAutoConvert(sdkPath, useArkAnalyzerTypeInference = null)
sdkProject.projectFiles
}
EtsScene(project.projectFiles, sdkFiles, projectName = project.projectName)
}

@Test
Expand Down Expand Up @@ -112,8 +107,9 @@ class RunOnDemoPhotosProject : TsMethodTestRunner() {
@Test
fun `test on particular method`() {
val method = scene.projectClasses
.filter { it.toString() == "@entry/utils/ResourceUtils: %dflt" }
.flatMap { it.methods }
.single { it.name == "onCreate" && it.enclosingClass?.name == "EntryAbility" }
.single { it.name == "getResourceString" && it.enclosingClass?.name == "%dflt" }

val tsOptions = TsOptions()
TsMachine(scene, options, tsOptions).use { machine ->
Expand Down
Loading