Reland "[Retrace] Add retrace api test infrastructure and a simple test"
This reverts commit 802ceb2a500beb7ac8be12d23e73b87522b74633.
Change-Id: I353d7e9d5c08fd2644813f1b5c76b059704457e3
diff --git a/.gitignore b/.gitignore
index 07f90a7..885f76d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -231,6 +231,8 @@
third_party/r8mappings.tar.gz
third_party/remapper
third_party/remapper.tar.gz
+third_party/retrace
+third_party/retrace.tar.gz
third_party/retrace_benchmark
third_party/retrace_benchmark.tar.gz
third_party/retrace_internal
diff --git a/build.gradle b/build.gradle
index 8b0525f..08c9141 100644
--- a/build.gradle
+++ b/build.gradle
@@ -346,6 +346,7 @@
"proguard/proguard5.2.1",
"proguard/proguard6.0.1",
"proguard/proguard-7.0.0",
+ "retrace/binary_compatibility",
"r8",
"r8-releases/2.0.74",
"r8mappings",
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index e7b9b00..da688a8 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -102,19 +102,24 @@
throws IOException {
try (ZipOutputStream stream =
new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(zipFile)))) {
- for (Path path : filesToZip) {
- ZipEntry zipEntry =
- new ZipEntry(
- StreamSupport.stream(
- Spliterators.spliteratorUnknownSize(
- basePath.relativize(path).iterator(), Spliterator.ORDERED),
- false)
- .map(Path::toString)
- .collect(Collectors.joining("/")));
- stream.putNextEntry(zipEntry);
- Files.copy(path, stream);
- stream.closeEntry();
- }
+ zip(stream, basePath, filesToZip);
+ }
+ }
+
+ public static void zip(ZipOutputStream stream, Path basePath, Collection<Path> filesToZip)
+ throws IOException {
+ for (Path path : filesToZip) {
+ ZipEntry zipEntry =
+ new ZipEntry(
+ StreamSupport.stream(
+ Spliterators.spliteratorUnknownSize(
+ basePath.relativize(path).iterator(), Spliterator.ORDERED),
+ false)
+ .map(Path::toString)
+ .collect(Collectors.joining("/")));
+ stream.putNextEntry(zipEntry);
+ Files.copy(path, stream);
+ stream.closeEntry();
}
}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 8c52813..2ccb378 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -8,7 +8,9 @@
import static com.android.tools.r8.ToolHelper.R8_TEST_BUCKET;
import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
import static com.google.common.collect.Lists.cartesianProduct;
+import static com.google.common.io.ByteStreams.toByteArray;
import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -93,6 +95,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -1913,4 +1916,54 @@
}
return false;
}
+
+ public static boolean assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception {
+ if (filesAreEqual(expectedJar, actualJar)) {
+ return true;
+ }
+ 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));
+ }
+ return false;
+ }
+
+ public 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());
+ }
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
index 8b3571a..91accca 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.TestState;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass;
-import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -116,7 +115,7 @@
@Test
public void testDatabaseGenerationUpToDate() throws Exception {
- BootstrapCurrentEqualityTest.filesAreEqual(generateJar(), API_DATABASE_JAR);
+ TestBase.filesAreEqual(generateJar(), API_DATABASE_JAR);
}
/**
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index e41bf8d..1a6717f 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -5,15 +5,12 @@
import static com.android.tools.r8.graph.GenericSignatureIdentityTest.testParseSignaturesInJar;
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-import static com.google.common.io.ByteStreams.toByteArray;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import com.android.tools.r8.ArchiveClassFileProvider;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ExternalR8TestCompileResult;
import com.android.tools.r8.TestBase;
@@ -29,15 +26,9 @@
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.Lists;
-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;
@@ -183,8 +174,7 @@
.setMode(CompilationMode.RELEASE)
.compile()
.outputJar();
- assert uploadJarsToCloudStorageIfTestFails(
- BootstrapCurrentEqualityTest::filesAreEqual, runR81, runR82);
+ assert uploadJarsToCloudStorageIfTestFails(TestBase::filesAreEqual, runR81, runR82);
}
@Test
@@ -247,56 +237,6 @@
BootstrapCurrentEqualityTest::assertProgramsEqual, result.outputJar(), runR8R8.outputJar());
}
- public static boolean assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception {
- if (filesAreEqual(expectedJar, actualJar)) {
- return true;
- }
- 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));
- }
- return false;
- }
-
- public 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();
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
index 7dd698b..409236d 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
@@ -4,11 +4,8 @@
package com.android.tools.r8.cf.bootstrap;
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.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.R8Command;
@@ -21,9 +18,6 @@
import com.google.common.base.Charsets;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -153,27 +147,4 @@
String pgMap = FileUtils.readTextFile(pgMapFile, Charsets.UTF_8);
return new R8Result(processResult, outputJar, pgMap);
}
-
- private static void assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception {
- 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 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());
- }
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
index bb66f2e..01eb3b8 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
@@ -12,13 +12,13 @@
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Pair;
@@ -166,7 +166,7 @@
+ " differ from the external run which uses "
+ r8jar
+ ". If up-to-date, the likely cause of this error is that R8 is non-deterministic.",
- BootstrapCurrentEqualityTest.filesAreEqual(outputThroughCf, outputThroughCfExternal));
+ TestBase.filesAreEqual(outputThroughCf, outputThroughCfExternal));
}
// Finally compile R8 on the ART runtime using the already compiled DEX version of R8.
@@ -194,7 +194,7 @@
assertEquals(0, artProcessResult.exitCode);
assertTrue(
"The output of R8/JVM in-process and R8/ART external differ.",
- BootstrapCurrentEqualityTest.filesAreEqual(outputThroughCf, outputThroughDex));
+ TestBase.filesAreEqual(outputThroughCf, outputThroughDex));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
new file mode 100644
index 0000000..4557036
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2021, 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.retrace.api;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.ZipUtils;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+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;
+
+@RunWith(Parameterized.class)
+public class RetraceApiBinaryCompatibilityTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public RetraceApiBinaryCompatibilityTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private static final Path BINARY_COMPATIBILITY_JAR =
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, "retrace", "binary_compatibility", "tests.jar");
+
+ private Path generateJar() throws Exception {
+ return RetraceApiTestHelper.generateJarForRetraceBinaryTests(
+ temp, RetraceApiTestHelper.getBinaryCompatibilityTests());
+ }
+
+ @Test
+ public void testBinaryJarIsUpToDate() throws Exception {
+ Path binaryContents = temp.newFolder().toPath();
+ Path generatedContents = temp.newFolder().toPath();
+ ZipUtils.unzip(BINARY_COMPATIBILITY_JAR, binaryContents);
+ ZipUtils.unzip(generateJar(), generatedContents);
+ try (Stream<Path> existingPaths = Files.walk(binaryContents);
+ Stream<Path> generatedPaths = Files.walk(generatedContents)) {
+ List<Path> existing = existingPaths.filter(this::isClassFile).collect(Collectors.toList());
+ List<Path> generated = generatedPaths.filter(this::isClassFile).collect(Collectors.toList());
+ assertEquals(existing.size(), generated.size());
+ assertNotEquals(0, existing.size());
+ for (Path classFile : generated) {
+ Path otherClassFile = binaryContents.resolve(classFile);
+ assertTrue(Files.exists(otherClassFile));
+ assertTrue(TestBase.filesAreEqual(classFile, otherClassFile));
+ }
+ }
+ }
+
+ private boolean isClassFile(Path path) {
+ return path.toString().endsWith(".class");
+ }
+
+ @Test
+ public void runCheckedInBinaryJar() throws Exception {
+ for (CfRuntime cfRuntime : CfRuntime.getCheckedInCfRuntimes()) {
+ RetraceApiTestHelper.runJunitOnTests(
+ cfRuntime,
+ ToolHelper.R8_RETRACE_JAR,
+ BINARY_COMPATIBILITY_JAR,
+ RetraceApiTestHelper.getBinaryCompatibilityTests());
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ TemporaryFolder temp = new TemporaryFolder();
+ temp.create();
+ Path generatedJar =
+ RetraceApiTestHelper.generateJarForRetraceBinaryTests(
+ temp, RetraceApiTestHelper.getBinaryCompatibilityTests());
+ Files.move(generatedJar, BINARY_COMPATIBILITY_JAR, REPLACE_EXISTING);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryTest.java
new file mode 100644
index 0000000..eac71c5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryTest.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2021, 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.retrace.api;
+
+public interface RetraceApiBinaryTest {}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiEmptyTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiEmptyTest.java
new file mode 100644
index 0000000..45df95c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiEmptyTest.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2021, 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.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetracedClassReference;
+import com.android.tools.r8.retrace.Retracer;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetraceApiEmptyTest extends RetraceApiTestBase {
+
+ public RetraceApiEmptyTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+ return RetraceTest.class;
+ }
+
+ public static class RetraceTest implements RetraceApiBinaryTest {
+
+ @Test
+ public void testNone() throws Exception {
+ String expected = "hello.World";
+ List<RetracedClassReference> retracedClasses = new ArrayList<>();
+ Retracer.createDefault(ProguardMapProducer.fromString(""), new DiagnosticsHandler() {})
+ .retraceClass(Reference.classFromTypeName(expected))
+ .stream()
+ .forEach(
+ result -> {
+ retracedClasses.add(result.getRetracedClass());
+ });
+ assertEquals(1, retracedClasses.size());
+ RetracedClassReference retracedClass = retracedClasses.get(0);
+ assertEquals(retracedClass.getTypeName(), expected);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestBase.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestBase.java
new file mode 100644
index 0000000..a58102a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestBase.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2021, 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.retrace.api;
+
+import static com.android.tools.r8.retrace.api.RetraceApiTestHelper.runJunitOnTests;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersBuilder;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import java.nio.file.Files;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+import org.junit.runners.Parameterized.Parameters;
+
+public abstract class RetraceApiTestBase extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestParametersBuilder.builder().withSystemRuntime().build();
+ }
+
+ public RetraceApiTestBase(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ protected abstract Class<? extends RetraceApiBinaryTest> binaryTestClass();
+
+ @Test
+ public void testDirect() {
+ Result result = JUnitCore.runClasses(binaryTestClass());
+ for (Failure failure : result.getFailures()) {
+ System.out.println(failure.toString());
+ }
+ assertTrue(result.wasSuccessful());
+ }
+
+ @Test
+ public void testRetraceLib() throws Exception {
+ Assume.assumeTrue(Files.exists(ToolHelper.R8_RETRACE_JAR));
+ runJunitOnTests(
+ parameters.getRuntime().asCf(), ToolHelper.R8_RETRACE_JAR, binaryTestClass(), temp);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
new file mode 100644
index 0000000..9b2f67d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2021, 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.retrace.api;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.Collectors;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.transformers.ClassFileTransformer;
+import com.android.tools.r8.transformers.ClassFileTransformer.InnerClassPredicate;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.junit.rules.TemporaryFolder;
+
+public class RetraceApiTestHelper {
+
+ private static final String JUNIT_JAR = "junit-4.13-beta-2.jar";
+ private static final String HAMCREST = "hamcrest-core-1.3.jar";
+
+ public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_FOR_BINARY_COMPATIBILITY =
+ ImmutableList.of(RetraceApiEmptyTest.RetraceTest.class);
+ public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
+ ImmutableList.of();
+
+ public static void runJunitOnTests(
+ CfRuntime runtime,
+ Path r8Jar,
+ Class<? extends RetraceApiBinaryTest> clazz,
+ TemporaryFolder temp)
+ throws Exception {
+ assertTrue(testIsSpecifiedAsBinaryOrPending(clazz));
+ List<Class<? extends RetraceApiBinaryTest>> testClasses = ImmutableList.of(clazz);
+ runJunitOnTests(
+ runtime, r8Jar, generateJarForRetraceBinaryTests(temp, testClasses), testClasses);
+ }
+
+ public static void runJunitOnTests(
+ CfRuntime runtime,
+ Path r8Jar,
+ Path testJar,
+ Collection<Class<? extends RetraceApiBinaryTest>> tests)
+ throws Exception {
+ List<Path> classPaths = ImmutableList.of(getJunitDependency(), getHamcrest(), r8Jar, testJar);
+ ProcessResult processResult =
+ ToolHelper.runJava(
+ runtime,
+ classPaths,
+ "org.junit.runner.JUnitCore",
+ StringUtils.join(" ", tests, Class::getTypeName));
+ assertEquals(0, processResult.exitCode);
+ assertThat(processResult.stdout, containsString("OK (" + tests.size() + " test"));
+ }
+
+ private static Path getJunitDependency() {
+ String junitPath =
+ Arrays.stream(System.getProperty("java.class.path").split(":"))
+ .filter(cp -> cp.endsWith(JUNIT_JAR))
+ .collect(Collectors.toSingle());
+ return Paths.get(junitPath);
+ }
+
+ private static Path getHamcrest() {
+ String junitPath =
+ Arrays.stream(System.getProperty("java.class.path").split(":"))
+ .filter(cp -> cp.endsWith(HAMCREST))
+ .collect(Collectors.toSingle());
+ return Paths.get(junitPath);
+ }
+
+ public static Path generateJarForRetraceBinaryTests(
+ TemporaryFolder temp, Collection<Class<? extends RetraceApiBinaryTest>> classes)
+ throws Exception {
+ Path jar = File.createTempFile("retrace_api_tests", ".jar", temp.getRoot()).toPath();
+ ZipBuilder zipBuilder = ZipBuilder.builder(jar);
+ for (Class<? extends RetraceApiBinaryTest> retraceApiTest : classes) {
+ zipBuilder.addFilesRelative(
+ ToolHelper.getClassPathForTests(),
+ ToolHelper.getClassFilesForInnerClasses(retraceApiTest));
+ zipBuilder.addBytes(
+ ZipUtils.zipEntryNameForClass(retraceApiTest),
+ ClassFileTransformer.create(retraceApiTest)
+ .removeInnerClasses(
+ InnerClassPredicate.onName(
+ DescriptorUtils.getBinaryNameFromJavaType(retraceApiTest.getTypeName())))
+ .transform());
+ }
+ zipBuilder.addFilesRelative(
+ ToolHelper.getClassPathForTests(),
+ ToolHelper.getClassFileForTestClass(RetraceApiBinaryTest.class));
+ return zipBuilder.build();
+ }
+
+ public static Collection<Class<? extends RetraceApiBinaryTest>> getBinaryCompatibilityTests() {
+ return CLASSES_FOR_BINARY_COMPATIBILITY;
+ }
+
+ private static boolean testIsSpecifiedAsBinaryOrPending(Class<?> clazz) {
+ return CLASSES_FOR_BINARY_COMPATIBILITY.contains(clazz)
+ || CLASSES_PENDING_BINARY_COMPATIBILITY.contains(clazz);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 2bbcf4a..bc72103 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.transformers;
import static com.android.tools.r8.references.Reference.classFromTypeName;
+import static com.android.tools.r8.transformers.ClassFileTransformer.InnerClassPredicate.always;
import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromDescriptor;
import static com.android.tools.r8.utils.StringUtils.replaceAll;
import static org.objectweb.asm.Opcodes.ASM7;
@@ -31,6 +32,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -556,12 +558,31 @@
boolean test(String name, String typeDescriptor);
}
+ @FunctionalInterface
+ public interface InnerClassPredicate {
+ boolean test(String name, String outerName, String innerName, int access);
+
+ static InnerClassPredicate always() {
+ return (name, outerName, innerName, access) -> true;
+ }
+
+ static InnerClassPredicate onName(String name) {
+ return (otherName, outerName, innerName, access) -> Objects.equals(name, otherName);
+ }
+ }
+
public ClassFileTransformer removeInnerClasses() {
+ return removeInnerClasses(always());
+ }
+
+ public ClassFileTransformer removeInnerClasses(InnerClassPredicate predicate) {
return addClassTransformer(
new ClassTransformer() {
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
- // Intentionally empty.
+ if (!predicate.test(name, outerName, innerName, access)) {
+ super.visitInnerClass(name, outerName, innerName, access);
+ }
}
});
}
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
new file mode 100644
index 0000000..3825d93
--- /dev/null
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -0,0 +1 @@
+63dd09b086d0d134219a379720c6b68a94afdf48
\ No newline at end of file