blob: a30d0bdf0ccf11448432997c8866e69f33a8a604 [file] [log] [blame]
// Copyright (c) 2018, 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.cf;
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static com.google.common.io.ByteStreams.toByteArray;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.ArchiveClassFileProvider;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ExternalR8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.Pair;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* This test relies on a freshly built build/libs/r8lib_with_deps.jar. If this test fails remove
* build directory and rebuild r8lib_with_deps by calling test.py or gradle r8libWithdeps.
*/
@RunWith(Parameterized.class)
public class BootstrapCurrentEqualityTest extends TestBase {
private static final Path MAIN_KEEP = Paths.get("src/main/keep.txt");
private static final String HELLO_NAME = "hello.Hello";
private static final String[] KEEP_HELLO = {
"-keep class " + HELLO_NAME + " {", " public static void main(...);", "}",
};
private static Pair<Path, Path> r8R8Debug;
private static Pair<Path, Path> r8R8Release;
private static boolean testExternal = true;
@ClassRule public static TemporaryFolder testFolder = new TemporaryFolder();
@BeforeClass
public static void beforeAll() throws Exception {
if (data().stream().count() > 0) {
r8R8Debug = compileR8(CompilationMode.DEBUG);
r8R8Release = compileR8(CompilationMode.RELEASE);
}
}
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withCfRuntimes().build();
}
public BootstrapCurrentEqualityTest(TestParameters parameters) {
// TODO: use parameters to run on the right java.
}
private static Pair<Path, Path> compileR8(CompilationMode mode) throws Exception {
// Run R8 on r8.jar.
final Path jar = testFolder.newFolder().toPath().resolve("out.jar");
final Path map = testFolder.newFolder().toPath().resolve("out.map");
if (testExternal) {
testForExternalR8(newTempFolder(), Backend.CF)
.useR8WithRelocatedDeps()
.setMode(mode)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
.compile()
.apply(c -> FileUtils.writeTextFile(map, c.getProguardMap()))
.writeToZip(jar);
} else {
testForR8(newTempFolder(), Backend.CF)
.setMode(mode)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
.compile()
.apply(c -> FileUtils.writeTextFile(map, c.getProguardMap()))
.writeToZip(jar);
}
return new Pair<>(jar, map);
}
@Test
public void testRetrace() throws IOException {
ProcessResult result =
ToolHelper.runProcess(
new ProcessBuilder(
"python",
Paths.get(ToolHelper.TOOLS_DIR, "test_self_retrace.py").toString(),
r8R8Release.getFirst().toString(),
r8R8Release.getSecond().toString()));
assertEquals(result.toString(), 0, result.exitCode);
}
@Test
public void testR8LibCompatibility() throws IOException, CompilationFailedException {
// Produce r81 = R8Lib(R8WithDeps) and r82 = R8LibNoDeps + Deps(R8WithDeps) and test that r81 is
// equal to r82. This test should only run if we are testing r8lib and we expect both R8libs to
// be built by gradle. If we are not testing with R8Lib, do not run this test.
if (!ToolHelper.isTestingR8Lib()) {
return;
}
Path runR81 =
testForExternalR8(Backend.CF)
.useProvidedR8(ToolHelper.R8LIB_JAR)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
.setMode(CompilationMode.RELEASE)
.compile()
.outputJar();
Path runR82 =
testForExternalR8(Backend.CF)
.useProvidedR8(ToolHelper.R8LIB_EXCLUDE_DEPS_JAR)
.addR8ExternalDepsToClasspath()
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
.setMode(CompilationMode.RELEASE)
.compile()
.outputJar();
assert filesAreEqual(runR81, runR82);
}
@Test
public void test() throws Exception {
Path helloJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION);
ProcessResult runResult = ToolHelper.runJava(helloJar, "hello.Hello");
assertEquals(0, runResult.exitCode);
compareR8(helloJar, runResult, KEEP_HELLO, "hello.Hello");
}
private void compareR8(Path program, ProcessResult runResult, String[] keep, String... args)
throws Exception {
ExternalR8TestCompileResult runR8Debug =
testForExternalR8(newTempFolder(), Backend.CF)
.useR8WithRelocatedDeps()
.addProgramFiles(program)
.addKeepRules(keep)
.setMode(CompilationMode.DEBUG)
.compile();
assertEquals(runResult.toString(), ToolHelper.runJava(runR8Debug.outputJar(), args).toString());
ExternalR8TestCompileResult runR8Release =
testForExternalR8(newTempFolder(), Backend.CF)
.useR8WithRelocatedDeps()
.addProgramFiles(program)
.addKeepRules(keep)
.setMode(CompilationMode.RELEASE)
.compile();
assertEquals(
runResult.toString(), ToolHelper.runJava(runR8Release.outputJar(), args).toString());
RunR8AndCheck(r8R8Debug, program, runR8Debug, keep, CompilationMode.DEBUG);
RunR8AndCheck(r8R8Debug, program, runR8Release, keep, CompilationMode.RELEASE);
RunR8AndCheck(r8R8Release, program, runR8Debug, keep, CompilationMode.DEBUG);
RunR8AndCheck(r8R8Release, program, runR8Release, keep, CompilationMode.RELEASE);
}
private void RunR8AndCheck(
Pair<Path, Path> r8,
Path program,
ExternalR8TestCompileResult result,
String[] keep,
CompilationMode mode)
throws Exception {
ExternalR8TestCompileResult runR8R8 =
testForExternalR8(newTempFolder(), Backend.CF)
.useProvidedR8(r8.getFirst())
.addProgramFiles(program)
.addKeepRules(keep)
.setMode(mode)
.compile();
// Check that the process outputs (exit code, stdout, stderr) are the same.
assertEquals(result.stdout(), runR8R8.stdout());
assertEquals(result.stderr(), runR8R8.stderr());
// Check that the output jars are the same.
assertProgramsEqual(result.outputJar(), runR8R8.outputJar());
}
public static void assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception {
if (filesAreEqual(expectedJar, actualJar)) {
return;
}
ArchiveClassFileProvider expected = new ArchiveClassFileProvider(expectedJar);
ArchiveClassFileProvider actual = new ArchiveClassFileProvider(actualJar);
assertEquals(getSortedDescriptorList(expected), getSortedDescriptorList(actual));
for (String descriptor : expected.getClassDescriptors()) {
assertArrayEquals(
"Class " + descriptor + " differs",
getClassAsBytes(expected, descriptor),
getClassAsBytes(actual, descriptor));
}
}
private static boolean filesAreEqual(Path file1, Path file2) throws IOException {
long size = Files.size(file1);
long sizeOther = Files.size(file2);
if (size != sizeOther) {
return false;
}
if (size < 4096) {
return Arrays.equals(Files.readAllBytes(file1), Files.readAllBytes(file2));
}
int byteRead1 = 0;
int byteRead2 = 0;
try (FileInputStream fs1 = new FileInputStream(file1.toString());
FileInputStream fs2 = new FileInputStream(file2.toString())) {
BufferedInputStream bs1 = new BufferedInputStream(fs1);
BufferedInputStream bs2 = new BufferedInputStream(fs2);
while (byteRead1 == byteRead2 && byteRead1 != -1) {
byteRead1 = bs1.read();
byteRead2 = bs2.read();
}
}
return byteRead1 == byteRead2;
}
private static List<String> getSortedDescriptorList(ArchiveClassFileProvider inputJar) {
ArrayList<String> descriptorList = new ArrayList<>(inputJar.getClassDescriptors());
Collections.sort(descriptorList);
return descriptorList;
}
private static byte[] getClassAsBytes(ArchiveClassFileProvider inputJar, String descriptor)
throws Exception {
return toByteArray(inputJar.getProgramResource(descriptor).getByteStream());
}
private static TemporaryFolder newTempFolder() throws IOException {
TemporaryFolder tempFolder = new TemporaryFolder(testFolder.newFolder());
tempFolder.create();
return tempFolder;
}
}