Skip to content

Commit

Permalink
Automate construction of ArkIR JSONs (#241)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lipen authored Jul 24, 2024
1 parent 4a75998 commit 30594f5
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 109 deletions.
104 changes: 46 additions & 58 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
branches:
- develop
- neo
workflow_dispatch:

permissions:
contents: read
Expand Down Expand Up @@ -49,6 +50,51 @@ jobs:
# Builds on other branches will only read existing entries from the cache.
cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }}

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '22'

- name: Set up ArkAnalyzer
run: |
REPO_URL="https://gitee.com/openharmony-sig/arkanalyzer.git"
DEST_DIR="arkanalyzer"
MAX_RETRIES=10
RETRY_DELAY=3 # Delay between retries in seconds
for ((i=1; i<=MAX_RETRIES; i++)); do
git clone --depth=1 $REPO_URL $DEST_DIR && break
echo "Clone failed, retrying in $RETRY_DELAY seconds..."
sleep "$RETRY_DELAY"
done
if [[ $i -gt $MAX_RETRIES ]]; then
echo "Failed to clone the repository after $MAX_RETRIES attempts."
exit 1
else
echo "Repository cloned successfully."
fi
echo "ARKANALYZER_DIR=$(realpath $DEST_DIR)" >> $GITHUB_ENV
cd $DEST_DIR
# checkout master on 2024-07-17
rev=a9d9fd6070fce5896d8e760ed7fd175b62b16605
for ((i=1; i<=MAX_RETRIES; i++)); do
git fetch --depth=1 origin $rev && break
echo "Fetch failed, retrying in $RETRY_DELAY seconds..."
sleep "$RETRY_DELAY"
done
git switch --detach $rev
npm install
npm run build
- name: Run ETS tests first
run: ./gradlew :jacodb-ets:test --scan

- name: Build and run tests
run: ./gradlew build --stacktrace --scan

Expand Down Expand Up @@ -111,61 +157,3 @@ jobs:
with:
files: "**/build/test-results/**/*.xml"
check_name: "Lifecycle test results"

build-ets:
name: Build and run jacodb-ets
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: 8
distribution: 'zulu'

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '22'

- name: Set up ArkAnalyzer
run: |
REPO_URL="https://gitee.com/openharmony-sig/arkanalyzer.git"
DEST_DIR="arkanalyzer"
MAX_RETRIES=10
RETRY_DELAY=5 # Delay between retries in seconds
for ((i=1; i<=MAX_RETRIES; i++)); do
git clone --depth=1 $REPO_URL $DEST_DIR && break
echo "Clone failed, retrying in $RETRY_DELAY seconds..."
sleep "$RETRY_DELAY"
done
if [[ $i -gt $MAX_RETRIES ]]; then
echo "Failed to clone the repository after $MAX_RETRIES attempts."
exit 1
else
echo "Repository cloned successfully."
fi
cd $DEST_DIR
# checkout master on 2024-07-17
rev=a9d9fd6070fce5896d8e760ed7fd175b62b16605
git fetch --depth=1 origin $rev
git switch --detach $rev
npm install
npm run build
echo "ARKANALYZER_DIR=$DEST_DIR" >> $GITHUB_ENV
- name: Generate test resources for jacodb-ets module
run: ./gradlew :jacodb-ets:generateTestResources

- name: Build and run tests in jacodb-ets module
run: ./gradlew --scan :jacodb-ets:test
4 changes: 2 additions & 2 deletions jacodb-ets/ARKANALYZER.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ Clone and install the ArkAnalyzer via NPM:

```shell
cd ~/dev
git clone --branch lipen/json-printer https://gitee.com/openharmony-sig/arkanalyzer
git clone https://gitee.com/openharmony-sig/arkanalyzer
cd arkanalyzer
npm install
nmp run build
npm run build
```

The `npm run build` command will generate the `out` directory, which contains the compiled code.
Expand Down
12 changes: 7 additions & 5 deletions jacodb-ets/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ dependencies {
// ARKANALYZER_DIR=~/dev/arkanalyzer ./gradlew generateTestResources
// ```
tasks.register("generateTestResources") {
group = "build"
description = "Generates test resources from TypeScript files using ArkAnalyzer."
doLast {
val envVarName = "ARKANALYZER_DIR"
val defaultArkAnalyzerDir = "../arkanalyzer"
Expand All @@ -42,16 +44,16 @@ tasks.register("generateTestResources") {
}

val resources = projectDir.resolve("src/test/resources")
val input = resources.resolve("source")
val output = resources.resolve("etsir/generated")
println("Generating test resources in '${output.relativeTo(projectDir)}'...")
val inputDir = resources.resolve("source")
val outputDir = resources.resolve("etsir/generated")
println("Generating test resources in '${outputDir.relativeTo(projectDir)}'...")

val cmd: List<String> = listOf(
"node",
script.absolutePath,
"--multi",
input.relativeTo(resources).path,
output.relativeTo(resources).path,
inputDir.relativeTo(resources).path,
outputDir.relativeTo(resources).path,
)
println("Running: '${cmd.joinToString(" ")}'")
val process = ProcessBuilder(cmd).directory(resources).start();
Expand Down
8 changes: 2 additions & 6 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Model.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,13 @@ data class EtsFileDto(
val exportInfos: List<ExportInfoDto>,
) {
companion object {
private val json = Json {
prettyPrint = true
}

fun loadFromJson(jsonString: String): EtsFileDto {
return json.decodeFromString(jsonString)
return Json.decodeFromString(jsonString)
}

@OptIn(ExperimentalSerializationApi::class)
fun loadFromJson(stream: InputStream): EtsFileDto {
return json.decodeFromStream(stream)
return Json.decodeFromStream(stream)
}
}
}
Expand Down
85 changes: 85 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2022 UnitTestBot contributors (utbot.org)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jacodb.ets.utils

import mu.KotlinLogging
import org.jacodb.ets.dto.EtsFileDto
import org.jacodb.ets.dto.convertToEtsFile
import org.jacodb.ets.model.EtsFile
import java.io.File
import java.io.FileNotFoundException
import java.nio.file.Paths
import java.util.concurrent.TimeUnit
import kotlin.io.path.exists
import kotlin.io.path.inputStream
import kotlin.io.path.notExists
import kotlin.io.path.pathString

private val logger = KotlinLogging.logger {}

private const val ENV_VAR_ARK_ANALYZER_DIR = "ARKANALYZER_DIR"
private const val DEFAULT_ARK_ANALYZER_DIR = "arkanalyzer"

private const val ENV_VAR_SERIALIZE_SCRIPT_PATH = "SERIALIZE_SCRIPT_PATH"
private const val DEFAULT_SERIALIZE_SCRIPT_PATH = "out/src/save/serializeArkIR.js"

private const val ENV_VAR_NODE_EXECUTABLE = "NODE_EXECUTABLE"
private const val DEFAULT_NODE_EXECUTABLE = "node"

fun loadEtsFileAutoConvert(tsPath: String): EtsFile {
val arkAnalyzerDir = Paths.get(System.getenv(ENV_VAR_ARK_ANALYZER_DIR) ?: DEFAULT_ARK_ANALYZER_DIR)
if (!arkAnalyzerDir.exists()) {
throw FileNotFoundException("ArkAnalyzer directory does not exist: '$arkAnalyzerDir'. Did you forget to set the '$ENV_VAR_ARK_ANALYZER_DIR' environment variable? Current value is '${System.getenv(ENV_VAR_ARK_ANALYZER_DIR)}', current dir is '${Paths.get("").toAbsolutePath()}'.")
}

val scriptPath = System.getenv(ENV_VAR_SERIALIZE_SCRIPT_PATH) ?: DEFAULT_SERIALIZE_SCRIPT_PATH
val script = arkAnalyzerDir.resolve(scriptPath)
if (!script.exists()) {
throw FileNotFoundException("Script file not found: '$script'. Did you forget to execute 'npm run build' in the arkanalyzer project?")
}

val node = System.getenv(ENV_VAR_NODE_EXECUTABLE) ?: DEFAULT_NODE_EXECUTABLE
val output = kotlin.io.path.createTempFile(prefix = File(tsPath).nameWithoutExtension + "_", suffix = ".json")
val cmd: List<String> = listOf(
node,
script.pathString,
tsPath,
output.pathString,
)
logger.info { "Running: '${cmd.joinToString(" ")}'" }
val process = ProcessBuilder(cmd).start()
val ok = process.waitFor(1, TimeUnit.MINUTES)

val stdout = process.inputStream.bufferedReader().readText().trim()
if (stdout.isNotBlank()) {
logger.info { "STDOUT:\n$stdout" }
}
val stderr = process.errorStream.bufferedReader().readText().trim()
if (stderr.isNotBlank()) {
logger.info { "STDERR:\n$stderr" }
}

if (!ok) {
logger.info { "Timeout!" }
process.destroy()
}

output.inputStream().use { stream ->
val etsFileDto = EtsFileDto.loadFromJson(stream)
return convertToEtsFile(etsFileDto)
}
}
18 changes: 8 additions & 10 deletions jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFileTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import org.jacodb.ets.base.EtsAssignStmt
import org.jacodb.ets.base.EtsInstanceFieldRef
import org.jacodb.ets.base.EtsThis
import org.jacodb.ets.model.EtsFile
import org.jacodb.ets.test.utils.loadEtsFile
import org.jacodb.ets.test.utils.loadEtsFileFromResource
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test

Expand All @@ -29,16 +29,14 @@ private val logger = mu.KotlinLogging.logger {}
class EtsFileTest {

companion object {
private const val BASE_PATH = "/etsir/samples"

private fun loadSample(name: String): EtsFile {
return loadEtsFile("$BASE_PATH/$name.ts.json")
private fun load(name: String): EtsFile {
return loadEtsFileFromResource("/$name.ts.json")
}
}

@Test
fun printEtsInstructions() {
val etsFile = loadSample("classes/SimpleClass")
val etsFile = load("etsir/samples/classes/SimpleClass")
etsFile.classes.forEach { cls ->
cls.methods.forEach { method ->
logger.info {
Expand All @@ -53,7 +51,7 @@ class EtsFileTest {

@Test
fun `test etsFile on TypeMismatch`() {
val etsFile = loadSample("TypeMismatch")
val etsFile = load("etsir/samples/TypeMismatch")
etsFile.classes.forEach { cls ->
cls.methods.forEach { etsMethod ->
when (etsMethod.name) {
Expand All @@ -71,7 +69,7 @@ class EtsFileTest {

@Test
fun `test initializers prepended to class constructor`() {
val etsFile = loadSample("PrependInitializer")
val etsFile = load("etsir/samples/PrependInitializer")
val cls = etsFile.classes.single { it.name == "Foo" }
val ctorBegin = cls.ctor.cfg.instructions.first() as EtsAssignStmt
val fieldRef = ctorBegin.lhv as EtsInstanceFieldRef
Expand All @@ -81,7 +79,7 @@ class EtsFileTest {

@Test
fun `test static field should not be initialized in constructor`() {
val etsFile = loadSample("StaticField")
val etsFile = load("etsir/samples/StaticField")
val cls = etsFile.classes.single { it.name == "Foo" }
Assertions.assertFalse(cls.ctor.cfg.stmts.any {
it is EtsAssignStmt && it.lhv is EtsInstanceFieldRef
Expand All @@ -90,7 +88,7 @@ class EtsFileTest {

@Test
fun `test default constructor should be synthesized`() {
val etsFile = loadSample("DefaultConstructor")
val etsFile = load("etsir/samples/DefaultConstructor")
val cls = etsFile.classes.single { it.name == "Foo" }
val fieldInit = cls.ctor.cfg.instructions.first() as EtsAssignStmt
val fieldRef = fieldInit.lhv as EtsInstanceFieldRef
Expand Down
16 changes: 13 additions & 3 deletions jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ import org.jacodb.ets.dto.convertToEtsFile
import org.jacodb.ets.dto.convertToEtsMethod
import org.jacodb.ets.model.EtsClassSignature
import org.jacodb.ets.model.EtsMethodSignature
import org.jacodb.ets.test.utils.loadDto
import org.jacodb.ets.test.utils.loadEtsFileDtoFromResource
import org.jacodb.ets.utils.loadEtsFileAutoConvert
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test

class EtsFromJsonTest {

companion object {
private const val BASE_PATH = "/etsir/samples"
private const val BASE_PATH = "etsir/samples"
}

private val json = Json {
Expand All @@ -63,12 +64,21 @@ class EtsFromJsonTest {

@Test
fun testLoadEtsFileFromJson() {
val etsDto = loadDto("$BASE_PATH/basic.ts.json")
val etsDto = loadEtsFileDtoFromResource("/$BASE_PATH/basic.ts.json")
println("etsDto = $etsDto")
val ets = convertToEtsFile(etsDto)
println("ets = $ets")
}

@Test
fun testLoadEtsFileAutoConvert() {
val path = "source/example.ts"
val res = this::class.java.getResource("/$path")
?: error("Resource not found: $path")
val etsFile = loadEtsFileAutoConvert(res.path)
println("etsFile = $etsFile")
}

@Test
fun testLoadValueFromJson() {
val jsonString = """
Expand Down
Loading

0 comments on commit 30594f5

Please sign in to comment.