Skip to content

Commit 5bd668f

Browse files
Update ParparVM tests to run against multiple JDKs and targets
This change updates the CI workflow and the test infrastructure to support running integration tests against multiple JDK versions (8, 11, 17, 21, and 25). Changes: - Updated `.github/workflows/parparvm-tests.yml` to install JDKs 8, 11, 17, 21, and 25 and export their paths as environment variables. The main test execution remains on JDK 8. - Created `CompilerHelper` in `vm/tests` to detect available JDKs from environment variables and manage compilation using `ProcessBuilder` instead of the in-process Compiler API. This avoids classpath pollution and supports newer JDKs. - Updated `BytecodeInstructionIntegrationTest` and `CleanTargetIntegrationTest` to use `CompilerHelper` and run parameterized tests across valid permutations of available Compilers and Target Versions (1.5, 1.8, 11, 17, 21, 25). - Added logic to handle `javac` flag changes on JDK 9+ (replacing `-bootclasspath` with `-classpath` or `--patch-module` as appropriate) and to skip invalid permutations. - Updated `vm/pom.xml` to use ASM 9.7 to support newer Java class file versions.
1 parent 1367324 commit 5bd668f

File tree

4 files changed

+301
-138
lines changed

4 files changed

+301
-138
lines changed

.github/workflows/parparvm-tests.yml

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,56 +27,64 @@ jobs:
2727
sudo apt-get update
2828
sudo apt-get install -y clang
2929
30+
# Install JDKs and export their paths
31+
- name: Set up JDK 8
32+
uses: actions/setup-java@v4
33+
with:
34+
distribution: 'temurin'
35+
java-version: '8'
36+
- name: Save JDK 8 Path
37+
run: echo "JDK_8_HOME=$JAVA_HOME" >> $GITHUB_ENV
38+
3039
- name: Set up JDK 11
3140
uses: actions/setup-java@v4
3241
with:
3342
distribution: 'temurin'
3443
java-version: '11'
35-
cache: 'maven'
36-
37-
- name: Run ParparVM JVM tests on JDK 11
38-
working-directory: vm
39-
run: mvn -B clean test -pl tests -am -Djacoco.skip=true
44+
- name: Save JDK 11 Path
45+
run: echo "JDK_11_HOME=$JAVA_HOME" >> $GITHUB_ENV
4046

4147
- name: Set up JDK 17
4248
uses: actions/setup-java@v4
4349
with:
4450
distribution: 'temurin'
4551
java-version: '17'
46-
47-
- name: Run ParparVM JVM tests on JDK 17
48-
working-directory: vm
49-
run: mvn -B clean test -pl tests -am -Djacoco.skip=true
52+
- name: Save JDK 17 Path
53+
run: echo "JDK_17_HOME=$JAVA_HOME" >> $GITHUB_ENV
5054

5155
- name: Set up JDK 21
5256
uses: actions/setup-java@v4
5357
with:
5458
distribution: 'temurin'
5559
java-version: '21'
56-
57-
- name: Run ParparVM JVM tests on JDK 21
58-
working-directory: vm
59-
run: mvn -B clean test -pl tests -am -Djacoco.skip=true
60+
- name: Save JDK 21 Path
61+
run: echo "JDK_21_HOME=$JAVA_HOME" >> $GITHUB_ENV
6062

6163
- name: Set up JDK 25
6264
uses: actions/setup-java@v4
6365
with:
6466
distribution: 'zulu'
65-
java-version: '25-ea'
67+
java-version: '25'
68+
- name: Save JDK 25 Path
69+
run: echo "JDK_25_HOME=$JAVA_HOME" >> $GITHUB_ENV
6670

67-
- name: Run ParparVM JVM tests on JDK 25
68-
working-directory: vm
69-
run: mvn -B clean test -pl tests -am -Djacoco.skip=true
70-
71-
- name: Set up JDK 8
71+
# Restore JDK 8 as the main runner
72+
- name: Restore JDK 8
7273
uses: actions/setup-java@v4
7374
with:
7475
distribution: 'temurin'
7576
java-version: '8'
77+
cache: 'maven'
7678

77-
- name: Run ParparVM JVM tests on JDK 8
79+
- name: Run ParparVM JVM tests
7880
working-directory: vm
79-
run: mvn -B clean test
81+
run: mvn -B clean test -pl tests -am
82+
env:
83+
JDK_8_HOME: ${{ env.JDK_8_HOME }}
84+
JDK_11_HOME: ${{ env.JDK_11_HOME }}
85+
JDK_17_HOME: ${{ env.JDK_17_HOME }}
86+
JDK_21_HOME: ${{ env.JDK_21_HOME }}
87+
JDK_25_HOME: ${{ env.JDK_25_HOME }}
8088

8189
- name: Publish ByteCodeTranslator quality previews
8290
if: ${{ always() && github.server_url == 'https://github.com' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) }}

vm/tests/src/test/java/com/codename1/tools/translator/BytecodeInstructionIntegrationTest.java

Lines changed: 91 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,20 @@
4141

4242
class BytecodeInstructionIntegrationTest {
4343

44+
static Stream<CompilerHelper.CompilerConfig> provideCompilerConfigs() {
45+
List<CompilerHelper.CompilerConfig> configs = new ArrayList<>();
46+
configs.addAll(CompilerHelper.getAvailableCompilers("1.5"));
47+
configs.addAll(CompilerHelper.getAvailableCompilers("1.8"));
48+
configs.addAll(CompilerHelper.getAvailableCompilers("11"));
49+
configs.addAll(CompilerHelper.getAvailableCompilers("17"));
50+
configs.addAll(CompilerHelper.getAvailableCompilers("21"));
51+
configs.addAll(CompilerHelper.getAvailableCompilers("25"));
52+
return configs.stream();
53+
}
54+
4455
@ParameterizedTest
45-
@ValueSource(strings = {"1.5", "1.8"})
46-
void translatesOptimizedBytecodeToLLVMExecutable(String targetVersion) throws Exception {
47-
if ("1.5".equals(targetVersion) && !isSourceVersionSupported("1.5")) {
48-
return; // Skip on newer JDKs that dropped 1.5 support
49-
}
56+
@org.junit.jupiter.params.provider.MethodSource("provideCompilerConfigs")
57+
void translatesOptimizedBytecodeToLLVMExecutable(CompilerHelper.CompilerConfig config) throws Exception {
5058
Parser.cleanup();
5159

5260
Path sourceDir = Files.createTempDirectory("bytecode-integration-sources");
@@ -61,28 +69,43 @@ void translatesOptimizedBytecodeToLLVMExecutable(String targetVersion) throws Ex
6169
Path nativeReport = sourceDir.resolve("native_report.c");
6270
Files.write(nativeReport, nativeReportSource().getBytes(StandardCharsets.UTF_8));
6371

64-
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
65-
assertNotNull(compiler, "A JDK is required to compile test sources");
66-
67-
// Compile App using JavaAPI as bootclasspath
72+
// Compile App using the specific JDK
6873
List<String> compileArgs = new ArrayList<>();
69-
compileArgs.add("-source");
70-
compileArgs.add(targetVersion);
71-
compileArgs.add("-target");
72-
compileArgs.add(targetVersion);
73-
compileArgs.add("-bootclasspath");
74-
compileArgs.add(javaApiDir.toString());
74+
75+
double targetVer = 1.8;
76+
try { targetVer = Double.parseDouble(config.targetVersion); } catch (NumberFormatException ignored) {}
77+
78+
double jdkVer = 1.8;
79+
try { jdkVer = Double.parseDouble(config.jdkVersion); } catch (NumberFormatException ignored) {}
80+
81+
if (jdkVer >= 9) {
82+
compileArgs.add("-source");
83+
compileArgs.add(config.targetVersion);
84+
compileArgs.add("-target");
85+
compileArgs.add(config.targetVersion);
86+
// On JDK 9+, -bootclasspath is removed.
87+
// --patch-module is not allowed with -target 8.
88+
// We rely on the JDK's own bootstrap classes but include our JavaAPI in classpath
89+
// so that any non-replaced classes are found.
90+
// This means we compile against JDK 9+ API but emit older bytecode.
91+
compileArgs.add("-classpath");
92+
compileArgs.add(javaApiDir.toString());
93+
} else {
94+
compileArgs.add("-source");
95+
compileArgs.add(config.targetVersion);
96+
compileArgs.add("-target");
97+
compileArgs.add(config.targetVersion);
98+
compileArgs.add("-bootclasspath");
99+
compileArgs.add(javaApiDir.toString());
100+
compileArgs.add("-Xlint:-options");
101+
}
102+
75103
compileArgs.add("-d");
76104
compileArgs.add(classesDir.toString());
77105
compileArgs.add(sourceDir.resolve("BytecodeInstructionApp.java").toString());
78106

79-
int compileResult = compiler.run(
80-
null,
81-
null,
82-
null,
83-
compileArgs.toArray(new String[0])
84-
);
85-
assertEquals(0, compileResult, "BytecodeInstructionApp should compile");
107+
int compileResult = CompilerHelper.compile(config.jdkHome, compileArgs);
108+
assertEquals(0, compileResult, "BytecodeInstructionApp should compile with " + config);
86109

87110
Files.copy(nativeReport, classesDir.resolve("native_report.c"));
88111

@@ -190,11 +213,8 @@ private String invokeLdcLocalVarsAppSource() {
190213
}
191214

192215
@ParameterizedTest
193-
@ValueSource(strings = {"1.5", "1.8"})
194-
void translatesInvokeAndLdcBytecodeToLLVMExecutable(String targetVersion) throws Exception {
195-
if ("1.5".equals(targetVersion) && !isSourceVersionSupported("1.5")) {
196-
return; // Skip on newer JDKs that dropped 1.5 support
197-
}
216+
@org.junit.jupiter.params.provider.MethodSource("provideCompilerConfigs")
217+
void translatesInvokeAndLdcBytecodeToLLVMExecutable(CompilerHelper.CompilerConfig config) throws Exception {
198218
Parser.cleanup();
199219

200220
Path sourceDir = Files.createTempDirectory("invoke-ldc-sources");
@@ -209,27 +229,37 @@ void translatesInvokeAndLdcBytecodeToLLVMExecutable(String targetVersion) throws
209229
Path nativeReport = sourceDir.resolve("native_report.c");
210230
Files.write(nativeReport, nativeReportSource().getBytes(StandardCharsets.UTF_8));
211231

212-
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
213-
assertNotNull(compiler, "A JDK is required to compile test sources");
214-
215232
List<String> compileArgs = new ArrayList<>();
216-
compileArgs.add("-source");
217-
compileArgs.add(targetVersion);
218-
compileArgs.add("-target");
219-
compileArgs.add(targetVersion);
220-
compileArgs.add("-bootclasspath");
221-
compileArgs.add(javaApiDir.toString());
233+
234+
double targetVer = 1.8;
235+
try { targetVer = Double.parseDouble(config.targetVersion); } catch (NumberFormatException ignored) {}
236+
237+
double jdkVer = 1.8;
238+
try { jdkVer = Double.parseDouble(config.jdkVersion); } catch (NumberFormatException ignored) {}
239+
240+
if (jdkVer >= 9) {
241+
compileArgs.add("-source");
242+
compileArgs.add(config.targetVersion);
243+
compileArgs.add("-target");
244+
compileArgs.add(config.targetVersion);
245+
compileArgs.add("-classpath");
246+
compileArgs.add(javaApiDir.toString());
247+
} else {
248+
compileArgs.add("-source");
249+
compileArgs.add(config.targetVersion);
250+
compileArgs.add("-target");
251+
compileArgs.add(config.targetVersion);
252+
compileArgs.add("-bootclasspath");
253+
compileArgs.add(javaApiDir.toString());
254+
compileArgs.add("-Xlint:-options");
255+
}
256+
222257
compileArgs.add("-d");
223258
compileArgs.add(classesDir.toString());
224259
compileArgs.add(sourceDir.resolve("InvokeLdcLocalVarsApp.java").toString());
225260

226-
int compileResult = compiler.run(
227-
null,
228-
null,
229-
null,
230-
compileArgs.toArray(new String[0])
231-
);
232-
assertEquals(0, compileResult, "InvokeLdcLocalVarsApp should compile");
261+
int compileResult = CompilerHelper.compile(config.jdkHome, compileArgs);
262+
assertEquals(0, compileResult, "InvokeLdcLocalVarsApp should compile with " + config);
233263

234264
Files.copy(nativeReport, classesDir.resolve("native_report.c"));
235265

@@ -985,18 +1015,15 @@ private String nativeReportSource() {
9851015
}
9861016

9871017
@ParameterizedTest
988-
@ValueSource(strings = {"1.5", "1.8"})
989-
void handleDefaultOutputWritesOutput(String targetVersion) throws Exception {
990-
if ("1.5".equals(targetVersion) && !isSourceVersionSupported("1.5")) {
991-
return; // Skip on newer JDKs that dropped 1.5 support
992-
}
1018+
@org.junit.jupiter.params.provider.MethodSource("provideCompilerConfigs")
1019+
void handleDefaultOutputWritesOutput(CompilerHelper.CompilerConfig config) throws Exception {
9931020
Parser.cleanup();
9941021
resetByteCodeClass();
9951022
Path sourceDir = Files.createTempDirectory("default-output-source");
9961023
Path outputDir = Files.createTempDirectory("default-output-dest");
9971024

9981025
Files.write(sourceDir.resolve("resource.txt"), "data".getBytes(StandardCharsets.UTF_8));
999-
compileDummyMainClass(sourceDir, "com.example", "MyAppDefault", targetVersion);
1026+
compileDummyMainClass(sourceDir, "com.example", "MyAppDefault", config);
10001027

10011028
String[] args = new String[] {
10021029
"csharp",
@@ -1076,18 +1103,15 @@ void copyDirRecursivelyCopies() throws Exception {
10761103
}
10771104

10781105
@ParameterizedTest
1079-
@ValueSource(strings = {"1.5", "1.8"})
1080-
void handleIosOutputGeneratesProjectStructure(String targetVersion) throws Exception {
1081-
if ("1.5".equals(targetVersion) && !isSourceVersionSupported("1.5")) {
1082-
return; // Skip on newer JDKs that dropped 1.5 support
1083-
}
1106+
@org.junit.jupiter.params.provider.MethodSource("provideCompilerConfigs")
1107+
void handleIosOutputGeneratesProjectStructure(CompilerHelper.CompilerConfig config) throws Exception {
10841108
Parser.cleanup();
10851109
resetByteCodeClass();
10861110
Path sourceDir = Files.createTempDirectory("ios-output-source");
10871111
Path outputDir = Files.createTempDirectory("ios-output-dest");
10881112

10891113
Files.write(sourceDir.resolve("resource.txt"), "data".getBytes(StandardCharsets.UTF_8));
1090-
compileDummyMainClass(sourceDir, "com.example", "MyAppIOS", targetVersion);
1114+
compileDummyMainClass(sourceDir, "com.example", "MyAppIOS", config);
10911115

10921116
// Add a bundle to test copyDir invocation in execute loop
10931117
Path bundleDir = sourceDir.resolve("test.bundle");
@@ -1139,7 +1163,7 @@ private void resetByteCodeClass() throws Exception {
11391163
((Set<?>) writableFieldsField.get(null)).clear();
11401164
}
11411165

1142-
private void compileDummyMainClass(Path sourceDir, String packageName, String className, String targetVersion) throws Exception {
1166+
private void compileDummyMainClass(Path sourceDir, String packageName, String className, CompilerHelper.CompilerConfig config) throws Exception {
11431167
Path packageDir = sourceDir;
11441168
for (String part : packageName.split("\\.")) {
11451169
packageDir = packageDir.resolve(part);
@@ -1152,13 +1176,17 @@ private void compileDummyMainClass(Path sourceDir, String packageName, String cl
11521176
"}\n";
11531177
Files.write(javaFile, content.getBytes(StandardCharsets.UTF_8));
11541178

1155-
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
1156-
int result = compiler.run(null, null, null,
1157-
"-source", targetVersion,
1158-
"-target", targetVersion,
1159-
"-d", sourceDir.toString(),
1160-
javaFile.toString());
1161-
assertEquals(0, result, "Compilation failed");
1179+
List<String> args = new ArrayList<>();
1180+
args.add("-source");
1181+
args.add(config.targetVersion);
1182+
args.add("-target");
1183+
args.add(config.targetVersion);
1184+
args.add("-d");
1185+
args.add(sourceDir.toString());
1186+
args.add(javaFile.toString());
1187+
1188+
int result = CompilerHelper.compile(config.jdkHome, args);
1189+
assertEquals(0, result, "Compilation failed with " + config);
11621190
}
11631191

11641192
@Test
@@ -1262,19 +1290,4 @@ void testArithmeticExpressionCoverage() {
12621290
// or mock if possible. But here we can check basic behavior.
12631291
}
12641292

1265-
private boolean isSourceVersionSupported(String version) {
1266-
String javaVersion = System.getProperty("java.specification.version");
1267-
if (javaVersion.startsWith("1.")) {
1268-
return true;
1269-
}
1270-
try {
1271-
int major = Integer.parseInt(javaVersion);
1272-
if ("1.5".equals(version)) {
1273-
if (major >= 9) return false;
1274-
}
1275-
} catch (NumberFormatException e) {
1276-
return true;
1277-
}
1278-
return true;
1279-
}
12801293
}

0 commit comments

Comments
 (0)