Add new test building abstractions.
Change-Id: I5b8595f924f2ee3715a6e7a9d7e1436ede977e56
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index a4f55c3..ed12177 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -60,6 +60,58 @@
private final List<StringResource> mainDexListResources;
private final List<String> mainDexClasses;
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ try {
+ if (!programResourceProviders.isEmpty()) {
+ builder.append(" Program resources:").append(System.lineSeparator());
+ printProgramResourceProviders(builder, programResourceProviders);
+ }
+ if (!classpathResourceProviders.isEmpty()) {
+ builder.append(" Classpath resources:").append(System.lineSeparator());
+ printClassFileProviders(builder, classpathResourceProviders);
+ }
+ if (!libraryResourceProviders.isEmpty()) {
+ builder.append(" Library resources:").append(System.lineSeparator());
+ printClassFileProviders(builder, libraryResourceProviders);
+ }
+ } catch (ResourceException e) {
+ e.printStackTrace();
+ }
+ return builder.toString();
+ }
+
+ private static void printProgramResourceProviders(
+ StringBuilder builder, Collection<ProgramResourceProvider> providers)
+ throws ResourceException {
+ for (ProgramResourceProvider provider : providers) {
+ for (ProgramResource resource : provider.getProgramResources()) {
+ printProgramResource(builder, resource);
+ }
+ }
+ }
+
+ private static void printClassFileProviders(
+ StringBuilder builder, Collection<ClassFileResourceProvider> providers) {
+ for (ClassFileResourceProvider provider : providers) {
+ for (String descriptor : provider.getClassDescriptors()) {
+ ProgramResource resource = provider.getProgramResource(descriptor);
+ printProgramResource(builder, resource);
+ }
+ }
+ }
+
+ private static void printProgramResource(StringBuilder builder, ProgramResource resource) {
+ builder.append(" ").append(resource.getOrigin());
+ Set<String> descriptors = resource.getClassDescriptors();
+ if (descriptors != null && !descriptors.isEmpty()) {
+ builder.append(" contains ");
+ StringUtils.append(builder, descriptors);
+ }
+ builder.append(System.lineSeparator());
+ }
+
// See factory methods and AndroidApp.Builder below.
private AndroidApp(
ImmutableList<ProgramResourceProvider> programResourceProviders,
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
new file mode 100644
index 0000000..368e8e4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -0,0 +1,51 @@
+// 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;
+
+import com.android.tools.r8.D8Command.Builder;
+import com.android.tools.r8.TestBase.Backend;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
+
+public class D8TestBuilder extends TestCompilerBuilder<D8Command, Builder, D8TestBuilder> {
+
+ private final D8Command.Builder builder;
+
+ private D8TestBuilder(TestState state, D8Command.Builder builder) {
+ super(state, builder, Backend.DEX);
+ this.builder = builder;
+ }
+
+ public static D8TestBuilder create(TestState state) {
+ return new D8TestBuilder(state, D8Command.builder());
+ }
+
+ @Override
+ D8TestBuilder self() {
+ return this;
+ }
+
+ @Override
+ void internalCompile(Builder builder) throws CompilationFailedException {
+ D8.run(builder.build());
+ }
+
+ public D8TestBuilder addClasspathClasses(Class<?>... classes) {
+ return addClasspathClasses(Arrays.asList(classes));
+ }
+
+ public D8TestBuilder addClasspathClasses(Collection<Class<?>> classes) {
+ return addClasspathFiles(getFilesForClasses(classes));
+ }
+
+ public D8TestBuilder addClasspathFiles(Path... files) {
+ return addClasspathFiles(Arrays.asList(files));
+ }
+
+ public D8TestBuilder addClasspathFiles(Collection<Path> files) {
+ builder.addClasspathFiles(files);
+ return self();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
new file mode 100644
index 0000000..484adee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -0,0 +1,158 @@
+// 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;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ListUtils;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class JvmTestBuilder extends TestBuilder<JvmTestBuilder> {
+
+ private static class ClassFileResource implements ProgramResource {
+
+ private final Path file;
+ private final String descriptor;
+ private final Origin origin;
+
+ ClassFileResource(Class<?> clazz) {
+ this(
+ ToolHelper.getClassFileForTestClass(clazz),
+ DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()));
+ }
+
+ ClassFileResource(Path file, String descriptor) {
+ this.file = file;
+ this.descriptor = descriptor;
+ origin = new PathOrigin(file);
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.CF;
+ }
+
+ @Override
+ public InputStream getByteStream() throws ResourceException {
+ try {
+ return Files.newInputStream(file);
+ } catch (IOException e) {
+ throw new ResourceException(getOrigin(), e);
+ }
+ }
+
+ @Override
+ public Set<String> getClassDescriptors() {
+ return Collections.singleton(descriptor);
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+ }
+
+ private static class ClassFileResourceProvider implements ProgramResourceProvider {
+
+ private final List<ProgramResource> resources;
+
+ public ClassFileResourceProvider(List<ProgramResource> resources) {
+ this.resources = resources;
+ }
+
+ @Override
+ public Collection<ProgramResource> getProgramResources() throws ResourceException {
+ return resources;
+ }
+
+ @Override
+ public DataResourceProvider getDataResourceProvider() {
+ return null;
+ }
+ }
+
+ // Ordered list of classpath entries.
+ private List<Path> classpath = new ArrayList<>();
+
+ private AndroidApp.Builder builder = AndroidApp.builder();
+
+ private JvmTestBuilder(TestState state) {
+ super(state);
+ }
+
+ public static JvmTestBuilder create(TestState state) {
+ return new JvmTestBuilder(state);
+ }
+
+ @Override
+ JvmTestBuilder self() {
+ return this;
+ }
+
+ @Override
+ public TestRunResult run(String mainClass) throws IOException {
+ ProcessResult result = ToolHelper.runJava(classpath, mainClass);
+ return new TestRunResult(builder.build(), result);
+ }
+
+ @Override
+ public JvmTestBuilder addLibraryFiles(Collection<Path> files) {
+ throw new Unimplemented("No support for changing the Java runtime library.");
+ }
+
+ @Override
+ public JvmTestBuilder addProgramClasses(Collection<Class<?>> classes) {
+ // Adding a collection of classes will build a jar of exactly those classes so that no other
+ // classes are made available via a too broad classpath directory.
+ List<ProgramResource> resources = ListUtils.map(classes, ClassFileResource::new);
+ AndroidApp build = AndroidApp.builder()
+ .addProgramResourceProvider(new ClassFileResourceProvider(resources)).build();
+ Path out;
+ try {
+ out = getState().getNewTempFolder().resolve("out.zip");
+ build.writeToZip(out, OutputMode.ClassFile);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ classpath.add(out);
+ builder.addProgramFiles(out);
+ return self();
+ }
+
+ @Override
+ public JvmTestBuilder addProgramFiles(Collection<Path> files) {
+ throw new Unimplemented(
+ "No support for adding paths directly (we need to compute the descriptor)");
+ }
+
+ public JvmTestBuilder addClasspath(Path... paths) {
+ return addClasspath(Arrays.asList(paths));
+ }
+
+ public JvmTestBuilder addClasspath(List<Path> paths) {
+ for (Path path : paths) {
+ assert Files.isDirectory(path) || FileUtils.isArchive(path);
+ classpath.add(path);
+ }
+ return self();
+ }
+
+ public JvmTestBuilder addTestClasspath() {
+ return addClasspath(ToolHelper.getClassPathForTests());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
new file mode 100644
index 0000000..50e659c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -0,0 +1,84 @@
+// 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;
+
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+public class R8TestBuilder extends TestCompilerBuilder<R8Command, Builder, R8TestBuilder> {
+
+ private final R8Command.Builder builder;
+
+ private R8TestBuilder(TestState state, Builder builder, Backend backend) {
+ super(state, builder, backend);
+ this.builder = builder;
+ }
+
+ public static R8TestBuilder create(TestState state, Backend backend) {
+ return new R8TestBuilder(state, R8Command.builder(), backend);
+ }
+
+ private boolean enableInliningAnnotations = false;
+
+ @Override
+ R8TestBuilder self() {
+ return this;
+ }
+
+ @Override
+ public void internalCompile(Builder builder) throws CompilationFailedException {
+ if (enableInliningAnnotations) {
+ ToolHelper.allowTestProguardOptions(builder);
+ }
+ R8.run(builder.build());
+ }
+
+ public R8TestBuilder addKeepRules(String... rules) {
+ return addKeepRules(Arrays.asList(rules));
+ }
+
+ public R8TestBuilder addKeepRules(Collection<String> rules) {
+ builder.addProguardConfiguration(new ArrayList<>(rules), Origin.unknown());
+ return self();
+ }
+
+ public R8TestBuilder addKeepAllClassesRule() {
+ addKeepRules("-keep class ** { *; }");
+ return self();
+ }
+
+ public R8TestBuilder addKeepClassRules(Class<?>... classes) {
+ for (Class<?> clazz : classes) {
+ addKeepRules("-keep class " + clazz.getTypeName());
+ }
+ return self();
+ }
+
+ public R8TestBuilder addKeepPackageRules(Package pkg) {
+ return addKeepRules("-keep class " + pkg.getName() + ".*");
+ }
+
+ public R8TestBuilder addKeepMainRule(Class<?> mainClass) {
+ return addKeepRules(
+ StringUtils.joinLines(
+ "-keep class " + mainClass.getTypeName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}"));
+ }
+
+ public R8TestBuilder enableInliningAnnotations() {
+ if (!enableInliningAnnotations) {
+ enableInliningAnnotations = true;
+ addKeepRules(
+ "-forceinline class * { @com.android.tools.r8.ForceInline *; }",
+ "-neverinline class * { @com.android.tools.r8.NeverInline *; }");
+ }
+ return self();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 94cc54b..efec188 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -59,6 +59,19 @@
import org.objectweb.asm.ClassVisitor;
public class TestBase {
+
+ public R8TestBuilder testForR8(Backend backend) {
+ return R8TestBuilder.create(new TestState(temp), backend);
+ }
+
+ public D8TestBuilder testForD8() {
+ return D8TestBuilder.create(new TestState(temp));
+ }
+
+ public JvmTestBuilder testForJvm() {
+ return JvmTestBuilder.create(new TestState(temp));
+ }
+
public enum Backend {
CF,
DEX
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
new file mode 100644
index 0000000..6d5a158
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -0,0 +1,89 @@
+// 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;
+
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+
+import com.android.tools.r8.utils.ListUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class TestBuilder<T extends TestBuilder<T>> {
+
+ private final TestState state;
+
+ public TestBuilder(TestState state) {
+ this.state = state;
+ }
+
+ public TestState getState() {
+ return state;
+ }
+
+ abstract T self();
+
+ public abstract TestRunResult run(String mainClass)
+ throws IOException, CompilationFailedException;
+
+ public TestRunResult run(Class mainClass) throws IOException, CompilationFailedException {
+ return run(mainClass.getTypeName());
+ }
+
+ public abstract T addProgramFiles(Collection<Path> files);
+
+ public T addProgramClasses(Class<?>... classes) {
+ return addProgramClasses(Arrays.asList(classes));
+ }
+
+ public T addProgramClasses(Collection<Class<?>> classes) {
+ return addProgramFiles(getFilesForClasses(classes));
+ }
+
+ public T addProgramFiles(Path... files) {
+ return addProgramFiles(Arrays.asList(files));
+ }
+
+ public T addProgramClassesAndInnerClasses(Class<?>... classes) throws IOException {
+ return addProgramClassesAndInnerClasses(Arrays.asList(classes));
+ }
+
+ public T addProgramClassesAndInnerClasses(Collection<Class<?>> classes) throws IOException {
+ return addProgramFiles(getFilesForClassesAndInnerClasses(classes));
+ }
+
+ public abstract T addLibraryFiles(Collection<Path> files);
+
+ public T addLibraryClasses(Class<?>... classes) {
+ return addLibraryClasses(Arrays.asList(classes));
+ }
+
+ public T addLibraryClasses(Collection<Class<?>> classes) {
+ return addLibraryFiles(getFilesForClasses(classes));
+ }
+
+ public T addLibraryFiles(Path... files) {
+ return addLibraryFiles(Arrays.asList(files));
+ }
+
+ static Collection<Path> getFilesForClasses(Collection<Class<?>> classes) {
+ return ListUtils.map(classes, ToolHelper::getClassFileForTestClass);
+ }
+
+ static Collection<Path> getFilesForClassesAndInnerClasses(Collection<Class<?>> classes)
+ throws IOException {
+ Set<Path> paths = new HashSet<>();
+ for (Class clazz : classes) {
+ Path path = ToolHelper.getClassFileForTestClass(clazz);
+ String prefix = path.toString().replace(CLASS_EXTENSION, "$");
+ paths.addAll(
+ ToolHelper.getClassFilesForTestDirectory(
+ path.getParent(), p -> p.equals(path) || p.toString().startsWith(prefix)));
+ }
+ return paths;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
new file mode 100644
index 0000000..9996dde
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -0,0 +1,48 @@
+// 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;
+
+import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.AndroidApp;
+import java.io.IOException;
+import java.nio.file.Path;
+
+public class TestCompileResult {
+ private final TestState state;
+ private final Backend backend;
+ private final AndroidApp app;
+
+ public TestCompileResult(TestState state, Backend backend, AndroidApp app) {
+ this.state = state;
+ this.backend = backend;
+ this.app = app;
+ }
+
+ public TestRunResult run(String mainClass) throws IOException {
+ switch (backend) {
+ case DEX:
+ return runArt(mainClass);
+ case CF:
+ return runJava(mainClass);
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ private TestRunResult runJava(String mainClass) throws IOException {
+ Path out = state.getNewTempFolder().resolve("out.zip");
+ app.writeToZip(out, OutputMode.ClassFile);
+ ProcessResult result = ToolHelper.runJava(out, mainClass);
+ return new TestRunResult(app, result);
+ }
+
+ private TestRunResult runArt(String mainClass) throws IOException {
+ Path out = state.getNewTempFolder().resolve("out.zip");
+ app.writeToZip(out, OutputMode.DexIndexed);
+ ProcessResult result = ToolHelper.runArtRaw(out.toString(), mainClass);
+ return new TestRunResult(app, result);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
new file mode 100644
index 0000000..c6af2c7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -0,0 +1,95 @@
+// 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;
+
+import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidAppConsumers;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+
+public abstract class TestCompilerBuilder<
+ C extends BaseCompilerCommand,
+ B extends BaseCompilerCommand.Builder<C, B>,
+ T extends TestCompilerBuilder<C, B, T>>
+ extends TestBuilder<T> {
+
+ private final B builder;
+ private final Backend backend;
+
+ // Default initialized setup. Can be overwritten if needed.
+ private Path defaultLibrary;
+ private ProgramConsumer programConsumer;
+ private AndroidApiLevel defaultMinApiLevel = ToolHelper.getMinApiLevelForDexVm();
+
+ TestCompilerBuilder(TestState state, B builder, Backend backend) {
+ super(state);
+ this.builder = builder;
+ this.backend = backend;
+ defaultLibrary = TestBase.runtimeJar(backend);
+ programConsumer = TestBase.emptyConsumer(backend);
+ }
+
+ abstract T self();
+
+ abstract void internalCompile(B builder) throws CompilationFailedException;
+
+ public TestCompileResult compile() throws CompilationFailedException {
+ AndroidAppConsumers sink = new AndroidAppConsumers();
+ builder.setProgramConsumer(sink.wrapProgramConsumer(programConsumer));
+ if (defaultLibrary != null) {
+ builder.addLibraryFiles(defaultLibrary);
+ }
+ if (backend == Backend.DEX && defaultMinApiLevel != null) {
+ builder.setMinApiLevel(defaultMinApiLevel.getLevel());
+ }
+ internalCompile(builder);
+ return new TestCompileResult(getState(), backend, sink.build());
+ }
+
+ @Override
+ public TestRunResult run(String mainClass) throws IOException, CompilationFailedException {
+ return compile().run(mainClass);
+ }
+
+ public T setMode(CompilationMode mode) {
+ builder.setMode(mode);
+ return self();
+ }
+
+ public T debug() {
+ return setMode(CompilationMode.DEBUG);
+ }
+
+ public T release() {
+ return setMode(CompilationMode.RELEASE);
+ }
+
+ public T setMinApi(AndroidApiLevel minApiLevel) {
+ // Should we ignore min-api calls when backend == CF?
+ this.defaultMinApiLevel = null;
+ builder.setMinApiLevel(minApiLevel.getLevel());
+ return self();
+ }
+
+ public T setProgramConsumer(ProgramConsumer programConsumer) {
+ assert programConsumer != null;
+ this.programConsumer = programConsumer;
+ return self();
+ }
+
+ @Override
+ public T addProgramFiles(Collection<Path> files) {
+ builder.addProgramFiles(files);
+ return self();
+ }
+
+ @Override
+ public T addLibraryFiles(Collection<Path> files) {
+ defaultLibrary = null;
+ builder.addLibraryFiles(files);
+ return self();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
new file mode 100644
index 0000000..462ca1f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -0,0 +1,68 @@
+// 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+public class TestRunResult {
+ private final AndroidApp app;
+ private final ProcessResult result;
+
+ public TestRunResult(AndroidApp app, ProcessResult result) {
+ this.app = app;
+ this.result = result;
+ }
+
+ public TestRunResult assertSuccess() {
+ assertEquals(errorMessage("Expected run to succeed."), 0, result.exitCode);
+ return this;
+ }
+
+ public TestRunResult assertFailure() {
+ assertNotEquals(errorMessage("Expected run to fail."), 0, result.exitCode);
+ return this;
+ }
+
+ public void assertSuccessWithOutput(String expected) {
+ assertSuccess();
+ assertEquals(errorMessage("Run std output incorrect."), expected, result.stdout);
+ }
+
+ public CodeInspector inspector() throws IOException, ExecutionException {
+ // Inspection post run implies success. If inspection of an invalid program is needed it should
+ // be done on the compilation result or on the input.
+ assertSuccess();
+ assertNotNull(app);
+ return new CodeInspector(app);
+ }
+
+ private String errorMessage(String message) {
+ StringBuilder builder = new StringBuilder(message).append('\n');
+ printInfo(builder);
+ return builder.toString();
+ }
+
+ private void printInfo(StringBuilder builder) {
+ builder.append("APPLICATION: ");
+ printApplication(builder);
+ builder.append('\n');
+ printProcessResult(builder);
+ }
+
+ private void printApplication(StringBuilder builder) {
+ builder.append(app == null ? "<default>" : app.toString());
+ }
+
+ private void printProcessResult(StringBuilder builder) {
+ builder.append("COMMAND: ").append(result.command).append('\n').append(result);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/TestState.java b/src/test/java/com/android/tools/r8/TestState.java
new file mode 100644
index 0000000..d516c7c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestState.java
@@ -0,0 +1,21 @@
+// 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;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import org.junit.rules.TemporaryFolder;
+
+public class TestState {
+
+ private final TemporaryFolder temp;
+
+ public TestState(TemporaryFolder temp) {
+ this.temp = temp;
+ }
+
+ public Path getNewTempFolder() throws IOException {
+ return temp.newFolder().toPath();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 515d191..8ae0dad 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1531,11 +1531,17 @@
public final int exitCode;
public final String stdout;
public final String stderr;
+ public final String command;
- ProcessResult(int exitCode, String stdout, String stderr) {
+ ProcessResult(int exitCode, String stdout, String stderr, String command) {
this.exitCode = exitCode;
this.stdout = stdout;
this.stderr = stderr;
+ this.command = command;
+ }
+
+ ProcessResult(int exitCode, String stdout, String stderr) {
+ this(exitCode, stdout, stderr, null);
}
@Override
@@ -1574,7 +1580,8 @@
}
public static ProcessResult runProcess(ProcessBuilder builder) throws IOException {
- System.out.println(String.join(" ", builder.command()));
+ String command = String.join(" ", builder.command());
+ System.out.println(command);
Process p = builder.start();
// Drain stdout and stderr so that the process does not block. Read stdout and stderr
// in parallel to make sure that neither buffer can get filled up which will cause the
@@ -1592,7 +1599,8 @@
} catch (InterruptedException e) {
throw new RuntimeException("Execution interrupted", e);
}
- return new ProcessResult(p.exitValue(), stdoutReader.getResult(), stderrReader.getResult());
+ return new ProcessResult(
+ p.exitValue(), stdoutReader.getResult(), stderrReader.getResult(), command);
}
public static R8Command.Builder addProguardConfigurationConsumer(
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/IndirectSuperInterfaceTest.java b/src/test/java/com/android/tools/r8/memberrebinding/IndirectSuperInterfaceTest.java
index 9552a61..977b925 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/IndirectSuperInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/IndirectSuperInterfaceTest.java
@@ -3,17 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.memberrebinding;
-import static org.junit.Assert.assertEquals;
-
import com.android.tools.r8.NeverInline;
-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.ToolHelper;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.AndroidAppConsumers;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
@@ -149,38 +141,23 @@
@Test
public void test() throws Exception {
- String expected =
- String.join(
- System.lineSeparator(),
- "A::method -> InterfaceA::method",
- "B::method -> InterfaceB::method",
- "C::method -> InterfaceC::method",
- "D::method -> InterfaceD::method");
- assertEquals(expected, runOnJava(TestClass.class));
+ String expected = StringUtils.joinLines(
+ "A::method -> InterfaceA::method",
+ "B::method -> InterfaceB::method",
+ "C::method -> InterfaceC::method",
+ "D::method -> InterfaceD::method");
- AndroidAppConsumers sink = new AndroidAppConsumers();
- Builder builder = R8Command.builder();
- for (Class<?> clazz : CLASSES) {
- builder.addClassProgramData(ToolHelper.getClassAsBytes(clazz), Origin.unknown());
- }
- builder
- .setProgramConsumer(sink.wrapProgramConsumer(emptyConsumer(backend)))
- .addLibraryFiles(runtimeJar(backend))
- .addProguardConfiguration(
- ImmutableList.of(
- // Keep all classes to prevent changes to the class hierarchy (e.g., due to
- // vertical class merging).
- "-keep class " + InterfaceA.class.getPackage().getName() + ".*",
- keepMainProguardConfigurationWithInliningAnnotation(TestClass.class)),
- Origin.unknown());
- ToolHelper.allowTestProguardOptions(builder);
- if (backend == Backend.DEX) {
- builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
- }
- R8.run(builder.build());
+ testForJvm()
+ .addTestClasspath()
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expected);
- ProcessResult result = runOnVMRaw(sink.build(), TestClass.class, backend);
- assertEquals(result.toString(), 0, result.exitCode);
- assertEquals(expected, result.stdout);
+ testForR8(backend)
+ .addProgramClasses(CLASSES)
+ .addKeepPackageRules(TestClass.class.getPackage())
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expected);
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java
index 96ce54a..6992f97 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java
@@ -9,15 +9,8 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.R8;
-import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.forceproguardcompatibility.keepattributes.TestKeepAttributes;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.AndroidAppConsumers;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -70,7 +63,6 @@
public void keepLineNumberTable()
throws CompilationFailedException, IOException, ExecutionException {
List<String> keepRules = ImmutableList.of(
- "-keep class ** { *; }",
"-keepattributes " + ProguardKeepAttributes.LINE_NUMBER_TABLE
);
MethodSubject mainMethod = compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
@@ -82,7 +74,6 @@
public void keepLineNumberTableAndLocalVariableTable()
throws CompilationFailedException, IOException, ExecutionException {
List<String> keepRules = ImmutableList.of(
- "-keep class ** { *; }",
"-keepattributes "
+ ProguardKeepAttributes.LINE_NUMBER_TABLE
+ ", "
@@ -97,7 +88,6 @@
@Test
public void keepLocalVariableTable() throws IOException, ExecutionException {
List<String> keepRules = ImmutableList.of(
- "-keep class ** { *; }",
"-keepattributes " + ProguardKeepAttributes.LOCAL_VARIABLE_TABLE
);
// Compiling with a keep rule for locals but no line results in an error in R8.
@@ -113,21 +103,14 @@
private MethodSubject compileRunAndGetMain(List<String> keepRules, CompilationMode mode)
throws CompilationFailedException, IOException, ExecutionException {
- AndroidAppConsumers sink = new AndroidAppConsumers();
- R8.run(
- R8Command.builder()
- .setMode(mode)
- .addProgramFiles(
- ToolHelper.getClassFilesForTestDirectory(
- ToolHelper.getClassFileForTestClass(CLASS).getParent()))
- .addLibraryFiles(runtimeJar(backend))
- .addProguardConfiguration(keepRules, Origin.unknown())
- .setProgramConsumer(sink.wrapProgramConsumer(emptyConsumer(backend)))
- .build());
- AndroidApp app = sink.build();
- CodeInspector codeInspector = new CodeInspector(app);
- runOnVM(app, CLASS.getTypeName(), backend);
- return codeInspector.clazz(CLASS).mainMethod();
+ return testForR8(backend)
+ .setMode(mode)
+ .addProgramClassesAndInnerClasses(CLASS)
+ .addKeepAllClassesRule()
+ .addKeepRules(keepRules)
+ .run(CLASS)
+ .inspector()
+ .clazz(CLASS)
+ .mainMethod();
}
-
}