Run JCTF tests with a single compilation on multiple vms.
Also:
- Don't test the CF -> R8 -> DEX configuration.
- Build with --release for the R8 pass in the R8_AFTER_D8 config.
Bug: 126683699, 128603074
Change-Id: I1afa4eb8d6bfc216c3da84a0fe1f15b5560176bd
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 60dc9ae..5a71add 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -9,13 +9,15 @@
import com.android.tools.r8.JctfTestSpecifications.Outcome;
import com.android.tools.r8.TestCondition.Runtime;
import com.android.tools.r8.TestCondition.RuntimeSet;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.DexVm.Kind;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ArtErrorParser;
import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
@@ -46,6 +48,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.BiFunction;
@@ -1526,19 +1529,53 @@
runArtTest(ToolHelper.getDexVm(), compilerUnderTest);
}
+ private static class CompilationOptions {
+ private final boolean disableInlining;
+ private final boolean disableClassInlining;
+ private final boolean disableUninstantiatedTypeOptimization;
+ private final boolean hasMissingClasses;
+
+ private CompilationOptions(TestSpecification spec) {
+ this.disableInlining = spec.disableInlining;
+ this.disableClassInlining = spec.disableClassInlining;
+ this.disableUninstantiatedTypeOptimization = spec.disableUninstantiatedTypeOptimization;
+ this.hasMissingClasses = spec.hasMissingClasses;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ CompilationOptions options = (CompilationOptions) o;
+ return disableInlining == options.disableInlining
+ && disableClassInlining == options.disableClassInlining
+ && disableUninstantiatedTypeOptimization == options.disableUninstantiatedTypeOptimization
+ && hasMissingClasses == options.hasMissingClasses;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ disableInlining,
+ disableClassInlining,
+ disableUninstantiatedTypeOptimization,
+ hasMissingClasses);
+ }
+ }
+
private void executeCompilerUnderTest(
CompilerUnderTest compilerUnderTest,
Collection<String> fileNames,
String resultPath,
CompilationMode compilationMode,
- boolean disableInlining,
- boolean disableClassInlining,
- boolean disableUninstantiatedTypeOptimization,
- boolean hasMissingClasses)
+ CompilationOptions compilationOptions)
throws CompilationFailedException {
- executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, compilationMode, null,
- disableInlining, disableClassInlining, disableUninstantiatedTypeOptimization,
- hasMissingClasses);
+ executeCompilerUnderTest(
+ compilerUnderTest, fileNames, resultPath, compilationMode, null, compilationOptions);
}
private void executeCompilerUnderTest(
@@ -1547,10 +1584,7 @@
String resultPath,
CompilationMode mode,
String keepRulesFile,
- boolean disableInlining,
- boolean disableClassInlining,
- boolean disableUninstantiatedTypeOptimization,
- boolean hasMissingClasses)
+ CompilationOptions compilationOptions)
throws CompilationFailedException {
assert mode != null;
switch (compilerUnderTest) {
@@ -1698,20 +1732,20 @@
ToolHelper.runR8(
builder.build(),
options -> {
- if (disableInlining) {
+ if (compilationOptions.disableInlining) {
options.enableInlining = false;
}
- if (disableClassInlining) {
+ if (compilationOptions.disableClassInlining) {
options.enableClassInlining = false;
}
- if (disableUninstantiatedTypeOptimization) {
+ if (compilationOptions.disableUninstantiatedTypeOptimization) {
options.enableUninstantiatedTypeOptimization = false;
}
// Make sure we don't depend on this settings.
options.classInliningInstructionLimit = 10000;
options.lineNumberOptimization = LineNumberOptimization.OFF;
// Some tests actually rely on missing classes for what they test.
- options.ignoreMissingClasses = hasMissingClasses;
+ options.ignoreMissingClasses = compilationOptions.hasMissingClasses;
});
break;
}
@@ -1794,15 +1828,38 @@
noInlining);
}
- protected void runJctfTest(CompilerUnderTest compilerUnderTest, String classFilePath,
- String fullClassName)
- throws IOException, ProguardRuleParserException, ExecutionException,
- CompilationFailedException {
- Runtime runtime;
- if (compilerUnderTest == CompilerUnderTest.R8CF) {
- runtime = Runtime.JAVA;
+ private static Runtime getRuntime(TestRuntime vm) {
+ if (vm.isCf()) {
+ return Runtime.JAVA;
+ } else if (vm.isDex()) {
+ return Runtime.fromDexVmVersion(vm.asDex().getVm().getVersion());
} else {
- runtime = Runtime.fromDexVmVersion(ToolHelper.getDexVm().getVersion());
+ throw new Unreachable();
+ }
+ }
+
+ private static class VmSpec {
+ final TestRuntime vm;
+ final TestSpecification spec;
+
+ private VmSpec(TestRuntime vm, TestSpecification testSpecification) {
+ this.vm = vm;
+ this.spec = testSpecification;
+ }
+ }
+
+ protected void runJctfTest(
+ CompilerUnderTest compilerUnderTest, String classFilePath, String fullClassName)
+ throws IOException, CompilationFailedException {
+ List<TestRuntime> vms = new ArrayList<>();
+ if (compilerUnderTest == CompilerUnderTest.R8CF) {
+ for (CfVm vm : TestParametersBuilder.getAvailableCfVms()) {
+ vms.add(new TestRuntime.CfRuntime(vm));
+ }
+ } else {
+ for (DexVm vm : TestParametersBuilder.getAvailableDexVms()) {
+ vms.add(new DexRuntime(vm));
+ }
}
CompilerUnderTest firstCompilerUnderTest =
@@ -1811,19 +1868,28 @@
: compilerUnderTest;
CompilationMode compilationMode = defaultCompilationMode(compilerUnderTest);
- File resultDir = temp.newFolder(firstCompilerUnderTest.toString().toLowerCase() + "-output");
+ List<VmSpec> vmSpecs = new ArrayList<>();
+ for (TestRuntime vm : vms) {
+ File resultDir =
+ temp.newFolder(
+ firstCompilerUnderTest.toString().toLowerCase() + "-output-" + vm.toString());
- TestSpecification specification =
- JctfTestSpecifications.getExpectedOutcome(
- name,
- firstCompilerUnderTest,
- runtime,
- compilationMode,
- compilerUnderTest == CompilerUnderTest.R8CF
- ? jctfOutcomeToSpecificationJava(name, resultDir)
- : jctfOutcomeToSpecification(name, DexTool.NONE, resultDir, ToolHelper.getDexVm()));
+ TestSpecification specification =
+ JctfTestSpecifications.getExpectedOutcome(
+ name,
+ firstCompilerUnderTest,
+ getRuntime(vm),
+ compilationMode,
+ compilerUnderTest == CompilerUnderTest.R8CF
+ ? jctfOutcomeToSpecificationJava(name, resultDir)
+ : jctfOutcomeToSpecification(name, DexTool.NONE, resultDir, vm.asDex().getVm()));
- if (specification.skipTest) {
+ if (!specification.skipTest) {
+ vmSpecs.add(new VmSpec(vm, specification));
+ }
+ }
+
+ if (vmSpecs.isEmpty()) {
return;
}
@@ -1887,62 +1953,82 @@
}
if (compilerUnderTest == CompilerUnderTest.R8CF) {
- runJctfTestDoRunOnJava(fileNames, specification, fullClassName, compilationMode, resultDir);
- } else {
-
- runJctfTestDoRunOnArt(
- fileNames,
- specification,
- firstCompilerUnderTest,
- fullClassName,
- compilationMode,
- ToolHelper.getDexVm(),
- resultDir);
+ assert vmSpecs.size() == 1
+ : "Running the same test on multiple JVMs should share the same build.";
+ for (VmSpec vmSpec : vmSpecs) {
+ runJctfTestDoRunOnJava(
+ fileNames, vmSpec.spec, fullClassName, compilationMode, vmSpec.vm.asCf().getVm());
+ }
+ return;
}
- // second pass if D8_R8Debug
- if (compilerUnderTest == CompilerUnderTest.R8_AFTER_D8) {
- List<String> d8OutputFileNames =
- Files.list(resultDir.toPath())
- .filter(FileUtils::isDexFile)
- .map(Path::toString)
- .collect(Collectors.toList());
- File r8ResultDir = temp.newFolder("r8-output");
- compilationMode = CompilationMode.DEBUG;
- specification =
+ CompilationOptions compilationOptions = null;
+ File compiledDir = temp.newFolder();
+ for (VmSpec vmSpec : vmSpecs) {
+ CompilationOptions thisOptions = new CompilationOptions(vmSpec.spec);
+ if (compilationOptions == null) {
+ compilationOptions = thisOptions;
+ executeCompilerUnderTest(
+ firstCompilerUnderTest,
+ fileNames,
+ compiledDir.getAbsolutePath(),
+ compilationMode,
+ compilationOptions);
+ } else {
+ // For now compile options don't change across vms.
+ assert compilationOptions.equals(thisOptions);
+ }
+ Files.copy(
+ compiledDir.toPath().resolve("classes.dex"),
+ vmSpec.spec.directory.toPath().resolve("classes.dex"));
+ runJctfTestDoRunOnArt(fileNames, vmSpec.spec, fullClassName, vmSpec.vm.asDex().getVm());
+ }
+
+ if (compilerUnderTest != CompilerUnderTest.R8_AFTER_D8) {
+ return;
+ }
+
+ // Second pass (R8), if R8_AFTER_D8.
+ CompilationOptions r8CompilationOptions = null;
+ File r8CompiledDir = temp.newFolder();
+ for (VmSpec vmSpec : vmSpecs) {
+ File r8ResultDir = temp.newFolder("r8-output-" + vmSpec.vm.toString());
+ TestSpecification specification =
JctfTestSpecifications.getExpectedOutcome(
name,
CompilerUnderTest.R8_AFTER_D8,
- runtime,
- compilationMode,
- jctfOutcomeToSpecification(name, DexTool.DX, r8ResultDir, ToolHelper.getDexVm()));
+ getRuntime(vmSpec.vm),
+ CompilationMode.RELEASE,
+ jctfOutcomeToSpecification(name, DexTool.DX, r8ResultDir, vmSpec.vm.asDex().getVm()));
if (specification.skipTest) {
- return;
+ continue;
}
- runJctfTestDoRunOnArt(
- d8OutputFileNames,
- specification,
- CompilerUnderTest.R8,
- fullClassName,
- compilationMode,
- ToolHelper.getDexVm(),
- r8ResultDir);
+ CompilationOptions thisOptions = new CompilationOptions(vmSpec.spec);
+ if (r8CompilationOptions == null) {
+ r8CompilationOptions = thisOptions;
+ executeCompilerUnderTest(
+ CompilerUnderTest.R8,
+ Collections.singletonList(compiledDir.toPath().resolve("classes.dex").toString()),
+ r8CompiledDir.getAbsolutePath(),
+ CompilationMode.RELEASE,
+ r8CompilationOptions);
+ } else {
+ // For now compile options don't change across vms.
+ assert r8CompilationOptions.equals(thisOptions);
+ }
+ Files.copy(
+ r8CompiledDir.toPath().resolve("classes.dex"),
+ specification.directory.toPath().resolve("classes.dex"));
+ runJctfTestDoRunOnArt(fileNames, specification, fullClassName, vmSpec.vm.asDex().getVm());
}
}
private void runJctfTestDoRunOnArt(
Collection<String> fileNames,
TestSpecification specification,
- CompilerUnderTest compilerUnderTest,
String fullClassName,
- CompilationMode mode,
- DexVm dexVm,
- File resultDir)
- throws IOException, CompilationFailedException {
- executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getAbsolutePath(), mode,
- specification.disableInlining, specification.disableClassInlining,
- specification.disableUninstantiatedTypeOptimization, specification.hasMissingClasses);
-
+ DexVm dexVm)
+ throws IOException {
if (!ToolHelper.artSupported() && !ToolHelper.dealsWithGoldenFiles()) {
return;
}
@@ -1951,16 +2037,9 @@
// Collect the generated dex files.
File[] outputFiles =
- resultDir.listFiles((File file) -> file.getName().endsWith(".dex"));
- if (outputFiles.length == 1) {
- // Just run Art on classes.dex.
- processedFile = outputFiles[0];
- } else {
- // Run Art on JAR file with multiple dex files.
- processedFile
- = temp.getRoot().toPath().resolve(specification.name + ".jar").toFile();
- buildJar(outputFiles, processedFile);
- }
+ specification.directory.listFiles((File file) -> file.getName().endsWith(".dex"));
+ assert outputFiles.length == 1;
+ processedFile = outputFiles[0];
boolean compileOnly = System.getProperty("jctf_compile_only", "0").equals("1");
if (compileOnly || specification.skipRun) {
@@ -1976,7 +2055,7 @@
if (dexVm.isNewerThan(DexVm.ART_4_4_4_HOST)) {
builder.appendArtOption("-Ximage:/system/non/existent/image.art");
}
- for (String s : ToolHelper.getBootLibs()) {
+ for (String s : ToolHelper.getBootLibs(dexVm)) {
builder.appendBootClassPath(new File(s).getCanonicalPath());
}
builder.setMainClass(JUNIT_TEST_RUNNER);
@@ -2003,20 +2082,18 @@
TestSpecification specification,
String fullClassName,
CompilationMode mode,
- File resultDir)
+ CfVm vm)
throws IOException, CompilationFailedException {
+ assert TestParametersBuilder.isSystemJdk(vm);
if (JctfTestSpecifications.compilationFailsWithAsmMethodTooLarge.contains(specification.name)) {
expectException(org.objectweb.asm.MethodTooLargeException.class);
}
executeCompilerUnderTest(
CompilerUnderTest.R8CF,
fileNames,
- resultDir.getAbsolutePath(),
+ specification.directory.getAbsolutePath(),
mode,
- specification.disableInlining,
- specification.disableClassInlining,
- specification.disableUninstantiatedTypeOptimization,
- specification.hasMissingClasses);
+ new CompilationOptions(specification));
boolean compileOnly = System.getProperty("jctf_compile_only", "0").equals("1");
@@ -2033,7 +2110,7 @@
// running the test.
ProcessResult result =
ToolHelper.runJava(
- resultDir.toPath(),
+ specification.directory.toPath(),
"-Xmx" + ToolHelper.BOT_MAX_HEAP_SIZE,
JUNIT_TEST_RUNNER,
fullClassName);
@@ -2168,9 +2245,11 @@
expectException(CompilationError.class);
try {
executeCompilerUnderTest(
- compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode,
- specification.disableInlining, specification.disableClassInlining,
- specification.disableUninstantiatedTypeOptimization, specification.hasMissingClasses);
+ compilerUnderTest,
+ fileNames,
+ resultDir.getCanonicalPath(),
+ compilationMode,
+ new CompilationOptions(specification));
} catch (CompilationFailedException e) {
throw new CompilationError(e.getMessage(), e);
}
@@ -2179,16 +2258,20 @@
} else if (specification.failsWithX8) {
expectException(Throwable.class);
executeCompilerUnderTest(
- compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode,
- specification.disableInlining, specification.disableClassInlining,
- specification.disableUninstantiatedTypeOptimization, specification.hasMissingClasses);
+ compilerUnderTest,
+ fileNames,
+ resultDir.getCanonicalPath(),
+ compilationMode,
+ new CompilationOptions(specification));
System.err.println("Should have failed R8/D8 compilation with an exception.");
return;
} else {
executeCompilerUnderTest(
- compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode,
- specification.disableInlining, specification.disableClassInlining,
- specification.disableUninstantiatedTypeOptimization, specification.hasMissingClasses);
+ compilerUnderTest,
+ fileNames,
+ resultDir.getCanonicalPath(),
+ compilationMode,
+ new CompilationOptions(specification));
}
if (!specification.skipRun
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 8059f21..4cefe0c 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -158,7 +158,7 @@
return isSystemJdk(vm);
}
- private static List<CfVm> getAvailableCfVms() {
+ public static List<CfVm> getAvailableCfVms() {
String cfVmsProperty = System.getProperty("cf_vms");
if (cfVmsProperty != null) {
return Arrays.stream(cfVmsProperty.split(":"))
@@ -174,7 +174,7 @@
}
}
- private static List<DexVm> getAvailableDexVms() {
+ public static List<DexVm> getAvailableDexVms() {
String dexVmsProperty = System.getProperty("dex_vms");
if (dexVmsProperty != null) {
return Arrays.stream(dexVmsProperty.split(":"))
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 7467bfa..1ab645d 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -704,10 +704,10 @@
}
}
- public static List<String> getBootLibs() {
- String prefix = getArtDir(getDexVm()) + "/";
+ public static List<String> getBootLibs(DexVm dexVm) {
+ String prefix = getArtDir(dexVm) + "/";
List<String> result = new ArrayList<>();
- BOOT_LIBS.get(getDexVm()).stream().forEach(x -> result.add(prefix + "framework/" + x));
+ BOOT_LIBS.get(dexVm).stream().forEach(x -> result.add(prefix + "framework/" + x));
return result;
}
diff --git a/tools/create_jctf_tests.py b/tools/create_jctf_tests.py
index 28dde99..bc065d9 100755
--- a/tools/create_jctf_tests.py
+++ b/tools/create_jctf_tests.py
@@ -129,7 +129,6 @@
relative_package = package[idx + len(dot_java_dot):]
generate_test(class_name, 'd8', 'R8_AFTER_D8', relative_package)
- generate_test(class_name, 'r8', 'R8', relative_package)
generate_test(class_name, 'r8cf', 'R8CF', relative_package)
diff --git a/tools/test.py b/tools/test.py
index 899337a..6f664eb 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -257,12 +257,13 @@
# Now run tests on selected runtime(s).
vms_to_test = [options.dex_vm] if options.dex_vm != "all" else ALL_ART_VMS
- # TODO(126683699): remove once we have single run
- if options.dex_vm == 'all' and options.only_jctf:
- vms_to_test = ["default", "5.1.1"]
# The full set of VMs is configured in the first run, then set to empty below.
dex_vms_property = ':'.join(vms_to_test)
+
+ if options.only_jctf:
+ vms_to_test = ['default']
+
for art_vm in vms_to_test:
vm_suffix = "_" + options.dex_vm_kind if art_vm != "default" else ""
return_code = gradle.RunGradle(