Merge "Force IntelliJ IDEA build directories"
diff --git a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
index 3376cb8..07a4463 100644
--- a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
@@ -3,8 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import org.junit.Assume;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -17,15 +21,24 @@
   public static final String CLASS = "BlockReordering";
   public static final String FILE = "BlockReordering.java";
 
-  private static DebuggeePath debuggeePath;
+  private static DebugTestConfig dexConfig;
 
   @BeforeClass
-  public static void initDebuggeePath() throws Exception {
+  public static void setup() throws Exception {
     // Force inversion of all conditionals to reliably construct a regression test for incorrect
     // line information when reordering blocks.
-    debuggeePath =
-        DebuggeePath.makeDex(
-            compileToDex(DEBUGGEE_JAR, options -> options.testing.invertConditionals = true));
+    Path result = temp.newFolder().toPath().resolve("inverted_conditionals.jar");
+    int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    ToolHelper.runD8(
+        D8Command.builder()
+            .addProgramFiles(DEBUGGEE_JAR)
+            .setOutputPath(result)
+            .setMinApiLevel(minSdk)
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+            .build(),
+        options -> options.testing.invertConditionals = true);
+    dexConfig = new D8BaseDebugTestConfig(temp, result);
   }
 
   @Test
@@ -35,7 +48,7 @@
         ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1));
     final String method = "conditionalReturn";
     runDebugTest(
-        debuggeePath,
+        dexConfig,
         CLASS,
         breakpoint(CLASS, method),
         run(),
@@ -58,7 +71,7 @@
         ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1));
     final String method = "invertConditionalReturn";
     runDebugTest(
-        debuggeePath,
+        dexConfig,
         CLASS,
         breakpoint(CLASS, method),
         run(),
@@ -81,7 +94,7 @@
         ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1));
     final String method = "fallthroughReturn";
     runDebugTest(
-        debuggeePath,
+        dexConfig,
         CLASS,
         breakpoint(CLASS, method),
         run(),
diff --git a/src/test/java/com/android/tools/r8/debug/CfBaseDebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/CfBaseDebugTestConfig.java
new file mode 100644
index 0000000..ec6038c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/CfBaseDebugTestConfig.java
@@ -0,0 +1,30 @@
+// 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.debug;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.List;
+
+public class CfBaseDebugTestConfig extends DebugTestConfig {
+
+  public static final Path JDWP_JAR = ToolHelper.getJdwpTestsJarPath(AndroidApiLevel.N.getLevel());
+
+  @Override
+  public RuntimeKind getRuntimeKind() {
+    return RuntimeKind.CF;
+  }
+
+  @Override
+  public List<Path> getPaths() {
+    return ImmutableList.of(JDWP_JAR);
+  }
+
+  @Override
+  public Path getProguardMap() {
+    return null;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/D8BaseDebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/D8BaseDebugTestConfig.java
new file mode 100644
index 0000000..10c6191
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/D8BaseDebugTestConfig.java
@@ -0,0 +1,95 @@
+// 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.debug;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.OutputMode;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.rules.TemporaryFolder;
+
+public class D8BaseDebugTestConfig extends DebugTestConfig {
+
+  public static final Path JDWP_JAR =
+      ToolHelper.getJdwpTestsJarPath(ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()));
+
+  // Internal cache to avoid multiple compilations of the base JDWP code.
+  private static AndroidApp compiledJdwp = null;
+
+  private static synchronized AndroidApp getCompiledJdwp() {
+    if (compiledJdwp == null) {
+      int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+      try {
+        compiledJdwp =
+            ToolHelper.runD8(
+                D8Command.builder()
+                    .addProgramFiles(JDWP_JAR)
+                    .setMinApiLevel(minSdk)
+                    .setMode(CompilationMode.DEBUG)
+                    .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+                    .build());
+      } catch (Throwable e) {
+        throw new RuntimeException(e);
+      }
+    }
+    return compiledJdwp;
+  }
+
+  private final List<Path> paths = new ArrayList<>();
+
+  private Path proguardMap = null;
+
+  public D8BaseDebugTestConfig(TemporaryFolder temp) {
+    this(temp, ImmutableList.of());
+  }
+
+  public D8BaseDebugTestConfig(TemporaryFolder temp, Path... paths) {
+    this(temp, Arrays.asList(paths));
+  }
+
+  public D8BaseDebugTestConfig(TemporaryFolder temp, List<Path> paths) {
+    addPaths(paths);
+    try {
+      Path out = temp.newFolder().toPath().resolve("d8_jdwp.jar");
+      getCompiledJdwp().write(out, OutputMode.Indexed);
+      addPaths(out);
+    } catch (Throwable e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public RuntimeKind getRuntimeKind() {
+    return RuntimeKind.DEX;
+  }
+
+  @Override
+  public List<Path> getPaths() {
+    return paths;
+  }
+
+  @Override
+  public Path getProguardMap() {
+    return proguardMap;
+  }
+
+  public void addPaths(Path... paths) {
+    addPaths(Arrays.asList(paths));
+  }
+
+  public void addPaths(List<Path> paths) {
+    this.paths.addAll(paths);
+  }
+
+  public void setProguardMap(Path proguardMap) {
+    this.proguardMap = proguardMap;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/D8DebugTestResourcesConfig.java b/src/test/java/com/android/tools/r8/debug/D8DebugTestResourcesConfig.java
new file mode 100644
index 0000000..448949c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/D8DebugTestResourcesConfig.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.debug;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.OutputMode;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.rules.TemporaryFolder;
+
+// Shared test configuration for D8 compiled resources from the "debugTestResources" target.
+public class D8DebugTestResourcesConfig extends D8BaseDebugTestConfig {
+
+  private static AndroidApp compiledResources = null;
+
+  private static synchronized AndroidApp getCompiledResources() throws Throwable {
+    if (compiledResources == null) {
+      int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+      compiledResources =
+          ToolHelper.runD8(
+              D8Command.builder()
+                  .addProgramFiles(DebugTestBase.DEBUGGEE_JAR)
+                  .setMinApiLevel(minSdk)
+                  .setMode(CompilationMode.DEBUG)
+                  .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+                  .build(),
+              null);
+    }
+    return compiledResources;
+  }
+
+  public D8DebugTestResourcesConfig(TemporaryFolder temp) {
+    super(temp);
+    try {
+      Path path = temp.newFolder().toPath().resolve("d8_debug_test_resources.jar");
+      getCompiledResources().write(path, OutputMode.Indexed);
+      addPaths(path);
+    } catch (Throwable e) {
+      throw new RuntimeException(e);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 510aea1..692dd13 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -11,8 +11,7 @@
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.debug.DebugTestConfig.RuntimeKind;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.ClassNamingForNameMapper;
 import com.android.tools.r8.naming.MemberNaming;
@@ -31,7 +30,6 @@
 import it.unimi.dsi.fastutil.longs.LongList;
 import java.io.File;
 import java.io.IOException;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayDeque;
@@ -74,7 +72,6 @@
 import org.apache.harmony.jpda.tests.share.JPDATestOptions;
 import org.junit.Assert;
 import org.junit.Assume;
-import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Ignore;
 import org.junit.Rule;
@@ -93,104 +90,50 @@
   public static final StepFilter INTELLIJ_FILTER = new StepFilter.IntelliJStepFilter();
   private static final StepFilter DEFAULT_FILTER = NO_FILTER;
 
-  enum DexCompilerKind {
-    DX,
-    D8
-  }
-
-  enum BinaryKind {
-    CF,
-    DEX
-  }
-
   protected static class DebuggeePath {
-    public final BinaryKind kind;
+    public final RuntimeKind kind;
     public final Path path;
 
     public static DebuggeePath makeDex(Path path) {
-      return new DebuggeePath(BinaryKind.DEX, path);
+      return new DebuggeePath(DebugTestConfig.RuntimeKind.DEX, path);
     }
 
     public static DebuggeePath makeClassFile(Path path) {
-      return new DebuggeePath(BinaryKind.CF, path);
+      return new DebuggeePath(DebugTestConfig.RuntimeKind.CF, path);
     }
 
-    public DebuggeePath(BinaryKind kind, Path path) {
+    public DebuggeePath(RuntimeKind kind, Path path) {
       this.kind = kind;
       this.path = path;
     }
   }
 
-  private static final DexCompilerKind DEX_COMPILER_KIND = DexCompilerKind.D8;
-
   private static final int FIRST_LINE = -1;
 
   // Set to true to enable verbose logs
   private static final boolean DEBUG_TESTS = false;
 
-  private static final Path JDWP_JAR = ToolHelper
-      .getJdwpTestsJarPath(ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()));
-  protected static final Path DEBUGGEE_JAR =
+  public static final Path DEBUGGEE_JAR =
       Paths.get(ToolHelper.BUILD_DIR, "test", "debug_test_resources.jar");
+
   private static final Path DEBUGGEE_JAVA8_JAR = Paths
       .get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_java8.jar");
-  private static final Path DEBUGGEE_KOTLIN_JAR = Paths
-      .get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_kotlin.jar");
   private static final String PROGUARD_MAP_FILENAME = "proguard.map";
 
   @ClassRule
-  public static TemporaryFolder temp = new TemporaryFolder();
-
-  // TODO(tamaskenez): Separate test setup from test runner.
-  private static Path jdwpDexD8;
-  private static Path debuggeeDexD8;
-  private static Path debuggeeJava8DexD8;
-  private static Path debuggeeKotlinDexD8;
+  public static TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   @Rule
   public TestName testName = new TestName();
 
-  @BeforeClass
-  public static void setUp() throws Exception {
-    jdwpDexD8 = compileToDex(JDWP_JAR, null);
-    debuggeeDexD8 = null;
-    debuggeeJava8DexD8 = null;
-    debuggeeKotlinDexD8 = null;
-  }
-
-  protected static synchronized Path getDebuggeeDexD8()
+  private static Path getDebuggeeJava8DexD8()
       throws IOException, CompilationException, CompilationFailedException {
-    if (debuggeeDexD8 == null) {
-      debuggeeDexD8 = compileToDex(DEBUGGEE_JAR, null);
-    }
-    return debuggeeDexD8;
-  }
-
-  private static synchronized Path getDebuggeeJava8DexD8()
-      throws IOException, CompilationException, CompilationFailedException {
-    if (debuggeeJava8DexD8 == null) {
-      debuggeeJava8DexD8 =
-          compileToDex(
-              DEBUGGEE_JAVA8_JAR,
-              options -> {
-                // Enable desugaring for preN runtimes
-                options.interfaceMethodDesugaring = OffOrAuto.Auto;
-              });
-    }
-    return debuggeeJava8DexD8;
-  }
-
-  private static synchronized Path getDebuggeeKotlinDexD8()
-      throws IOException, CompilationException, CompilationFailedException {
-    if (debuggeeKotlinDexD8 == null) {
-      debuggeeKotlinDexD8 = compileToDex(DEBUGGEE_KOTLIN_JAR, null);
-    }
-    return debuggeeKotlinDexD8;
-  }
-
-  protected static DebuggeePath getDebuggeeDexD8OrCf(boolean cf)
-      throws IOException, CompilationException, CompilationFailedException {
-    return cf ? DebuggeePath.makeClassFile(DEBUGGEE_JAR) : DebuggeePath.makeDex(getDebuggeeDexD8());
+    return compileToDex(
+        DEBUGGEE_JAVA8_JAR,
+        options -> {
+          // Enable desugaring for preN runtimes
+          options.interfaceMethodDesugaring = OffOrAuto.Auto;
+        });
   }
 
   protected static DebuggeePath getDebuggeeJava8DexD8OrCf(boolean cf)
@@ -200,46 +143,20 @@
         : DebuggeePath.makeDex(getDebuggeeJava8DexD8());
   }
 
-  protected static Path compileToDex(Path jarToCompile, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException, CompilationFailedException {
-    return compileToDex(DEX_COMPILER_KIND, jarToCompile, optionsConsumer);
-  }
-
-  static Path compileToDex(
-      DexCompilerKind compiler, Path jarToCompile, Consumer<InternalOptions> optionsConsumer)
+  static Path compileToDex(Path jarToCompile, Consumer<InternalOptions> optionsConsumer)
       throws IOException, CompilationException, CompilationFailedException {
     int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
     assert jarToCompile.toFile().exists();
     Path dexOutputDir = temp.newFolder().toPath();
-    switch (compiler) {
-      case D8:
-        {
-          ToolHelper.runD8(
-              D8Command.builder()
-                  .addProgramFiles(jarToCompile)
-                  .setOutputPath(dexOutputDir)
-                  .setMinApiLevel(minSdk)
-                  .setMode(CompilationMode.DEBUG)
-                  .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
-                  .build(),
-              optionsConsumer);
-          break;
-        }
-      case DX:
-        {
-          ProcessResult result =
-              ToolHelper.runDX(
-                  new String[] {
-                    "--output=" + dexOutputDir,
-                    "--min-sdk-version=" + minSdk,
-                    jarToCompile.toString()
-                  });
-          Assert.assertEquals(result.stderr, 0, result.exitCode);
-          break;
-        }
-      default:
-        throw new Unreachable();
-    }
+    ToolHelper.runD8(
+        D8Command.builder()
+            .addProgramFiles(jarToCompile)
+            .setOutputPath(dexOutputDir)
+            .setMinApiLevel(minSdk)
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+            .build(),
+        optionsConsumer);
     return dexOutputDir.resolve("classes.dex");
   }
 
@@ -276,9 +193,9 @@
     return dexOutputDir.resolve("classes.dex");
   }
 
-  private BinaryKind currentlyRunningBinaryKind = null;
+  private RuntimeKind currentlyRunningBinaryKind = null;
 
-  protected final BinaryKind getCurrentlyRunningBinaryKind() {
+  protected final RuntimeKind getCurrentlyRunningBinaryKind() {
     if (currentlyRunningBinaryKind == null) {
       throw new RuntimeException("Nothing is running currently.");
     }
@@ -291,63 +208,42 @@
   }
 
   protected final boolean isRunningJava() {
-    return getCurrentlyRunningBinaryKind() == BinaryKind.CF;
+    return getCurrentlyRunningBinaryKind() == DebugTestConfig.RuntimeKind.CF;
   }
 
   protected final boolean isRunningArt() {
-    return getCurrentlyRunningBinaryKind() == BinaryKind.DEX;
+    return getCurrentlyRunningBinaryKind() == DebugTestConfig.RuntimeKind.DEX;
+  }
+
+  protected final void runDebugTest(
+      DebugTestConfig config, String debuggeeClass, JUnit3Wrapper.Command... commands)
+      throws Throwable {
+    runInternal(config, debuggeeClass, Arrays.asList(commands));
   }
 
   protected final void runDebugTest(String debuggeeClass, JUnit3Wrapper.Command... commands)
       throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeDexD8()),
-        Collections.<Path>emptyList(),
-        debuggeeClass,
-        Arrays.asList(commands));
-  }
-
-  protected final void runDebugTest(List<Path> extraPaths, String debuggeeClass,
-      JUnit3Wrapper.Command... commands) throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeDexD8()),
-        extraPaths,
-        debuggeeClass,
-        Arrays.asList(commands));
+    runDebugTest(debuggeeClass, Arrays.asList(commands));
   }
 
   protected final void runDebugTest(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
       throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeDexD8()),
-        Collections.<Path>emptyList(),
-        debuggeeClass,
-        commands);
+    runInternal(new D8DebugTestResourcesConfig(temp), debuggeeClass, commands);
   }
 
-  protected final void runDebugTestJava8(String debuggeeClass, JUnit3Wrapper.Command... commands)
+  protected final void runDebugTest(
+      List<Path> extraPaths, String debuggeeClass, JUnit3Wrapper.Command... commands)
       throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeJava8DexD8()),
-        Collections.<Path>emptyList(),
-        debuggeeClass,
-        Arrays.asList(commands));
-  }
-
-  protected final void runDebugTestJava8(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
-      throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeJava8DexD8()),
-        Collections.<Path>emptyList(),
-        debuggeeClass,
-        commands);
-  }
-
-  protected final void runDebugTestKotlin(String debuggeeClass, JUnit3Wrapper.Command... commands)
-      throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeKotlinDexD8()),
-        Collections.<Path>emptyList(),
+    runInternal(
+        new D8DebugTestResourcesConfig(temp) {
+          @Override
+          public List<Path> getPaths() {
+            return new ImmutableList.Builder<Path>()
+                .addAll(super.getPaths())
+                .addAll(extraPaths)
+                .build();
+          }
+        },
         debuggeeClass,
         Arrays.asList(commands));
   }
@@ -355,14 +251,13 @@
   protected void runDebugTest(
       DebuggeePath debuggeePath, String debuggeeClass, JUnit3Wrapper.Command... commands)
       throws Throwable {
-    runDebugTest(
-        debuggeePath, Collections.<Path>emptyList(), debuggeeClass, Arrays.asList(commands));
+    runDebugTest(debuggeePath, ImmutableList.of(), debuggeeClass, Arrays.asList(commands));
   }
 
   protected void runDebugTest(
       DebuggeePath debuggeePath, String debuggeeClass, List<JUnit3Wrapper.Command> commands)
       throws Throwable {
-    runDebugTest(debuggeePath, Collections.<Path>emptyList(), debuggeeClass, commands);
+    runDebugTest(debuggeePath, ImmutableList.of(), debuggeeClass, commands);
   }
 
   protected void runDebugTest(
@@ -380,6 +275,38 @@
       String debuggeeClass,
       List<JUnit3Wrapper.Command> commands)
       throws Throwable {
+    DebugTestConfig debuggeeConfig;
+    if (debuggeePath.kind == DebugTestConfig.RuntimeKind.CF) {
+      debuggeeConfig =
+          new CfBaseDebugTestConfig() {
+            @Override
+            public List<Path> getPaths() {
+              return new ImmutableList.Builder<Path>()
+                  .addAll(super.getPaths())
+                  .addAll(extraPaths)
+                  .add(debuggeePath.path)
+                  .build();
+            }
+          };
+    } else {
+      debuggeeConfig =
+          new D8BaseDebugTestConfig(temp) {
+            @Override
+            public List<Path> getPaths() {
+              return new ImmutableList.Builder<Path>()
+                  .addAll(super.getPaths())
+                  .addAll(extraPaths)
+                  .add(debuggeePath.path)
+                  .build();
+            }
+          };
+    }
+    runInternal(debuggeeConfig, debuggeeClass, commands);
+  }
+
+  private void runInternal(
+      DebugTestConfig config, String debuggeeClass, List<JUnit3Wrapper.Command> commands)
+      throws Throwable {
     // Skip test due to unsupported runtime.
     Assume.assumeTrue("Skipping test " + testName.getMethodName() + " because ART is not supported",
         ToolHelper.artSupported());
@@ -387,25 +314,19 @@
             + " because debug tests are not yet supported on Windows",
         !ToolHelper.isWindows());
 
-    String[] paths = new String[extraPaths.size() + 2];
-    int indexPath = 0;
-    ClassNameMapper classNameMapper = null;
-    if (debuggeePath.kind == BinaryKind.CF) {
-      paths[indexPath++] = JDWP_JAR.toString();
-    } else {
-      paths[indexPath++] = jdwpDexD8.toString();
-      Path proguardMapPath = debuggeePath.path.resolveSibling(PROGUARD_MAP_FILENAME);
-      if (Files.exists(proguardMapPath)) {
-        classNameMapper = ClassNameMapper.mapperFromFile(proguardMapPath);
-      }
-    }
-    paths[indexPath++] = debuggeePath.path.toString();
-    for (Path extraPath : extraPaths) {
-      paths[indexPath++] = extraPath.toString();
-    }
+    ClassNameMapper classNameMapper =
+        config.getProguardMap() == null
+            ? null
+            : ClassNameMapper.mapperFromFile(config.getProguardMap());
 
-    currentlyRunningBinaryKind = debuggeePath.kind;
-    new JUnit3Wrapper(debuggeeClass, paths, commands, classNameMapper, isRunningArt()).runBare();
+    currentlyRunningBinaryKind = config.getRuntimeKind();
+    new JUnit3Wrapper(
+            debuggeeClass,
+            config.getPaths().stream().map(Path::toString).toArray(String[]::new),
+            commands,
+            classNameMapper,
+            isRunningArt())
+        .runBare();
   }
 
   protected final JUnit3Wrapper.Command run() {
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/DebugTestConfig.java
new file mode 100644
index 0000000..32df428
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestConfig.java
@@ -0,0 +1,29 @@
+// 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.debug;
+
+import java.nio.file.Path;
+import java.util.List;
+
+public abstract class DebugTestConfig {
+
+  public enum RuntimeKind {
+    CF,
+    DEX
+  }
+
+  public abstract RuntimeKind getRuntimeKind();
+
+  public abstract List<Path> getPaths();
+
+  public abstract Path getProguardMap();
+
+  public boolean isCfRuntime() {
+    return getRuntimeKind() == RuntimeKind.CF;
+  }
+
+  public boolean isDexRuntime() {
+    return getRuntimeKind() == RuntimeKind.DEX;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
index 6f9c726..84af8f9 100644
--- a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -13,6 +15,7 @@
 import java.io.StringReader;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
 import org.junit.Rule;
@@ -22,7 +25,48 @@
 // TODO(b/65474850) Should we build Jasmin at compile time or runtime ?
 public class JasminDebugTest extends DebugTestBase {
 
-  public static final boolean RUN_JAVA = false;
+  static class JasminTestConfig extends D8BaseDebugTestConfig {
+
+    public JasminTestConfig(TemporaryFolder temp, JasminBuilder builder) {
+      super(temp, compile(temp, builder));
+    }
+
+    private static Path compile(TemporaryFolder temp, JasminBuilder builder) {
+      try {
+        ImmutableList<ClassBuilder> classes = builder.getClasses();
+        File out = temp.newFolder();
+        List<Path> classFiles = new ArrayList<>(classes.size());
+        for (ClassBuilder clazz : classes) {
+          ClassFile file = new ClassFile();
+          file.readJasmin(new StringReader(clazz.toString()), clazz.name, false);
+          Path path = out.toPath().resolve(clazz.name + ".class");
+          Files.createDirectories(path.getParent());
+          try (OutputStream outputStream = Files.newOutputStream(path)) {
+            file.write(outputStream);
+          }
+          classFiles.add(path);
+        }
+        return compile(temp, classFiles);
+      } catch (Throwable e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    private static Path compile(TemporaryFolder temp, List<Path> paths) throws Throwable {
+      int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+      Path out = temp.newFolder().toPath().resolve("d8_jasmin.jar");
+      ToolHelper.runD8(
+          D8Command.builder()
+              .addProgramFiles(paths)
+              .setOutputPath(out)
+              .setMinApiLevel(minSdk)
+              .setMode(CompilationMode.DEBUG)
+              .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+              .build(),
+          null);
+      return out;
+    }
+  }
 
   @Rule
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@@ -32,10 +76,8 @@
     final String className = "UselessCheckCast";
     final String sourcefile = className + ".j";
     final String methodName = "test";
-    List<Path> paths = getExtraPaths(getBuilderForUselessCheckcast(className, methodName));
     runDebugTest(
-        getDebuggeeDexD8OrCf(RUN_JAVA),
-        paths,
+        new JasminTestConfig(temp, getBuilderForUselessCheckcast(className, methodName)),
         className,
         breakpoint(className, methodName),
         run(),
@@ -81,27 +123,4 @@
 
     return builder;
   }
-
-  private List<Path> getExtraPaths(JasminBuilder builder) throws Exception {
-    ImmutableList<ClassBuilder> classes = builder.getClasses();
-    List<Path> extraPaths = new ArrayList<>(classes.size());
-    File out = temp.newFolder();
-
-    for (ClassBuilder clazz : classes) {
-      ClassFile file = new ClassFile();
-      file.readJasmin(new StringReader(clazz.toString()), clazz.name, false);
-      Path path = out.toPath().resolve(clazz.name + ".class");
-      Files.createDirectories(path.getParent());
-      try (OutputStream outputStream = Files.newOutputStream(path)) {
-        file.write(outputStream);
-      }
-      if (RUN_JAVA) {
-        extraPaths.add(path);
-      } else {
-        extraPaths.add(compileToDex(path, null));
-      }
-    }
-
-    return extraPaths;
-  }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java b/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java
index a0a9c3f..50da158 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java
@@ -4,16 +4,70 @@
 
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.OutputMode;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.List;
 import org.apache.harmony.jpda.tests.framework.jdwp.Frame.Variable;
 import org.apache.harmony.jpda.tests.framework.jdwp.Location;
+import org.junit.BeforeClass;
+import org.junit.rules.TemporaryFolder;
 
 /**
  * A specialization for Kotlin-based tests which provides extra commands.
  */
 public abstract class KotlinDebugTestBase extends DebugTestBase {
 
+  private static final Path DEBUGGEE_KOTLIN_JAR =
+      Paths.get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_kotlin.jar");
+
+  protected static class KotlinD8Config extends D8BaseDebugTestConfig {
+
+    private static AndroidApp compiledResources = null;
+
+    private static synchronized AndroidApp getCompiledResources() throws Throwable {
+      if (compiledResources == null) {
+        int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+        compiledResources =
+            ToolHelper.runD8(
+                D8Command.builder()
+                    .addProgramFiles(DEBUGGEE_KOTLIN_JAR)
+                    .setMinApiLevel(minSdk)
+                    .setMode(CompilationMode.DEBUG)
+                    .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+                    .build());
+      }
+      return compiledResources;
+    }
+
+    public KotlinD8Config(TemporaryFolder temp) {
+      super(temp);
+      try {
+        Path out = temp.newFolder().toPath().resolve("d8_debug_test_resources_kotlin.jar");
+        getCompiledResources().write(out, OutputMode.Indexed);
+        addPaths(out);
+      } catch (Throwable e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  private static KotlinD8Config d8Config;
+
+  @BeforeClass
+  public static void setup() {
+    d8Config = new KotlinD8Config(temp);
+  }
+
+  protected KotlinD8Config getD8Config() {
+    return d8Config;
+  }
+
   protected final JUnit3Wrapper.Command kotlinStepOver() {
     return testBaseBeforeStep -> {
       final JUnit3Wrapper.DebuggeeState debuggeeStateBeforeStep = testBaseBeforeStep
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
index 8e9d145..cdba21b 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -7,7 +7,6 @@
 import static org.junit.Assert.assertTrue;
 
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
-import org.junit.Ignore;
 import org.junit.Test;
 
 // TODO check double-depth inline (an inline in another inline)
@@ -16,7 +15,9 @@
   @Test
   public void testStepOverInline() throws Throwable {
     String methodName = "singleInline";
-    runDebugTestKotlin("KotlinInline",
+    runDebugTest(
+        getD8Config(),
+        "KotlinInline",
         breakpoint("KotlinInline", methodName),
         run(),
         inspect(s -> {
@@ -48,7 +49,9 @@
   @Test
   public void testStepIntoInline() throws Throwable {
     String methodName = "singleInline";
-    runDebugTestKotlin("KotlinInline",
+    runDebugTest(
+        getD8Config(),
+        "KotlinInline",
         breakpoint("KotlinInline", methodName),
         run(),
         inspect(s -> {
@@ -84,7 +87,9 @@
   @Test
   public void testStepOutInline() throws Throwable {
     String methodName = "singleInline";
-    runDebugTestKotlin("KotlinInline",
+    runDebugTest(
+        getD8Config(),
+        "KotlinInline",
         breakpoint("KotlinInline", methodName),
         run(),
         inspect(s -> {
@@ -121,7 +126,9 @@
   @Test
   public void testKotlinInline() throws Throwable {
     final String inliningMethodName = "invokeInlinedFunctions";
-    runDebugTestKotlin("KotlinInline",
+    runDebugTest(
+        getD8Config(),
+        "KotlinInline",
         breakpoint("KotlinInline", inliningMethodName),
         run(),
         inspect(s -> {
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinTest.java b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
index 2baa426..5623d6d 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
@@ -14,7 +14,9 @@
   // TODO(shertz) add more variables ?
   @Test
   public void testStepOver() throws Throwable {
-    runDebugTestKotlin("KotlinApp",
+    runDebugTest(
+        getD8Config(),
+        "KotlinApp",
         breakpoint("KotlinApp$Companion", "main"),
         run(),
         inspect(s -> {
@@ -44,7 +46,9 @@
 
   @Test
   public void testStepIntoAndOut() throws Throwable {
-    runDebugTestKotlin("KotlinApp",
+    runDebugTest(
+        getD8Config(),
+        "KotlinApp",
         breakpoint("KotlinApp$Companion", "main"),
         run(),
         inspect(s -> {
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index 76f8870..f240875 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -12,7 +12,6 @@
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.Tag;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -20,8 +19,6 @@
  */
 public class LocalsTest extends DebugTestBase {
 
-  private static final boolean RUN_JAVA = false;
-
   public static final String SOURCE_FILE = "Locals.java";
 
   @Test
@@ -726,6 +723,6 @@
     commands.add(checkLocal("i", Value.createInt(0)));
     commands.add(run());
 
-    runDebugTest(getDebuggeeDexD8OrCf(RUN_JAVA), className, commands);
+    runDebugTest(className, commands);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/MinificationTest.java b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
index 1e439c7..3b1985a 100644
--- a/src/test/java/com/android/tools/r8/debug/MinificationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
@@ -3,20 +3,20 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase.MinifyMode;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.utils.CompilationFailedException;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.Origin;
 import com.google.common.collect.ImmutableList;
-import java.io.IOException;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.concurrent.ExecutionException;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
@@ -24,22 +24,7 @@
 @RunWith(Parameterized.class)
 public class MinificationTest extends DebugTestBase {
 
-  public static final String SOURCE_FILE = "Minified.java";
-  private static final HashMap<Config, Path> debuggeePathMap = new HashMap<>();
-
-  private static class Config {
-    public final MinifyMode minificationMode;
-    public final boolean writeProguardMap;
-
-    Config(MinifyMode minificationMode, boolean writeProguardMap) {
-      this.minificationMode = minificationMode;
-      this.writeProguardMap = writeProguardMap;
-    }
-
-    public boolean minifiedNames() {
-      return minificationMode.isMinify() && !writeProguardMap;
-    }
-  }
+  private static final String SOURCE_FILE = "Minified.java";
 
   @Parameterized.Parameters(name = "minification: {0}, proguardMap: {1}")
   public static Collection minificationControl() {
@@ -53,51 +38,68 @@
     return builder.build();
   }
 
-  private final Config config;
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
-  private synchronized DebuggeePath getDebuggeePath()
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException,
-      CompilationFailedException {
-    Path path = debuggeePathMap.get(config);
-    if (path == null) {
-      List<String> proguardConfigurations = Collections.<String>emptyList();
-      if (config.minificationMode.isMinify()) {
-        ImmutableList.Builder<String> builder = ImmutableList.builder();
-        builder.add("-keep public class Minified { public static void main(java.lang.String[]); }");
-        builder.add("-keepattributes SourceFile");
-        builder.add("-keepattributes LineNumberTable");
-        if (config.minificationMode == MinifyMode.AGGRESSIVE) {
-          builder.add("-overloadaggressively");
-        }
-        proguardConfigurations = builder.build();
-      }
-      path =
-          compileToDexViaR8(
-              null,
-              null,
-              DEBUGGEE_JAR,
-              proguardConfigurations,
-              config.writeProguardMap,
-              CompilationMode.DEBUG);
-      debuggeePathMap.put(config, path);
-    }
-    return DebuggeePath.makeDex(path);
-  }
+  private final MinifyMode minificationMode;
+  private final boolean writeProguardMap;
 
   public MinificationTest(MinifyMode minificationMode, boolean writeProguardMap) throws Exception {
-    config = new Config(minificationMode, writeProguardMap);
+    this.minificationMode = minificationMode;
+    this.writeProguardMap = writeProguardMap;
+  }
+
+  private boolean minifiedNames() {
+    return minificationMode.isMinify() && !writeProguardMap;
+  }
+
+  private DebugTestConfig getTestConfig() throws Throwable {
+    List<String> proguardConfigurations = Collections.emptyList();
+    if (minificationMode.isMinify()) {
+      ImmutableList.Builder<String> builder = ImmutableList.builder();
+      builder.add("-keep public class Minified { public static void main(java.lang.String[]); }");
+      builder.add("-keepattributes SourceFile");
+      builder.add("-keepattributes LineNumberTable");
+      if (minificationMode == MinifyMode.AGGRESSIVE) {
+        builder.add("-overloadaggressively");
+      }
+      proguardConfigurations = builder.build();
+    }
+
+    int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    Path dexOutputDir = temp.newFolder().toPath();
+    Path proguardMap = writeProguardMap ? dexOutputDir.resolve("proguard.map") : null;
+    R8Command.Builder builder =
+        R8Command.builder()
+            .addProgramFiles(DEBUGGEE_JAR)
+            .setOutputPath(dexOutputDir)
+            .setMinApiLevel(minSdk)
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)));
+    if (proguardMap != null) {
+      builder.setProguardMapOutput(proguardMap);
+    }
+    if (!proguardConfigurations.isEmpty()) {
+      builder.addProguardConfiguration(proguardConfigurations, Origin.unknown());
+    }
+    ToolHelper.runR8(builder.build());
+
+    D8BaseDebugTestConfig config =
+        new D8BaseDebugTestConfig(temp, dexOutputDir.resolve("classes.dex"));
+    config.setProguardMap(proguardMap);
+    return config;
   }
 
   @Test
   public void testBreakInMainClass() throws Throwable {
     final String className = "Minified";
-    final String methodName = config.minifiedNames() ? "a" : "test";
+    final String methodName = minifiedNames() ? "a" : "test";
     final String signature = "()V";
-    final String innerClassName = config.minifiedNames() ? "a" : "Minified$Inner";
-    final String innerMethodName = config.minifiedNames() ? "a" : "innerTest";
+    final String innerClassName = minifiedNames() ? "a" : "Minified$Inner";
+    final String innerMethodName = minifiedNames() ? "a" : "innerTest";
     final String innerSignature = "()I";
     runDebugTest(
-        getDebuggeePath(),
+        getTestConfig(),
         className,
         breakpoint(className, methodName, signature),
         run(),
@@ -115,11 +117,11 @@
   @Test
   public void testBreakInPossiblyRenamedClass() throws Throwable {
     final String className = "Minified";
-    final String innerClassName = config.minifiedNames() ? "a" : "Minified$Inner";
-    final String innerMethodName = config.minifiedNames() ? "a" : "innerTest";
+    final String innerClassName = minifiedNames() ? "a" : "Minified$Inner";
+    final String innerMethodName = minifiedNames() ? "a" : "innerTest";
     final String innerSignature = "()I";
     runDebugTest(
-        getDebuggeePath(),
+        getTestConfig(),
         className,
         breakpoint(innerClassName, innerMethodName, innerSignature),
         run(),
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
index 36f1b29..3e380fe 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
@@ -28,7 +28,7 @@
       Paths.get(ToolHelper.BUILD_DIR, "test", "debuginfo_examples_dex.jar");
 
   @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   static AndroidApp compileWithD8(Class... classes) throws CompilationException, IOException,
       CompilationFailedException {
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
index dfb3fd1..4b011a2 100644
--- a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -232,7 +232,7 @@
   private static File d8Out = null;
 
   @ClassRule
-  public static TemporaryFolder temp = new TemporaryFolder();
+  public static TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   @BeforeClass
   public static void compileLibraries() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 0fe9e75..1c55379 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -94,7 +94,8 @@
   private static List<String> MANY_CLASSES;
 
   @ClassRule
-  public static TemporaryFolder generatedApplicationsFolder = new TemporaryFolder();
+  public static TemporaryFolder generatedApplicationsFolder =
+      ToolHelper.getTemporaryFolderForTest();
 
   // Generate the test applications in a @BeforeClass method, as they are used by several tests.
   @BeforeClass
diff --git a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
index 41370d4..3b0a933 100644
--- a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
@@ -48,7 +48,7 @@
       Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "applymapping044" + FileUtils.JAR_EXTENSION);
 
   @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   private Path out;