Replace static runtime fields by getter functions.

Change-Id: I5a414ba99edafac147156dc961840fd3f0fd649b
diff --git a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
index 1754bff..1defe48 100644
--- a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
+++ b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
@@ -6,6 +6,7 @@
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.List;
@@ -27,21 +28,21 @@
 
   @Parameters(name = "{0}, library: {1}")
   public static List<Object[]> data() {
-    return buildParameters(getTestParameters().withAllRuntimes().build(), CfVm.values());
+    return buildParameters(
+        getTestParameters().withAllRuntimes().build(), TestRuntime.getCheckedInCfRuntimes());
   }
 
   private final TestParameters parameters;
-  private final CfVm library;
+  private final CfRuntime library;
 
-  public CompileWithJdkClassFileProviderTest(TestParameters parameters, CfVm library) {
+  public CompileWithJdkClassFileProviderTest(TestParameters parameters, CfRuntime library) {
     this.parameters = parameters;
     this.library = library;
   }
 
   @Test
   public void compileSimpleCodeWithJdkLibrary() throws Exception {
-    ClassFileResourceProvider provider =
-        JdkClassFileProvider.fromJdkHome(TestRuntime.getCheckedInJDKHome(library));
+    ClassFileResourceProvider provider = JdkClassFileProvider.fromJdkHome(library.getJavaHome());
 
     testForR8(parameters.getBackend())
         .addLibraryProvider(provider)
@@ -58,7 +59,7 @@
   @Test
   public void compileSimpleCodeWithSystemJdk() throws Exception {
     // Don't run duplicate tests (library is not used by the test).
-    assumeTrue(library == CfVm.JDK8);
+    assumeTrue(library.getVm() == CfVm.JDK8);
 
     ClassFileResourceProvider provider = JdkClassFileProvider.fromSystemJdk();
 
@@ -76,8 +77,7 @@
 
   @Test
   public void compileCodeWithJava9APIUsage() throws Exception {
-    ClassFileResourceProvider provider =
-        JdkClassFileProvider.fromJdkHome(TestRuntime.getCheckedInJDKHome(library));
+    ClassFileResourceProvider provider = JdkClassFileProvider.fromJdkHome(library.getJavaHome());
 
     TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder =
         testForR8(parameters.getBackend())
@@ -85,7 +85,7 @@
             .addProgramClassFileData(dumpClassWhichUseJava9Flow())
             .addKeepMainRule("MySubscriber");
 
-    if (library == CfVm.JDK8) {
+    if (library.getVm() == CfVm.JDK8) {
       try {
         // java.util.concurrent.Flow$Subscriber is not present in JDK8 rt.jar.
         testBuilder.compileWithExpectedDiagnostics(
diff --git a/src/test/java/com/android/tools/r8/JavaCompilerTool.java b/src/test/java/com/android/tools/r8/JavaCompilerTool.java
index d527670..c7f7652 100644
--- a/src/test/java/com/android/tools/r8/JavaCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/JavaCompilerTool.java
@@ -7,7 +7,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ZipUtils;
@@ -23,24 +23,24 @@
 
 public class JavaCompilerTool {
 
-  private final CfVm jdk;
+  private final CfRuntime jdk;
   private final TestState state;
   private final List<Path> sources = new ArrayList<>();
   private final List<Path> classpath = new ArrayList<>();
   private final List<String> options = new ArrayList<>();
   private Path output = null;
 
-  private JavaCompilerTool(CfVm jdk, TestState state) {
+  private JavaCompilerTool(CfRuntime jdk, TestState state) {
     this.jdk = jdk;
     this.state = state;
   }
 
-  public static JavaCompilerTool create(CfVm jdk, TemporaryFolder temp) {
+  public static JavaCompilerTool create(CfRuntime jdk, TemporaryFolder temp) {
     assert temp != null;
     return create(jdk, new TestState(temp));
   }
 
-  public static JavaCompilerTool create(CfVm jdk, TestState state) {
+  public static JavaCompilerTool create(CfRuntime jdk, TestState state) {
     assert state != null;
     return new JavaCompilerTool(jdk, state);
   }
@@ -106,7 +106,7 @@
   private ProcessResult compileInternal(Path output) throws IOException {
     Path outdir = Files.isDirectory(output) ? output : state.getNewTempFolder();
     List<String> cmdline = new ArrayList<>();
-    cmdline.add(ToolHelper.getJavaExecutable(jdk) + "c");
+    cmdline.add(jdk.getJavaExecutable() + "c");
     cmdline.addAll(options);
     if (!classpath.isEmpty()) {
       cmdline.add("-cp");
diff --git a/src/test/java/com/android/tools/r8/JdkClassFileProviderTest.java b/src/test/java/com/android/tools/r8/JdkClassFileProviderTest.java
index d6f38af..fbcc9d6 100644
--- a/src/test/java/com/android/tools/r8/JdkClassFileProviderTest.java
+++ b/src/test/java/com/android/tools/r8/JdkClassFileProviderTest.java
@@ -4,18 +4,40 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.TestRuntime.CfVm;
 import com.google.common.io.ByteStreams;
 import java.io.IOException;
 import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 import org.objectweb.asm.Opcodes;
 
+@RunWith(Parameterized.class)
 public class JdkClassFileProviderTest extends TestBase implements Opcodes {
 
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withCfRuntimes().build();
+  }
+
+  final TestParameters parameters;
+
+  public JdkClassFileProviderTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private CfRuntime getRuntime() {
+    return parameters.getRuntime().asCf();
+  }
+
   @Test
-  public void testInvalid8RuntimeClassPath() throws Exception {
+  public void testInvalidRuntimeClassPath() throws Exception {
     Path path = temp.newFolder().toPath();
     try {
       JdkClassFileProvider.fromJdkHome(path);
@@ -27,22 +49,23 @@
   }
 
   @Test
-  public void testJdk8JavHome() throws Exception {
+  public void testJdkJavaHome() throws Exception {
     ClassFileResourceProvider provider =
-        JdkClassFileProvider.fromJdkHome(ToolHelper.getJavaHome(TestRuntime.CfVm.JDK8));
+        JdkClassFileProvider.fromJdkHome(getRuntime().getJavaHome());
     assertJavaLangObject(provider);
     assert provider instanceof AutoCloseable;
+    if (getRuntime().isNewerThanOrEqual(CfVm.JDK9)) {
+      assertJavaUtilConcurrentFlowSubscriber(provider);
+    }
     ((AutoCloseable) provider).close();
   }
 
   @Test
   public void testJdk8RuntimeClassPath() throws Exception {
+    assumeTrue(getRuntime().getVm() == CfVm.JDK8);
     ClassFileResourceProvider provider =
         JdkClassFileProvider.fromJavaRuntimeJar(
-            ToolHelper.getJavaHome(TestRuntime.CfVm.JDK8)
-                .resolve("jre")
-                .resolve("lib")
-                .resolve("rt.jar"));
+            getRuntime().getJavaHome().resolve("jre").resolve("lib").resolve("rt.jar"));
     assertJavaLangObject(provider);
     assert provider instanceof AutoCloseable;
     ((AutoCloseable) provider).close();
@@ -50,8 +73,9 @@
 
   @Test
   public void testJdk8SystemModules() throws Exception {
+    assumeTrue(getRuntime().getVm() == CfVm.JDK8);
     try {
-      JdkClassFileProvider.fromSystemModulesJdk(ToolHelper.getJavaHome(TestRuntime.CfVm.JDK8));
+      JdkClassFileProvider.fromSystemModulesJdk(getRuntime().getJavaHome());
       fail("Not supposed to succeed");
     } catch (NoSuchFileException e) {
       assertThat(e.toString(), containsString("lib/jrt-fs.jar"));
@@ -59,39 +83,10 @@
   }
 
   @Test
-  public void testJdk9JavaHome() throws Exception {
+  public void testJdk9PlusSystemModules() throws Exception {
+    assumeTrue(getRuntime().isNewerThanOrEqual(CfVm.JDK9));
     ClassFileResourceProvider provider =
-        JdkClassFileProvider.fromJdkHome(ToolHelper.getJavaHome(TestRuntime.CfVm.JDK9));
-    assertJavaLangObject(provider);
-    assertJavaUtilConcurrentFlowSubscriber(provider);
-    assert provider instanceof AutoCloseable;
-    ((AutoCloseable) provider).close();
-  }
-
-  @Test
-  public void testJdk9SystemModules() throws Exception {
-    ClassFileResourceProvider provider =
-        JdkClassFileProvider.fromSystemModulesJdk(ToolHelper.getJavaHome(TestRuntime.CfVm.JDK9));
-    assertJavaLangObject(provider);
-    assertJavaUtilConcurrentFlowSubscriber(provider);
-    assert provider instanceof AutoCloseable;
-    ((AutoCloseable) provider).close();
-  }
-
-  @Test
-  public void testJdk11JavaHome() throws Exception {
-    ClassFileResourceProvider provider =
-        JdkClassFileProvider.fromJdkHome(ToolHelper.getJavaHome(TestRuntime.CfVm.JDK11));
-    assertJavaLangObject(provider);
-    assertJavaUtilConcurrentFlowSubscriber(provider);
-    assert provider instanceof AutoCloseable;
-    ((AutoCloseable) provider).close();
-  }
-
-  @Test
-  public void testJdk11SystemModules() throws Exception {
-    ClassFileResourceProvider provider =
-        JdkClassFileProvider.fromSystemModulesJdk(ToolHelper.getJavaHome(TestRuntime.CfVm.JDK11));
+        JdkClassFileProvider.fromSystemModulesJdk(getRuntime().getJavaHome());
     assertJavaLangObject(provider);
     assertJavaUtilConcurrentFlowSubscriber(provider);
     assert provider instanceof AutoCloseable;
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
index 4f31cc8..e44287f 100644
--- a/src/test/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -48,7 +48,7 @@
       throws IOException {
     assert runtime.isCf();
     ProcessResult result =
-        ToolHelper.runJava(runtime.asCf().getVm(), classpath, ObjectArrays.concat(mainClass, args));
+        ToolHelper.runJava(runtime.asCf(), classpath, ObjectArrays.concat(mainClass, args));
     return new JvmTestRunResult(builder.build(), runtime, result);
   }
 
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index e66a3de..4109295 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -94,7 +94,7 @@
   private ProcessResult compileInternal(Path output) throws IOException {
     Path outdir = Files.isDirectory(output) ? output : state.getNewTempFolder();
     List<String> cmdline = new ArrayList<>();
-    cmdline.add(ToolHelper.getJavaExecutable(jdk.getVm()));
+    cmdline.add(jdk.getJavaExecutable().toString());
     cmdline.add("-jar");
     cmdline.add(KT_PRELOADER);
     cmdline.add("org.jetbrains.kotlin.preloading.Preloader");
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 9ab7d53..2eb2d42 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
 import com.android.tools.r8.DataResourceProvider.Visitor;
 import com.android.tools.r8.TestRuntime.CfRuntime;
-import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -176,10 +175,10 @@
   }
 
   public JavaCompilerTool javac(CfRuntime jdk) {
-    return JavaCompilerTool.create(jdk.getVm(), temp);
+    return JavaCompilerTool.create(jdk, temp);
   }
 
-  public static JavaCompilerTool javac(CfVm jdk, TemporaryFolder temp) {
+  public static JavaCompilerTool javac(CfRuntime jdk, TemporaryFolder temp) {
     return JavaCompilerTool.create(jdk, temp);
   }
 
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 8c643ff..f8e0eb0 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -294,8 +294,7 @@
         .addAll(additionalClassPath)
         .add(out)
         .build();
-    ProcessResult result =
-        ToolHelper.runJava(runtime.asCf().getVm(), vmArguments, classPath, arguments);
+    ProcessResult result = ToolHelper.runJava(runtime.asCf(), vmArguments, classPath, arguments);
     return createRunResult(runtime, result);
   }
 
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index ae3e78a..bc0e494 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -3,14 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.TestRuntime.CfVm;
-import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.TestRuntime.NoneRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.Arrays;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -219,20 +217,8 @@
 
   // Public method to check that the CF runtime coincides with the system runtime.
   public static boolean isSystemJdk(CfVm vm) {
-    String version = System.getProperty("java.version");
-    switch (vm) {
-      case JDK8:
-        return version.startsWith("1.8.");
-      case JDK9:
-        return version.startsWith("9.");
-      case JDK11:
-        return version.startsWith("11.");
-    }
-    throw new Unreachable();
-  }
-
-  private static boolean isSupportedJdk(CfVm vm) {
-    return isSystemJdk(vm) || TestRuntime.isCheckedInJDK(vm);
+    TestRuntime systemRuntime = TestRuntime.getSystemRuntime();
+    return systemRuntime.isCf() && systemRuntime.asCf().getVm().equals(vm);
   }
 
   public static boolean isRuntimesPropertySet() {
@@ -243,30 +229,33 @@
     return System.getProperty("runtimes");
   }
 
-  private static Stream<TestRuntime> getAvailableRuntimes() {
-    Stream<TestRuntime> runtimes;
-    if (isRuntimesPropertySet()) {
-      runtimes =
-          Arrays.stream(getRuntimesProperty().split(":"))
-              .filter(s -> !s.isEmpty())
-              .map(
-                  name -> {
-                    TestRuntime runtime = TestRuntime.fromName(name);
-                    if (runtime != null) {
-                      return runtime;
-                    }
-                    throw new RuntimeException("Unexpected runtime property name: " + name);
-                  });
-    } else {
-      runtimes =
-          Stream.concat(
-              Stream.of(NoneRuntime.getInstance()),
-              Stream.concat(
-                  Arrays.stream(TestRuntime.CfVm.values()).map(CfRuntime::fromCfVm),
-                  Arrays.stream(DexVm.Version.values()).map(DexRuntime::new)));
+  private static Stream<TestRuntime> getUnfilteredAvailableRuntimes() {
+    // The runtimes are built in a linked hash map to ensure a deterministic order and avoid
+    // duplicates.
+    LinkedHashMap<TestRuntime, TestRuntime> runtimes = new LinkedHashMap<>();
+    // Place the none-runtime first.
+    NoneRuntime noneRuntime = NoneRuntime.getInstance();
+    runtimes.putIfAbsent(noneRuntime, noneRuntime);
+    // Then the checked in runtimes (CF and DEX).
+    for (TestRuntime checkedInRuntime : TestRuntime.getCheckedInRuntimes()) {
+      runtimes.putIfAbsent(checkedInRuntime, checkedInRuntime);
     }
-    // TODO(b/127785410) Support multiple VMs at the same time.
-    return runtimes.filter(runtime -> !runtime.isCf() || isSupportedJdk(runtime.asCf().getVm()));
+    // Then finally the system runtime. It will likely be the same as a checked in and adding it
+    // makes the overall order more stable.
+    TestRuntime systemRuntime = TestRuntime.getSystemRuntime();
+    runtimes.putIfAbsent(systemRuntime, systemRuntime);
+    return runtimes.values().stream();
+  }
+
+  private static Stream<TestRuntime> getAvailableRuntimes() {
+    if (isRuntimesPropertySet()) {
+      String[] runtimeFilters = getRuntimesProperty().split(":");
+      return getUnfilteredAvailableRuntimes()
+          .filter(
+              runtime ->
+                  Arrays.stream(runtimeFilters).anyMatch(filter -> runtime.name().equals(filter)));
+    }
+    return getUnfilteredAvailableRuntimes();
   }
 
   public static List<CfVm> getAvailableCfVms() {
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index 8177679..db24a6d 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -6,31 +6,20 @@
 
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.google.common.collect.ImmutableMap;
+import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
 
 // Base class for the runtime structure in the test parameters.
-public class TestRuntime {
-
-  public static TestRuntime fromName(String name) {
-    if (NoneRuntime.NAME.equals(name)) {
-      return NoneRuntime.getInstance();
-    }
-    CfVm cfVm = CfVm.fromName(name);
-    if (cfVm != null) {
-      return CfRuntime.fromCfVm(cfVm);
-    }
-    if (name.startsWith("dex-")) {
-      DexVm dexVm = DexVm.fromShortName(name.substring(4) + "_host");
-      if (dexVm != null) {
-        return new DexRuntime(dexVm);
-      }
-    }
-    return null;
-  }
+public abstract class TestRuntime {
 
   // Enum describing the possible/supported CF runtimes.
   public enum CfVm {
@@ -79,51 +68,99 @@
     }
   }
 
-  // Values are the path in third_party/openjdk to the repository with bin
-  private static ImmutableMap<CfVm, Path> CHECKED_IN_JDKS = initializeCheckedInJDKs();
+  private static final Path JDK8_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk8");
+  private static final Path JDK9_PATH =
+      Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "openjdk-9.0.4");
+  private static final Path JDK11_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-11");
 
-  private static ImmutableMap<CfVm, Path> initializeCheckedInJDKs() {
+  public static CfRuntime getCheckedInJdk8() {
+    Path home;
     if (ToolHelper.isLinux()) {
-      return ImmutableMap.of(
-          CfVm.JDK8,
-          Paths.get("jdk8", "linux-x86"),
-          CfVm.JDK9,
-          Paths.get("openjdk-9.0.4", "linux"),
-          CfVm.JDK11,
-          Paths.get("jdk-11", "Linux"));
+      home = JDK8_PATH.resolve("linux-x86");
+    } else if (ToolHelper.isMac()) {
+      home = JDK8_PATH.resolve("darwin-x86");
+    } else {
+      assert ToolHelper.isWindows();
+      return null;
     }
-    if (ToolHelper.isMac()) {
-      return ImmutableMap.of(
-          CfVm.JDK8,
-          Paths.get("jdk8", "darwin-x86"),
-          CfVm.JDK9,
-          Paths.get("openjdk-9.0.4", "osx"),
-          CfVm.JDK11,
-          Paths.get("jdk-11", "Mac", "Contents", "Home"));
+    return new CfRuntime(CfVm.JDK8, home);
+  }
+
+  public static CfRuntime getCheckedInJdk9() {
+    Path home;
+    if (ToolHelper.isLinux()) {
+      home = JDK9_PATH.resolve("linux");
+    } else if (ToolHelper.isMac()) {
+      home = JDK9_PATH.resolve("osx");
+    } else {
+      assert ToolHelper.isWindows();
+      home = JDK9_PATH.resolve("windows");
     }
-    assert ToolHelper.isWindows();
-    return ImmutableMap.of(
-        CfVm.JDK9,
-        Paths.get("openjdk-9.0.4", "windows"),
-        CfVm.JDK11,
-        Paths.get("jdk-11", "Windows"));
+    return new CfRuntime(CfVm.JDK9, home);
   }
 
-  static boolean isCheckedInJDK(CfVm jdk) {
-    return CHECKED_IN_JDKS.containsKey(jdk);
+  public static CfRuntime getCheckedInJdk11() {
+    Path home;
+    if (ToolHelper.isLinux()) {
+      home = JDK11_PATH.resolve("Linux");
+    } else if (ToolHelper.isMac()) {
+      home = Paths.get(JDK11_PATH.toString(), "Mac", "Contents", "Home");
+    } else {
+      assert ToolHelper.isWindows();
+      home = JDK11_PATH.resolve("Windows");
+    }
+    return new CfRuntime(CfVm.JDK11, home);
   }
 
-  static Path getCheckedInJDKHome(CfVm jdk) {
-    return Paths.get("third_party", "openjdk").resolve(CHECKED_IN_JDKS.get(jdk));
+  public static List<CfRuntime> getCheckedInCfRuntimes() {
+    CfRuntime[] jdks =
+        new CfRuntime[] {getCheckedInJdk8(), getCheckedInJdk9(), getCheckedInJdk11()};
+    Builder<CfRuntime> builder = ImmutableList.builder();
+    for (CfRuntime jdk : jdks) {
+      if (jdk != null) {
+        builder.add(jdk);
+      }
+    }
+    return builder.build();
   }
 
-  static Path getCheckedInJDKPathFor(CfVm jdk) {
-    return getCheckedInJDKHome(jdk).resolve(Paths.get("bin", "java"));
+  private static List<DexRuntime> getCheckedInDexRuntimes() {
+    if (ToolHelper.isLinux()) {
+      return ListUtils.map(Arrays.asList(DexVm.Version.values()), DexRuntime::new);
+    }
+    assert ToolHelper.isMac() || ToolHelper.isWindows();
+    return ImmutableList.of();
   }
 
+  // For compatibility with old tests not specifying a Java runtime
+  @Deprecated
   public static TestRuntime getDefaultJavaRuntime() {
-    // For compatibility with old tests not specifying a Java runtime
-    return CfRuntime.fromCfVm(CfVm.JDK9);
+    return getCheckedInJdk9();
+  }
+
+  public static List<TestRuntime> getCheckedInRuntimes() {
+    return ImmutableList.<TestRuntime>builder()
+        .addAll(getCheckedInCfRuntimes())
+        .addAll(getCheckedInDexRuntimes())
+        .build();
+  }
+
+  public static TestRuntime getSystemRuntime() {
+    String version = System.getProperty("java.version");
+    String home = System.getProperty("java.home");
+    if (version == null || version.isEmpty() || home == null || home.isEmpty()) {
+      throw new Unimplemented("Unable to create a system runtime");
+    }
+    if (version.startsWith("1.8.")) {
+      return new CfRuntime(CfVm.JDK8, Paths.get(home));
+    }
+    if (version.startsWith("9.")) {
+      return new CfRuntime(CfVm.JDK9, Paths.get(home));
+    }
+    if (version.startsWith("11.")) {
+      return new CfRuntime(CfVm.JDK11, Paths.get(home));
+    }
+    throw new Unimplemented("No support for JDK version: " + version);
   }
 
   public static class NoneRuntime extends TestRuntime {
@@ -138,9 +175,24 @@
     }
 
     @Override
+    public String name() {
+      return NAME;
+    }
+
+    @Override
     public String toString() {
       return NAME;
     }
+
+    @Override
+    public boolean equals(Object other) {
+      return this == other;
+    }
+
+    @Override
+    public int hashCode() {
+      return System.identityHashCode(this);
+    }
   }
 
   // Wrapper for the DEX runtimes.
@@ -158,6 +210,11 @@
     }
 
     @Override
+    public String name() {
+      return "dex-" + vm.getVersion().name();
+    }
+
+    @Override
     public boolean isDex() {
       return true;
     }
@@ -172,6 +229,20 @@
     }
 
     @Override
+    public boolean equals(Object other) {
+      if (!(other instanceof DexRuntime)) {
+        return false;
+      }
+      DexRuntime dexRuntime = (DexRuntime) other;
+      return vm == dexRuntime.vm;
+    }
+
+    @Override
+    public int hashCode() {
+      return vm.hashCode();
+    }
+
+    @Override
     public String toString() {
       return "dex-" + vm.getVersion().toString();
     }
@@ -183,58 +254,28 @@
 
   // Wrapper for the CF runtimes.
   public static class CfRuntime extends TestRuntime {
-    public static final CfRuntime JDK8 = new CfRuntime(CfVm.JDK8);
-    public static final CfRuntime JDK9 = new CfRuntime(CfVm.JDK9);
-    public static final CfRuntime JDK11 = new CfRuntime(CfVm.JDK11);
-
-    private final boolean systemJdk;
     private final CfVm vm;
     private final Path home;
 
-    private CfRuntime(CfVm vm, Path home, boolean systemJdk) {
-      assert vm != null;
-      this.systemJdk = systemJdk;
-      this.vm = vm;
-      this.home = home;
-    }
-
     public CfRuntime(CfVm vm, Path home) {
-      this(vm, home, false);
+      assert vm != null;
+      this.vm = vm;
+      this.home = home.toAbsolutePath();
     }
 
-    private CfRuntime(CfVm vm) {
-      this(vm, getCheckedInJDKHome(vm), isThisSystemJdk(vm));
+    @Override
+    public String name() {
+      return vm.name();
     }
 
-    private static boolean isThisSystemJdk(CfVm vm) {
-      String version = System.getProperty("java.version");
-      switch (vm) {
-        case JDK8:
-          return version.startsWith("1.8.");
-        case JDK9:
-          return version.startsWith("9.");
-        case JDK11:
-          return version.startsWith("11.");
-      }
-      throw new Unreachable();
+    public Path getJavaHome() {
+      return home;
     }
 
     public Path getJavaExecutable() {
       return home.resolve("bin").resolve("java");
     }
 
-    public static CfRuntime fromCfVm(CfVm vm) {
-      switch (vm) {
-        case JDK8:
-          return JDK8;
-        case JDK9:
-          return JDK9;
-        case JDK11:
-          return JDK11;
-      }
-      throw new Unreachable();
-    }
-
     @Override
     public boolean isCf() {
       return true;
@@ -256,7 +297,24 @@
 
     @Override
     public boolean equals(Object obj) {
-      return obj instanceof CfRuntime && ((CfRuntime) obj).vm.equals(vm);
+      if (!(obj instanceof CfRuntime)) {
+        return false;
+      }
+      CfRuntime cfRuntime = (CfRuntime) obj;
+      return vm == cfRuntime.vm && home.equals(cfRuntime.home);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(vm, home);
+    }
+
+    public boolean isNewerThan(CfVm version) {
+      return !vm.lessThanOrEqual(version);
+    }
+
+    public boolean isNewerThanOrEqual(CfVm version) {
+      return vm == version || !vm.lessThanOrEqual(version);
     }
   }
 
@@ -285,4 +343,12 @@
     }
     throw new Unreachable("Unexpected runtime without backend: " + this);
   }
+
+  @Override
+  public abstract boolean equals(Object other);
+
+  @Override
+  public abstract int hashCode();
+
+  public abstract String name();
 }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 650e9eb..6b224e3 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -11,7 +11,7 @@
 
 import com.android.tools.r8.DeviceRunner.DeviceRunnerConfigurationException;
 import com.android.tools.r8.TestBase.Backend;
-import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper.DexVm.Kind;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.errors.Unreachable;
@@ -1263,22 +1263,22 @@
 
   public static ProcessResult runJava(List<String> vmArgs, List<Path> classpath, String... args)
       throws IOException {
-    return runJava(null, vmArgs, classpath, args);
+    return runJava(TestRuntime.getSystemRuntime().asCf(), vmArgs, classpath, args);
   }
 
-  public static ProcessResult runJava(CfVm runtime, List<Path> classpath, String... args)
+  public static ProcessResult runJava(CfRuntime runtime, List<Path> classpath, String... args)
       throws IOException {
     return runJava(runtime, ImmutableList.of(), classpath, args);
   }
 
+  @Deprecated
   public static ProcessResult runKotlinc(
-      CfVm runtime,
       List<Path> classPaths,
       Path directoryToCompileInto,
       List<String> extraOptions,
       Path... filesToCompile)
       throws IOException {
-    List<String> cmdline = new ArrayList<>(Arrays.asList(getJavaExecutable(runtime)));
+    List<String> cmdline = new ArrayList<>(Arrays.asList(getJavaExecutable()));
     cmdline.add("-jar");
     cmdline.add(KT_PRELOADER);
     cmdline.add("org.jetbrains.kotlin.preloading.Preloader");
@@ -1307,10 +1307,11 @@
   }
 
   public static ProcessResult runJava(
-      CfVm runtime, List<String> vmArgs, List<Path> classpath, String... args) throws IOException {
+      CfRuntime runtime, List<String> vmArgs, List<Path> classpath, String... args)
+      throws IOException {
     String cp =
         classpath.stream().map(Path::toString).collect(Collectors.joining(CLASSPATH_SEPARATOR));
-    List<String> cmdline = new ArrayList<String>(Arrays.asList(getJavaExecutable(runtime)));
+    List<String> cmdline = new ArrayList<>(Arrays.asList(runtime.getJavaExecutable().toString()));
     cmdline.addAll(vmArgs);
     cmdline.add("-cp");
     cmdline.add(cp);
@@ -1412,27 +1413,13 @@
   }
 
   @Deprecated
-  // Use getJavaExecutable(CfVm) to specify a JDK version or getSystemJavaExecutable
+  // Use CfRuntime.getJavaExecutable() for a specific JDK or getSystemJavaExecutable
   public static String getJavaExecutable() {
     return getSystemJavaExecutable();
   }
 
   public static String getSystemJavaExecutable() {
-    return Paths.get(System.getProperty("java.home"), "bin", "java").toString();
-  }
-
-  public static String getJavaExecutable(CfVm runtime) {
-    if (TestRuntime.isCheckedInJDK(runtime)) {
-      return TestRuntime.getCheckedInJDKPathFor(runtime).toString();
-    } else {
-      // TODO(b/127785410): Always assume a non-null runtime.
-      assert runtime == null || TestParametersBuilder.isSystemJdk(runtime);
-      return getSystemJavaExecutable();
-    }
-  }
-
-  public static Path getJavaHome(CfVm runtime) {
-    return TestRuntime.getCheckedInJDKHome(runtime);
+    return TestRuntime.getSystemRuntime().asCf().getJavaExecutable().toString();
   }
 
   public static ProcessResult runArtRaw(ArtCommandBuilder builder) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
index 85b74b4..cafb01b 100644
--- a/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
@@ -15,7 +15,7 @@
 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.TestRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.utils.FileUtils;
@@ -82,7 +82,7 @@
     final Path jar = testFolder.newFolder().toPath().resolve("out.jar");
     final Path map = testFolder.newFolder().toPath().resolve("out.map");
     if (testExternal) {
-      testForExternalR8(newTempFolder(), Backend.CF, CfRuntime.JDK9)
+      testForExternalR8(newTempFolder(), Backend.CF, TestRuntime.getCheckedInJdk9())
           .useR8WithRelocatedDeps()
           .setMode(mode)
           .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTestBase.java
index 096f6a5..fc2e028 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTestBase.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
 
+import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
@@ -25,7 +26,7 @@
             + " windows",
         !ToolHelper.isWindows());
 
-    CfRuntime runtime = CfRuntime.JDK8;
+    CfRuntime runtime = TestRuntime.getCheckedInJdk8();
     Path conversionFolder = temp.newFolder("conversions").toPath();
 
     // Compile the stubs to be able to compile the conversions.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11AtomicTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11AtomicTests.java
index db0a09a..d2501e9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11AtomicTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11AtomicTests.java
@@ -10,7 +10,7 @@
 import static org.hamcrest.CoreMatchers.endsWith;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestRuntime.CfVm;
+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.utils.BooleanUtils;
@@ -65,7 +65,7 @@
   public static void compileAtomicClasses() throws Exception {
     File atomicClassesDir = new File(ATOMIC_COMPILED_TESTS_FOLDER.toString());
     assert atomicClassesDir.exists() || atomicClassesDir.mkdirs();
-    javac(CfVm.JDK11, getStaticTemp())
+    javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
         .addClasspathFiles(
             Collections.singletonList(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
         .addSourceFiles(ATOMIC_TESTS_FILES)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ConcurrentMapTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ConcurrentMapTests.java
index 71458f0..2d2f865 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ConcurrentMapTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ConcurrentMapTests.java
@@ -14,7 +14,7 @@
 
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestRuntime.CfVm;
+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.ir.desugar.DesugaredLibraryWrapperSynthesizer;
@@ -77,7 +77,7 @@
   public static void compileConcurrentClasses() throws Exception {
     File concurrentClassesDir = new File(CONCURRENT_COMPILED_TESTS_FOLDER.toString());
     assert concurrentClassesDir.exists() || concurrentClassesDir.mkdirs();
-    javac(CfVm.JDK11, getStaticTemp())
+    javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
         .addClasspathFiles(
             Collections.singletonList(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
         .addSourceFiles(getAllFilesWithSuffixInDirectory(CONCURRENT_TESTS_FOLDER, JAVA_EXTENSION))
@@ -95,7 +95,7 @@
     Path[] classesToCompile = concurrentHashFilesAndDependencies.toArray(new Path[0]);
     File concurrentHashClassesDir = new File(CONCURRENT_HASH_COMPILED_TESTS_FOLDER.toString());
     assert concurrentHashClassesDir.exists() || concurrentHashClassesDir.mkdirs();
-    javac(CfVm.JDK11, getStaticTemp())
+    javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
         .addClasspathFiles(
             Collections.singletonList(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
         .addSourceFiles(classesToCompile)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11CoreLibTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11CoreLibTestBase.java
index 9a0b63b..b3e6156 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11CoreLibTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11CoreLibTestBase.java
@@ -8,7 +8,7 @@
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
 
-import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -80,7 +80,7 @@
             "java.base=ALL-UNNAMED",
             "--patch-module",
             "java.base=" + JDK_11_JAVA_BASE_EXTENSION_FILES_DIR);
-    javac(CfVm.JDK11, getStaticTemp())
+    javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
         .addOptions(options)
         .addClasspathFiles(
             Collections.singletonList(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11MathTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11MathTests.java
index 9f64237..69c3558 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11MathTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11MathTests.java
@@ -10,6 +10,7 @@
 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.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import java.io.File;
@@ -68,14 +69,14 @@
   public static void compileMathClasses() throws Exception {
     File mathClassesDir = new File(JDK_11_MATH_TESTS_DIR.toString());
     assert mathClassesDir.exists() || mathClassesDir.mkdirs();
-    javac(CfVm.JDK11, getStaticTemp())
+    javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
         .addSourceFiles(JDK_11_MATH_JAVA_FILES)
         .setOutputPath(JDK_11_MATH_TESTS_DIR)
         .compile();
 
     File strictMathClassesDir = new File(JDK_11_STRICT_MATH_TESTS_DIR.toString());
     assert strictMathClassesDir.exists() || strictMathClassesDir.mkdirs();
-    javac(CfVm.JDK11, getStaticTemp())
+    javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
         .addSourceFiles(JDK_11_STRICT_MATH_JAVA_FILES)
         .setOutputPath(JDK_11_STRICT_MATH_TESTS_DIR)
         .compile();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ObjectsTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ObjectsTests.java
index 730ee1b..c8a3e2d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ObjectsTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ObjectsTests.java
@@ -10,6 +10,7 @@
 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.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm;
@@ -55,7 +56,7 @@
   public static void compileObjectsClass() throws Exception {
     File objectsDir = new File(JDK_11_OBJECTS_TESTS_DIR.toString());
     assert objectsDir.exists() || objectsDir.mkdirs();
-    javac(CfVm.JDK11, getStaticTemp())
+    javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
         .addSourceFiles(JDK_11_OBJECTS_JAVA_DIR.resolve(BASIC_OBJECTS_TEST + JAVA_EXTENSION))
         .setOutputPath(JDK_11_OBJECTS_TESTS_DIR)
         .compile();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java
index 784d320..62e1bf9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.D8TestRunResult;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestRuntime.CfVm;
+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.utils.AndroidApiLevel;
@@ -172,7 +172,7 @@
             "java.base=ALL-UNNAMED",
             "--patch-module",
             "java.base=" + JDK_11_JAVA_BASE_EXTENSION_CLASSES_DIR);
-    javac(CfVm.JDK11, getStaticTemp())
+    javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
         .addOptions(options)
         .addClasspathFiles(
             Collections.singletonList(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
index 86a4edb..819e6ce 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
@@ -16,7 +16,7 @@
 import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRuntime.CfVm;
+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;
@@ -43,7 +43,7 @@
   public static void compilePathBackport() throws Exception {
     assumeTrue("JDK8 is not checked-in on Windows", !ToolHelper.isWindows());
     pathMock = getStaticTemp().newFolder("PathMock").toPath();
-    javac(CfVm.JDK8, getStaticTemp())
+    javac(TestRuntime.getCheckedInJdk8(), getStaticTemp())
         .setOutputPath(pathMock)
         .addSourceFiles(
             getAllFilesWithSuffixInDirectory(Paths.get("src/test/r8OnArtBackport"), "java"))
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 882c545..148f165 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
@@ -10,7 +10,7 @@
 import com.android.tools.r8.R8;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRuntime.CfVm;
+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;
@@ -72,7 +72,7 @@
     Path ouputThroughCf = ouputFolder.toPath().resolve("outThroughCf.zip").toAbsolutePath();
     ProcessResult javaProcessResult =
         ToolHelper.runJava(
-            CfVm.JDK9,
+            TestRuntime.getCheckedInJdk9(),
             Collections.singletonList(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR),
             "-Xmx512m",
             R8.class.getTypeName(),
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
index 1730e78..8af167c 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime;
-import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.cf.BootstrapCurrentEqualityTest;
@@ -98,9 +97,10 @@
     String prevRunResult = null;
     for (Path jar : jarsToCompare()) {
       // All jars except ToolHelper.R8_WITH_RELOCATED_DEPS_JAR are compiled for JDK11.
-      TestRuntime runtime = jar == ToolHelper.R8_WITH_RELOCATED_DEPS_JAR
-          ? parameters.getRuntime()
-          : CfRuntime.JDK11;
+      TestRuntime runtime =
+          jar == ToolHelper.R8_WITH_RELOCATED_DEPS_JAR
+              ? parameters.getRuntime()
+              : TestRuntime.getCheckedInJdk11();
       Path generatedJar =
           testForExternalR8(Backend.CF, runtime)
               .useProvidedR8(jar)
@@ -110,9 +110,7 @@
               .outputJar();
       String runResult =
           ToolHelper.runJava(
-                  parameters.getRuntime().asCf().getVm(),
-                  ImmutableList.of(generatedJar),
-                  "hello.Hello")
+                  parameters.getRuntime().asCf(), ImmutableList.of(generatedJar), "hello.Hello")
               .toString();
       if (prevRunResult != null) {
         assertEquals(prevRunResult, runResult);
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index e210196..25c4a53 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -40,7 +40,6 @@
 @RunWith(Parameterized.class)
 public class GenerateBackportMethods extends TestBase {
 
-  static final Path javaExecutable = Paths.get(ToolHelper.getJavaExecutable(CfVm.JDK9));
   static final Path googleFormatDir = Paths.get(ToolHelper.THIRD_PARTY_DIR, "google-java-format");
   static final Path googleFormatJar =
       googleFormatDir.resolve("google-java-format-1.7-all-deps.jar");
@@ -90,7 +89,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withNoneRuntime().build();
+    return getTestParameters().withCfRuntime(CfVm.JDK9).build();
   }
 
   public GenerateBackportMethods(TestParameters parameters) {
@@ -104,15 +103,18 @@
     assertEquals("Classes should be listed in sorted order", sorted, methodTemplateClasses);
     assertEquals(
         FileUtils.readTextFile(backportMethodsFile, StandardCharsets.UTF_8),
-        generateBackportMethods());
+        generateBackportMethods(parameters.getRuntime().asCf().getJavaExecutable().toString()));
   }
 
   // Running this method will regenerate / overwrite the content of the backport methods.
   public static void main(String[] args) throws Exception {
-    FileUtils.writeToFile(backportMethodsFile, null, generateBackportMethods().getBytes());
+    FileUtils.writeToFile(
+        backportMethodsFile,
+        null,
+        generateBackportMethods(ToolHelper.getSystemJavaExecutable()).getBytes());
   }
 
-  private static String generateBackportMethods() throws IOException {
+  private static String generateBackportMethods(String javaExecutable) throws IOException {
     InternalOptions options = new InternalOptions();
     CfCodePrinter codePrinter = new CfCodePrinter();
     JarClassFileReader reader =
@@ -146,7 +148,7 @@
     ProcessBuilder builder =
         new ProcessBuilder(
             ImmutableList.of(
-                javaExecutable.toString(),
+                javaExecutable,
                 "-jar",
                 googleFormatJar.toString(),
                 outfile.toAbsolutePath().toString()));
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameTest.java
index 8b04b16..c7ba86e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameTest.java
@@ -57,7 +57,6 @@
     ProcessResult processResult =
         ToolHelper.runKotlinc(
             null,
-            null,
             supertypeLibJar,
             null,
             getKotlinFileInTest(supertypeLibFolder, "impl"),
@@ -70,7 +69,6 @@
     processResult =
         ToolHelper.runKotlinc(
             null,
-            null,
             extLibJar,
             null,
             getKotlinFileInTest(extLibFolder, "B")
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceConstructorRenamingTest.java b/src/test/java/com/android/tools/r8/naming/InterfaceConstructorRenamingTest.java
index b2ed534..ebd7420 100644
--- a/src/test/java/com/android/tools/r8/naming/InterfaceConstructorRenamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/InterfaceConstructorRenamingTest.java
@@ -26,7 +26,6 @@
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.BeforeParam;
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
@@ -44,11 +43,6 @@
 
   @ClassRule public static TemporaryFolder staticTemp = ToolHelper.getTemporaryFolderForTest();
 
-  @BeforeParam
-  public static void forceCompilation(TestParameters parameters) {
-    compilation.apply(parameters.getBackend());
-  }
-
   private static R8TestCompileResult compile(Backend backend)
       throws com.android.tools.r8.CompilationFailedException, IOException, ExecutionException {
     R8TestCompileResult compileResult =
@@ -86,7 +80,7 @@
 
   @Test
   public void test() throws Throwable {
-    compile(parameters.getBackend())
+    compilation.apply(parameters.getBackend())
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutput(EXPECTED);
   }
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
index 98780e9..7fa0543 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
@@ -31,7 +31,6 @@
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.BeforeParam;
 
 @RunWith(Parameterized.class)
 public class ApplyMappingAfterVerticalMergingMethodTest extends TestBase {
@@ -100,11 +99,6 @@
   @ClassRule
   public static TemporaryFolder staticTemp = ToolHelper.getTemporaryFolderForTest();
 
-  @BeforeParam
-  public static void forceCompilation(TestParameters parameters) {
-    compilationResults.apply(parameters.getBackend());
-  }
-
   public static CompilationResult compile(Backend backend)
       throws ExecutionException, CompilationFailedException, IOException {
     R8TestCompileResult library = compileLibrary(backend);
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
index ab80fe5..298134a 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
@@ -32,7 +32,6 @@
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.BeforeParam;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.ClassWriter;
@@ -179,11 +178,6 @@
 
   @ClassRule public static TemporaryFolder staticTemp = ToolHelper.getTemporaryFolderForTest();
 
-  @BeforeParam
-  public static void forceCompilation(TestParameters parameters) {
-    compilationResults.apply(parameters.getBackend());
-  }
-
   private static Function<Backend, CompilationResults> compilationResults =
       memoizeFunction(RemoveAssertionsTest::compileAll);
 
diff --git a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
index 287b736..5220c01 100644
--- a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
@@ -35,7 +35,6 @@
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.BeforeParam;
 
 @RunWith(Parameterized.class)
 public class NonVirtualOverrideTest extends TestBase {
@@ -91,14 +90,6 @@
 
   @ClassRule public static TemporaryFolder staticTemp = ToolHelper.getTemporaryFolderForTest();
 
-  @BeforeParam
-  public static void forceCompilation(
-      TestParameters parameters, boolean enableClassInlining, boolean enableVerticalClassMerging) {
-    expectedResults.apply(isDexVmBetween5_1_1and7_0_0(parameters));
-    compilationResults.apply(
-        new Dimensions(parameters.getBackend(), enableClassInlining, enableVerticalClassMerging));
-  }
-
   private static Function<Boolean, String> expectedResults =
       memoizeFunction(NonVirtualOverrideTest::getExpectedResult);