Update api stubbing tests to add class to bootclasspath

Change-Id: Ia7daf0b605cbee7333c0f746bcbbe2697ba6b885
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index f0c648b..ccdd0ca 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -2237,7 +2237,7 @@
       builder.appendArtOption("-Xnoimage-dex2oat");
     }
     for (String s : ToolHelper.getBootLibs(dexVm)) {
-      builder.appendBootClassPath(new File(s).getCanonicalPath());
+      builder.appendBootClasspath(new File(s).getCanonicalPath());
     }
     builder.setMainClass(JUNIT_TEST_RUNNER);
     builder.appendProgramArgument(fullClassName);
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 0480694..b96cef4 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -179,7 +179,7 @@
     for (int i = 2; i < args.length; i++) {
       args[i] = featureDependencies[i - 2].toString();
     }
-    return runArt(runtime, additionalRunClassPath, mainClassSubject.getFinalName(), args);
+    return runArt(runtime, mainClassSubject.getFinalName(), args);
   }
 
   public String getProguardMap() {
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 9ab97e7..af079d9 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -33,6 +33,7 @@
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ObjectArrays;
+import java.io.File;
 import java.io.IOException;
 import java.io.PrintStream;
 import java.nio.file.Path;
@@ -56,6 +57,7 @@
   public final int minApiLevel;
   private final OutputMode outputMode;
   final List<Path> additionalRunClassPath = new ArrayList<>();
+  final List<Path> additionalBootClasspath = new ArrayList<>();
   final List<String> vmArguments = new ArrayList<>();
   private boolean withArt6Plus64BitsLib = false;
   private boolean withArtFrameworks = true;
@@ -164,12 +166,10 @@
       case DEX:
         return runArt(
             new DexRuntime(ToolHelper.getDexVm()),
-            additionalRunClassPath,
             mainClassSubject.getFinalName());
       case CF:
         return runJava(
             TestRuntime.getDefaultJavaRuntime(),
-            additionalRunClassPath,
             mainClassSubject.getFinalName());
       default:
         throw new Unreachable();
@@ -207,12 +207,11 @@
     }
     assertThat("Did you forget a keep rule for the main method?", mainClassSubject, isPresent());
     if (runtime.isDex()) {
-      return runArt(runtime, additionalRunClassPath, mainClassSubject.getFinalName(), args);
+      return runArt(runtime, mainClassSubject.getFinalName(), args);
     }
     assert runtime.isCf();
     return runJava(
         runtime,
-        additionalRunClassPath,
         ObjectArrays.concat(mainClassSubject.getFinalName(), args));
   }
 
@@ -256,6 +255,38 @@
     }
   }
 
+  public CR addBootClasspathClasses(Class<?>... classes) throws Exception {
+    return addBootClasspathClasses(Arrays.asList(classes));
+  }
+
+  public CR addBootClasspathClasses(List<Class<?>> classes) throws Exception {
+    if (getBackend() == Backend.DEX) {
+      additionalBootClasspath.add(
+          testForD8(state.getTempFolder())
+              .addProgramClasses(classes)
+              .setMinApi(minApiLevel)
+              .compile()
+              .writeToZip());
+      return self();
+    }
+    assert getBackend() == Backend.CF;
+    try {
+      Path path = state.getNewTempFolder().resolve("runtime-classes.jar");
+      ArchiveConsumer consumer = new ArchiveConsumer(path);
+      for (Class<?> clazz : classes) {
+        consumer.accept(
+            ByteDataView.of(ToolHelper.getClassAsBytes(clazz)),
+            DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()),
+            null);
+      }
+      consumer.finished(null);
+      additionalBootClasspath.add(path);
+      return self();
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   public CR addDesugaredCoreLibraryRunClassPath(
       Function<AndroidApiLevel, Path> classPathSupplier, AndroidApiLevel minAPILevel) {
     addRunClasspathFiles(classPathSupplier.apply(minAPILevel));
@@ -523,30 +554,29 @@
     }
   }
 
-  private RR runJava(TestRuntime runtime, List<Path> additionalClassPath, String... arguments)
-      throws IOException {
+  private RR runJava(TestRuntime runtime, String... arguments) throws IOException {
     assert runtime != null;
     Path out = state.getNewTempFolder().resolve("out.zip");
     app.writeToZip(out, OutputMode.ClassFile);
-    List<Path> classPath = ImmutableList.<Path>builder()
-        .addAll(additionalClassPath)
-        .add(out)
-        .build();
-    ProcessResult result = ToolHelper.runJava(runtime.asCf(), vmArguments, classPath, arguments);
+    List<Path> classPath =
+        ImmutableList.<Path>builder().addAll(additionalRunClassPath).add(out).build();
+    ProcessResult result =
+        ToolHelper.runJava(
+            runtime.asCf(), vmArguments, additionalBootClasspath, classPath, arguments);
     return createRunResult(runtime, result);
   }
 
-  RR runArt(
-      TestRuntime runtime, List<Path> additionalClassPath, String mainClass, String... arguments)
-      throws IOException {
+  RR runArt(TestRuntime runtime, String mainClass, String... arguments) throws IOException {
     DexVm vm = runtime.asDex().getVm();
     // TODO(b/127785410): Always assume a non-null runtime.
     Path out = state.getNewTempFolder().resolve("out.zip");
     app.writeToZip(out, OutputMode.DexIndexed);
-    List<String> classPath = ImmutableList.<String>builder()
-        .addAll(additionalClassPath.stream().map(Path::toString).collect(Collectors.toList()))
-        .add(out.toString())
-        .build();
+    List<String> classPath =
+        ImmutableList.<String>builder()
+            .addAll(
+                additionalRunClassPath.stream().map(Path::toString).collect(Collectors.toList()))
+            .add(out.toString())
+            .build();
     Consumer<ArtCommandBuilder> commandConsumer =
         withArt6Plus64BitsLib && vm.getVersion().isNewerThanOrEqual(DexVm.Version.V6_0_1)
             ? builder -> builder.appendArtOption("--64")
@@ -554,6 +584,22 @@
     commandConsumer =
         commandConsumer.andThen(
             builder -> {
+              if (!additionalBootClasspath.isEmpty()) {
+                DexVm dexVm = runtime.asDex().getVm();
+                if (dexVm.isNewerThan(DexVm.ART_4_4_4_HOST)) {
+                  builder.appendArtOption("-Ximage:/system/non/existent/image.art");
+                  builder.appendArtOption("-Xnoimage-dex2oat");
+                }
+                try {
+                  for (String s : ToolHelper.getBootLibs(dexVm)) {
+                    builder.appendBootClasspath(new File(s).getCanonicalPath());
+                  }
+                } catch (Exception e) {
+                  throw new RuntimeException();
+                }
+                additionalBootClasspath.forEach(
+                    path -> builder.appendBootClasspath(path.toString()));
+              }
               for (String vmArgument : vmArguments) {
                 builder.appendArtOption(vmArgument);
               }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2d0cc12..57fabd1 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -422,7 +422,7 @@
     protected List<String> classpaths = new ArrayList<>();
     protected String mainClass;
     protected List<String> programArguments = new ArrayList<>();
-    protected List<String> bootClassPaths = new ArrayList<>();
+    protected List<String> bootClasspaths = new ArrayList<>();
     protected String executionDirectory;
 
     public CommandBuilder appendArtOption(String option) {
@@ -450,8 +450,8 @@
       return this;
     }
 
-    public CommandBuilder appendBootClassPath(String lib) {
-      bootClassPaths.add(lib);
+    public CommandBuilder appendBootClasspath(String lib) {
+      bootClasspaths.add(lib);
       return this;
     }
 
@@ -475,13 +475,13 @@
         builder.append(entry.getValue());
         result.add(builder.toString());
       }
+      if (!bootClasspaths.isEmpty()) {
+        result.add("-Xbootclasspath:" + String.join(":", bootClasspaths));
+      }
       if (!classpaths.isEmpty()) {
         result.add("-cp");
         result.add(String.join(":", classpaths));
       }
-      if (!bootClassPaths.isEmpty()) {
-        result.add("-Xbootclasspath:" + String.join(":", bootClassPaths));
-      }
       if (mainClass != null) {
         result.add(mainClass);
       }
@@ -555,7 +555,7 @@
           .setVmOptions(options)
           .setSystemProperties(systemProperties)
           .setClasspath(toFileList(classpaths))
-          .setBootClasspath(toFileList(bootClassPaths))
+          .setBootClasspath(toFileList(bootClasspaths))
           .setMainClass(mainClass)
           .setProgramArguments(programArguments);
     }
@@ -1416,12 +1416,28 @@
   public static ProcessResult runJava(
       CfRuntime runtime, List<String> vmArgs, List<Path> classpath, String... args)
       throws IOException {
-    String cp =
-        classpath.stream().map(Path::toString).collect(Collectors.joining(CLASSPATH_SEPARATOR));
+    return runJava(runtime, vmArgs, ImmutableList.of(), classpath, args);
+  }
+
+  public static ProcessResult runJava(
+      CfRuntime runtime,
+      List<String> vmArgs,
+      List<Path> bootClasspaths,
+      List<Path> classpath,
+      String... args)
+      throws IOException {
     List<String> cmdline = new ArrayList<>(Arrays.asList(runtime.getJavaExecutable().toString()));
     cmdline.addAll(vmArgs);
+    if (!bootClasspaths.isEmpty()) {
+      cmdline.add(
+          "-Xbootclasspath/a:"
+              + bootClasspaths.stream()
+                  .map(Path::toString)
+                  .collect(Collectors.joining(CLASSPATH_SEPARATOR)));
+    }
     cmdline.add("-cp");
-    cmdline.add(cp);
+    cmdline.add(
+        classpath.stream().map(Path::toString).collect(Collectors.joining(CLASSPATH_SEPARATOR)));
     cmdline.addAll(Arrays.asList(args));
     ProcessBuilder builder = new ProcessBuilder(cmdline);
     return runProcess(builder);
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
index 97351d8..4125cc2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -7,10 +7,12 @@
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.testing.AndroidBuildVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.Matchers;
@@ -34,6 +36,9 @@
 
   @Test
   public void testR8() throws Exception {
+    // TODO(b/197078995): Make this work on 12.
+    assumeFalse(
+        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
     boolean isMockApiLevel =
         parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
     testForR8(parameters.getBackend())
@@ -49,7 +54,7 @@
         .applyIf(
             parameters.isDexRuntime()
                 && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel),
-            b -> b.addRunClasspathClasses(LibraryClass.class))
+            b -> b.addBootClasspathClasses(LibraryClass.class))
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLinesIf(isMockApiLevel, "LibraryClass::foo")
         .assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
index ad7cfc8..46a9fe8 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -7,10 +7,12 @@
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.testing.AndroidBuildVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.Matchers;
@@ -34,6 +36,9 @@
 
   @Test
   public void testR8() throws Exception {
+    // TODO(b/197078995): Make this work on 12.
+    assumeFalse(
+        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
     boolean isMockApiLevel =
         parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
     testForR8(parameters.getBackend())
@@ -50,7 +55,7 @@
         .applyIf(
             parameters.isDexRuntime()
                 && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel),
-            b -> b.addRunClasspathClasses(LibraryClass.class))
+            b -> b.addBootClasspathClasses(LibraryClass.class))
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLinesIf(isMockApiLevel, "ProgramClass::foo")
         .assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMethodMissingClassTest.java
index 5203e98..6b91898 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMethodMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMethodMissingClassTest.java
@@ -8,10 +8,12 @@
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.testing.AndroidBuildVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.Matchers;
@@ -37,6 +39,9 @@
 
   @Test
   public void testR8() throws Exception {
+    // TODO(b/197078995): Make this work on 12.
+    assumeFalse(
+        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
     boolean preMockApis =
         parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(initialLibraryMockLevel);
     boolean postMockApis =
@@ -64,7 +69,7 @@
                     .getRuntime()
                     .maxSupportedApiLevel()
                     .isGreaterThanOrEqualTo(initialLibraryMockLevel),
-            b -> b.addRunClasspathClasses(LibraryClass.class))
+            b -> b.addBootClasspathClasses(LibraryClass.class))
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLinesIf(preMockApis, "Hello World")
         .assertSuccessWithOutputLinesIf(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
index 2d5df16..285f3d0 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -8,10 +8,12 @@
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.testing.AndroidBuildVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.Matchers;
@@ -36,6 +38,9 @@
 
   @Test
   public void testR8() throws Exception {
+    // TODO(b/197078995): Make this work on 12.
+    assumeFalse(
+        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
     boolean isMockApiLevel =
         parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockApiLevel);
     testForR8(parameters.getBackend())
@@ -58,14 +63,14 @@
                     .getRuntime()
                     .maxSupportedApiLevel()
                     .isGreaterThanOrEqualTo(lowerMockApiLevel),
-            b -> b.addRunClasspathClasses(LibraryClass.class, LibraryInterface.class))
+            b -> b.addBootClasspathClasses(LibraryClass.class, LibraryInterface.class))
         .applyIf(
             parameters.isDexRuntime()
                 && parameters
                     .getRuntime()
                     .maxSupportedApiLevel()
                     .isGreaterThanOrEqualTo(mockApiLevel),
-            b -> b.addRunClasspathClasses(OtherLibraryClass.class))
+            b -> b.addBootClasspathClasses(OtherLibraryClass.class))
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLinesIf(isMockApiLevel, "ProgramClass::foo")
         .assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")