Merge "Run Android N tests with D8 and R8"
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidNTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidNTest.java
new file mode 100644
index 0000000..7fb4bcf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidNTest.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2017, 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.errors.CompilationError;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.function.UnaryOperator;
+
+public class D8RunExamplesAndroidNTest extends RunExamplesAndroidNTest<D8Command.Builder> {
+
+  class D8TestRunner extends TestRunner {
+
+    D8TestRunner(String testName, String packageName, String mainClass) {
+      super(testName, packageName, mainClass);
+    }
+
+    @Override
+    TestRunner withMinApiLevel(int minApiLevel) {
+      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+    }
+
+    @Override
+    void build(Path inputFile, Path out) throws Throwable {
+      D8Command.Builder builder = D8Command.builder();
+      for (UnaryOperator<Builder> transformation : builderTransformations) {
+        builder = transformation.apply(builder);
+      }
+      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+      D8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
+      try {
+        ToolHelper.runD8(command, this::combinedOptionConsumer);
+      } catch (RuntimeException re) {
+        throw re instanceof CompilationError ? re : re.getCause();
+      }
+    }
+  }
+
+  @Override
+  TestRunner test(String testName, String packageName, String mainClass) {
+    return new D8TestRunner(testName, packageName, mainClass);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java
index c4b13d9..13c38ca 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java
@@ -4,144 +4,40 @@
 
 package com.android.tools.r8;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.OffOrAuto;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import java.util.Map;
 import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
+import java.util.function.UnaryOperator;
 
-public class R8RunExamplesAndroidNTest {
+public class R8RunExamplesAndroidNTest extends RunExamplesAndroidNTest<R8Command.Builder> {
 
-  private static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR;
+  class R8TestRunner extends TestRunner {
 
-  private static Map<DexVm, List<String>> failsOn =
-      ImmutableMap.of(
-          DexVm.ART_4_4_4,
-          ImmutableList.of(),
-          DexVm.ART_5_1_1,
-          ImmutableList.of(),
-          DexVm.ART_6_0_1,
-          ImmutableList.of(),
-          DexVm.ART_7_0_0,
-          ImmutableList.of(),
-          DexVm.ART_DEFAULT,
-          ImmutableList.of());
-
-  @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
-  @Rule public ExpectedException thrown = ExpectedException.none();
-
-  @Test
-  public void staticInterfaceMethods() throws Throwable {
-    doTest(
-        "staticinterfacemethods",
-        "interfacemethods",
-        "StaticInterfaceMethods",
-        Constants.ANDROID_K_API,
-        options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
-  }
-
-  @Test
-  public void staticInterfaceMethodsErrorDueToMinSdk() throws Throwable {
-    thrown.expect(CompilationError.class);
-    doTest(
-        "staticinterfacemethods-error-due-to-min-sdk",
-        "interfacemethods",
-        "StaticInterfaceMethods");
-  }
-
-  @Test
-  public void defaultMethods() throws Throwable {
-    doTest(
-        "defaultmethods",
-        "interfacemethods",
-        "DefaultMethods",
-        Constants.ANDROID_K_API,
-        options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
-  }
-
-  @Test
-  public void defaultMethodsErrorDueToMinSdk() throws Throwable {
-    thrown.expect(CompilationError.class);
-    doTest("defaultmethods-error-due-to-min-sdk", "interfacemethods", "DefaultMethods");
-  }
-
-  private void doTest(String testName, String packageName, String className) throws Throwable {
-    doTest(testName, packageName, className, R8Command.builder(), options -> {});
-  }
-
-  private void doTest(
-      String testName,
-      String packageName,
-      String className,
-      int minSdk,
-      Consumer<InternalOptions> optionsConsumer)
-      throws Throwable {
-    doTest(
-        testName,
-        packageName,
-        className,
-        R8Command.builder().setMinApiLevel(minSdk),
-        optionsConsumer);
-  }
-
-  public void doTest(
-      String testName,
-      String packageName,
-      String className,
-      R8Command.Builder builder,
-      Consumer<InternalOptions> optionsConsumer)
-      throws Throwable {
-    String mainClass = packageName + "." + className;
-    Path inputFile = Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
-    Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
-
-    try {
-      ToolHelper.runR8(
-          builder.addProgramFiles(inputFile).setOutputPath(out).build(), optionsConsumer);
-    } catch (ExecutionException e) {
-      throw e.getCause();
+    R8TestRunner(String testName, String packageName, String mainClass) {
+      super(testName, packageName, mainClass);
     }
 
-    if (!ToolHelper.artSupported()) {
-      return;
+    @Override
+    TestRunner withMinApiLevel(int minApiLevel) {
+      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
     }
 
-    boolean expectedToFail = false;
-    if (failsOn.containsKey(ToolHelper.getDexVm())
-        && failsOn.get(ToolHelper.getDexVm()).contains(testName)) {
-      expectedToFail = true;
-      thrown.expect(Throwable.class);
+    @Override
+    void build(Path inputFile, Path out) throws Throwable {
+      try {
+        R8Command.Builder builder = R8Command.builder();
+        for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
+          builder = transformation.apply(builder);
+        }
+        R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
+        ToolHelper.runR8(command, this::combinedOptionConsumer);
+      } catch (ExecutionException e) {
+        throw e.getCause();
+      }
     }
-    String output = ToolHelper.runArtNoVerificationErrors(out.toString(), mainClass);
-    if (!expectedToFail) {
-      ProcessResult javaResult =
-          ToolHelper.runJava(ImmutableList.of(inputFile.toString()), mainClass);
-      assertEquals("JVM run failed", javaResult.exitCode, 0);
-      assertTrue(
-          "JVM output does not match art output.\n\tjvm: "
-              + javaResult.stdout
-              + "\n\tart: "
-              + output,
-          output.equals(javaResult.stdout));
-    }
+  }
+
+  @Override
+  TestRunner test(String testName, String packageName, String mainClass) {
+    return new R8TestRunner(testName, packageName, mainClass);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
new file mode 100644
index 0000000..cb28cc4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2017, 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.JAR_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OffOrAuto;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.function.UnaryOperator;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public abstract class RunExamplesAndroidNTest<B> {
+
+  private static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR;
+
+  abstract class TestRunner {
+    final String testName;
+    final String packageName;
+    final String mainClass;
+
+    final List<Consumer<InternalOptions>> optionConsumers = new ArrayList<>();
+    final List<UnaryOperator<B>> builderTransformations = new ArrayList<>();
+
+    TestRunner(String testName, String packageName, String mainClass) {
+      this.testName = testName;
+      this.packageName = packageName;
+      this.mainClass = mainClass;
+    }
+
+    TestRunner withOptionConsumer(Consumer<InternalOptions> consumer) {
+      optionConsumers.add(consumer);
+      return this;
+    }
+
+    TestRunner withInterfaceMethodDesugaring(OffOrAuto behavior) {
+      return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
+    }
+
+    TestRunner withBuilderTransformation(UnaryOperator<B> builderTransformation) {
+      builderTransformations.add(builderTransformation);
+      return this;
+    }
+
+    void combinedOptionConsumer(InternalOptions options) {
+      for (Consumer<InternalOptions> consumer : optionConsumers) {
+        consumer.accept(options);
+      }
+    }
+
+    void run() throws Throwable {
+      String qualifiedMainClass = packageName + "." + mainClass;
+      Path inputFile = Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+      Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
+
+      build(inputFile, out);
+
+      if (!ToolHelper.artSupported()) {
+        return;
+      }
+
+      boolean expectedToFail = false;
+      if (failsOn.containsKey(ToolHelper.getDexVm())
+          && failsOn.get(ToolHelper.getDexVm()).contains(testName)) {
+        expectedToFail = true;
+        thrown.expect(Throwable.class);
+      }
+      String output = ToolHelper.runArtNoVerificationErrors(out.toString(), qualifiedMainClass);
+      if (!expectedToFail) {
+        ProcessResult javaResult =
+            ToolHelper.runJava(ImmutableList.of(inputFile.toString()), qualifiedMainClass);
+        assertEquals("JVM run failed", javaResult.exitCode, 0);
+        assertTrue(
+            "JVM output does not match art output.\n\tjvm: "
+                + javaResult.stdout
+                + "\n\tart: "
+                + output,
+            output.equals(javaResult.stdout));
+      }
+    }
+
+    abstract TestRunner withMinApiLevel(int minApiLevel);
+
+    abstract void build(Path inputFile, Path out) throws Throwable;
+  }
+
+  private static Map<DexVm, List<String>> failsOn =
+      ImmutableMap.of(
+          DexVm.ART_4_4_4,
+          ImmutableList.of(),
+          DexVm.ART_5_1_1,
+          ImmutableList.of(),
+          DexVm.ART_6_0_1,
+          ImmutableList.of(),
+          DexVm.ART_7_0_0,
+          ImmutableList.of(),
+          DexVm.ART_DEFAULT,
+          ImmutableList.of());
+
+  @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Rule public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void staticInterfaceMethods() throws Throwable {
+    test("staticinterfacemethods", "interfacemethods", "StaticInterfaceMethods")
+        .withMinApiLevel(Constants.ANDROID_K_API)
+        .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+        .run();
+  }
+
+  @Test
+  public void staticInterfaceMethodsErrorDueToMinSdk() throws Throwable {
+    thrown.expect(CompilationError.class);
+    test("staticinterfacemethods-error-due-to-min-sdk", "interfacemethods",
+        "StaticInterfaceMethods")
+        .run();
+  }
+
+  @Test
+  public void defaultMethods() throws Throwable {
+    test("defaultmethods", "interfacemethods", "DefaultMethods")
+        .withMinApiLevel(Constants.ANDROID_K_API)
+        .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+        .run();
+  }
+
+  @Test
+  public void defaultMethodsErrorDueToMinSdk() throws Throwable {
+    thrown.expect(CompilationError.class);
+    test("defaultmethods-error-due-to-min-sdk", "interfacemethods",
+        "DefaultMethods")
+        .run();
+  }
+
+  abstract TestRunner test(String testName, String packageName, String mainClass);
+}