blob: e910eb6ac7977cd034fcb717e3f955e21b83d37f [file] [log] [blame]
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
import com.android.tools.r8.R8RunArtTestsTest.DexTool;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.JarBuilder;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class R8RunExamplesTest {
private static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_BUILD_DIR;
private static final String JAR_EXTENSION = ".jar";
private static final String DEX_EXTENSION = ".dex";
private static final String DEFAULT_DEX_FILENAME = "classes.dex";
private static final Map<String, TestCondition> outputNotIdenticalToJVMOutput =
new ImmutableMap.Builder<String, TestCondition>()
// Traverses stack frames that contain Art specific frames.
.put("throwing.Throwing", TestCondition.any())
// Early art versions incorrectly print Float.MIN_VALUE.
.put(
"filledarray.FilledArray",
TestCondition.match(
TestCondition.runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
.build();
// For local testing on a specific Art version(s) change this set. e.g. to
// ImmutableSet.of("default") or pass the option -Ddex_vm=<version> to the Java VM.
private static Set<DexVm> artVersions = ToolHelper.getArtVersions();
@Parameters(name = "{0}{1}")
public static Collection<String[]> data() {
String[] tests = {
"arithmetic.Arithmetic",
"arrayaccess.ArrayAccess",
"barray.BArray",
"bridge.BridgeMethod",
"cse.CommonSubexpressionElimination",
"constants.Constants",
"controlflow.ControlFlow",
"conversions.Conversions",
"floating_point_annotations.FloatingPointValuedAnnotationTest",
"filledarray.FilledArray",
"hello.Hello",
"ifstatements.IfStatements",
"instancevariable.InstanceVariable",
"instanceofstring.InstanceofString",
"invoke.Invoke",
"jumbostring.JumboString",
"loadconst.LoadConst",
"newarray.NewArray",
"regalloc.RegAlloc",
"returns.Returns",
"staticfield.StaticField",
"stringbuilding.StringBuilding",
"switches.Switches",
"sync.Sync",
"throwing.Throwing",
"trivial.Trivial",
"trycatch.TryCatch",
"nestedtrycatches.NestedTryCatches",
"trycatchmany.TryCatchMany",
"invokeempty.InvokeEmpty",
"regress.Regress",
"regress2.Regress2",
"regress_37726195.Regress",
"regress_37658666.Regress",
"regress_37875803.Regress",
"regress_37955340.Regress",
"regress_62300145.Regress",
"memberrebinding2.Test",
"memberrebinding3.Test",
"minification.Minification",
"enclosingmethod.Main",
"interfaceinlining.Main",
"switchmaps.Switches",
};
List<String[]> fullTestList = new ArrayList<>(tests.length * 2);
for (String test : tests) {
String qualified = test;
String pkg = qualified.substring(0, qualified.lastIndexOf('.'));
fullTestList.add(new String[]{pkg, DEX_EXTENSION, qualified});
fullTestList.add(new String[]{pkg, JAR_EXTENSION, qualified});
}
return fullTestList;
}
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
private final String name;
private final String mainClass;
private final String fileType;
private static Map<DexVm, List<String>> failsOn =
ImmutableMap.of(
DexVm.ART_4_4_4, ImmutableList.of(
"vmdebug.dex",
"vmdebug.jar",
"memberrebinding2.dex", // b/38187737
"memberrebinding2.jar" // b/38187737
),
DexVm.ART_5_1_1, ImmutableList.of(
"vmdebug.dex",
"vmdebug.jar",
"memberrebinding2.dex", // b/38187737
"memberrebinding2.jar" // b/38187737
),
DexVm.ART_6_0_1, ImmutableList.of(
"vmdebug.dex",
"vmdebug.jar",
"memberrebinding2.dex", // b/38187737
"memberrebinding2.jar" // b/38187737
),
DexVm.ART_7_0_0, ImmutableList.of(
"memberrebinding2.dex", // b/38187737
"memberrebinding2.jar" // b/38187737
),
DexVm.ART_DEFAULT, ImmutableList.of(
"memberrebinding2.dex", // b/38187737
"memberrebinding2.jar" // b/38187737
)
);
public R8RunExamplesTest(String name, String fileType, String mainClass) {
this.name = name;
this.fileType = fileType;
this.mainClass = mainClass;
}
private Path getInputFile() {
if (fileType.equals(JAR_EXTENSION)) {
return getOriginalJarFile();
} else {
assert fileType.equals(DEX_EXTENSION);
return getOriginalDexFile();
}
}
public Path getOriginalJarFile() {
return Paths.get(EXAMPLE_DIR, name + JAR_EXTENSION);
}
private Path getOriginalDexFile() {
return Paths.get(EXAMPLE_DIR, name, DEFAULT_DEX_FILENAME);
}
private Path getGeneratedDexFile() throws IOException {
return Paths.get(temp.getRoot().getCanonicalPath(), DEFAULT_DEX_FILENAME);
}
private String getTestName() {
return this.name + this.fileType;
}
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void generateR8Version()
throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
String out = temp.getRoot().getCanonicalPath();
ToolHelper.runR8(getInputFile().toString(), out);
}
@Test
public void outputIsIdentical() throws IOException, InterruptedException, ExecutionException {
if (!ToolHelper.artSupported()) {
return;
}
String original = getOriginalDexFile().toString();
File generated;
// Collect the generated dex files.
File[] outputFiles =
temp.getRoot().listFiles((File file) -> file.getName().endsWith(".dex"));
if (outputFiles.length == 1) {
// Just run Art on classes.dex.
generated = outputFiles[0];
} else {
// Run Art on JAR file with multiple dex files.
generated = temp.getRoot().toPath().resolve(name + ".jar").toFile();
JarBuilder.buildJar(outputFiles, generated);
}
ToolHelper.ProcessResult javaResult =
ToolHelper.runJava(ImmutableList.of(getOriginalJarFile().toString()), mainClass);
if (javaResult.exitCode != 0) {
fail("JVM failed for: " + mainClass);
}
// TODO(ager): Once we have a bot running using dalvik (version 4.4.4) we should remove
// this explicit loop to get rid of repeated testing on the buildbots.
for (DexVm version : artVersions) {
if (failsOn.containsKey(version) && failsOn.get(version).contains(getTestName())) {
thrown.expect(Throwable.class);
}
// Check output against Art output on original dex file.
String output =
ToolHelper.checkArtOutputIdentical(original, generated.toString(), mainClass, version);
// Check output against JVM output.
if (shouldMatchJVMOutput(version)) {
String javaOutput = javaResult.stdout;
assertEquals(
"JVM and Art output differ:\n" + "JVM:\n" + javaOutput + "\nArt:\n" + output,
output,
javaOutput);
}
}
}
private boolean shouldMatchJVMOutput(DexVm version) {
TestCondition condition = outputNotIdenticalToJVMOutput.get(mainClass);
if (condition == null) {
return true;
}
return !condition.test(DexTool.NONE, CompilerUnderTest.R8, version, CompilationMode.RELEASE);
}
}