Allow to run tests in different relative execution directory

Bug:b/290903900
Change-Id: I316753a9dfca05e33f352cbe17794039fb5a1ad7
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 3bad712..1adba6d 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1213,7 +1213,7 @@
     Path out = File.createTempFile("junit", ".zip", temp.getRoot()).toPath();
     app.writeToZipForTesting(out, OutputMode.DexIndexed);
     return ToolHelper.runArtRaw(
-        ImmutableList.of(out.toString()), mainClass, cmdBuilder, version, false);
+        ImmutableList.of(out.toString()), mainClass, cmdBuilder, version, null);
   }
 
   /** Run application on Art with the specified main class. */
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index dfdfa9d..83c3d0c 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -64,7 +64,7 @@
   final List<Path> additionalBootClasspath = new ArrayList<>();
   final List<String> vmArguments = new ArrayList<>();
   private boolean withArt6Plus64BitsLib = false;
-  private boolean withArtFrameworks = true;
+  private Path relativeExecutionDirectory = null;
   private LibraryDesugaringTestConfiguration libraryDesugaringTestConfiguration;
 
   TestCompileResult(TestState state, AndroidApp app, int minApiLevel, OutputMode outputMode) {
@@ -120,8 +120,8 @@
     return self();
   }
 
-  public final CR withArtFrameworks() {
-    withArtFrameworks = true;
+  public final CR withRelativeExecutionDirectory(Path relativeExecutionDirectory) {
+    this.relativeExecutionDirectory = relativeExecutionDirectory;
     return self();
   }
 
@@ -688,7 +688,7 @@
             });
     ProcessResult result =
         ToolHelper.runArtRaw(
-            classPath, mainClass, commandConsumer, vm, withArtFrameworks, arguments);
+            classPath, mainClass, commandConsumer, vm, relativeExecutionDirectory, arguments);
     return createRunResult(runtime, result);
   }
 
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 177d064..5a2dc07 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -453,7 +453,7 @@
     protected String mainClass;
     protected List<String> programArguments = new ArrayList<>();
     protected List<String> bootClasspaths = new ArrayList<>();
-    protected String executionDirectory;
+    protected Path relativeExecutionDirectory;
 
     public CommandBuilder appendArtOption(String option) {
       options.add(option);
@@ -523,8 +523,8 @@
 
     public ProcessBuilder asProcessBuilder() {
       ProcessBuilder processBuilder = new ProcessBuilder(command());
-      if (executionDirectory != null) {
-        processBuilder.directory(new File(executionDirectory));
+      if (relativeExecutionDirectory != null) {
+        processBuilder.directory(relativeExecutionDirectory.toFile());
       }
       return processBuilder;
     }
@@ -541,7 +541,6 @@
   public static class ArtCommandBuilder extends CommandBuilder {
 
     private DexVm version;
-    private boolean withArtFrameworks;
     private ArtResultCacheLookupKey artResultCacheLookupKey;
 
     public ArtCommandBuilder() {
@@ -562,13 +561,17 @@
 
     @Override
     protected String getExecutable() {
-      if (withArtFrameworks && version.isNewerThan(DexVm.ART_4_4_4_HOST)) {
-        // Run directly Art in its repository, which has been patched by gradle to match expected
-        // path for the frameworks.
-        executionDirectory = getArtDir(version);
-        return getRawArtBinary(version);
+      String result = version != null ? getArtBinary(version) : getArtBinary();
+      if (relativeExecutionDirectory != null) {
+        // The execution directory has to be relative.
+        assert !relativeExecutionDirectory.isAbsolute();
+        Path toRoot = Paths.get("");
+        for (int i = 0; i < relativeExecutionDirectory.getNameCount(); i++) {
+          toRoot = toRoot.resolve("..");
+        }
+        return toRoot.resolve(result).toString();
       }
-      return version != null ? getArtBinary(version) : getArtBinary();
+      return result;
     }
 
     public boolean isForDevice() {
@@ -617,11 +620,10 @@
     private void hashParts(Hasher hasher) {
       // Call getExecutable first, this will set executionDirectory if needed.
       hasher.putString(this.getExecutable(), StandardCharsets.UTF_8);
-      if (this.executionDirectory != null) {
-        hasher.putString(this.executionDirectory, StandardCharsets.UTF_8);
+      if (this.relativeExecutionDirectory != null) {
+        hasher.putString(this.relativeExecutionDirectory.toString(), StandardCharsets.UTF_8);
       }
       hasher.putString(this.mainClass, StandardCharsets.UTF_8);
-      hasher.putBoolean(this.withArtFrameworks);
       hashFilesFromList(hasher, classpaths);
       hashFilesFromList(hasher, bootClasspaths);
       systemProperties.forEach(
@@ -1790,7 +1792,7 @@
   public static ProcessResult runArtRaw(List<String> files, String mainClass,
       Consumer<ArtCommandBuilder> extras)
       throws IOException {
-    return runArtRaw(files, mainClass, extras, null, false);
+    return runArtRaw(files, mainClass, extras, null, null);
   }
 
   // Index used to name directory aimed at storing dex files and process result
@@ -1803,12 +1805,12 @@
       String mainClass,
       Consumer<ArtCommandBuilder> extras,
       DexVm version,
-      boolean withArtFrameworks,
+      Path relativeExecutionDirectory,
       String... args)
       throws IOException {
     ArtCommandBuilder builder =
         version != null ? new ArtCommandBuilder(version) : new ArtCommandBuilder();
-    builder.withArtFrameworks = withArtFrameworks;
+    builder.relativeExecutionDirectory = relativeExecutionDirectory;
     files.forEach(builder::appendClasspath);
     builder.setMainClass(mainClass);
     if (extras != null) {
@@ -2041,7 +2043,7 @@
       Consumer<ArtCommandBuilder> extras,
       DexVm version)
       throws IOException {
-    ProcessResult result = runArtRaw(files, mainClass, extras, version, false);
+    ProcessResult result = runArtRaw(files, mainClass, extras, version, null);
     failOnProcessFailure(result);
     failOnVerificationErrors(result);
     return result;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
index ed6b91a..2f9d1f1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
@@ -14,6 +14,7 @@
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.D8TestCompileResult;
@@ -39,12 +40,14 @@
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import org.jetbrains.annotations.NotNull;
 import org.junit.Assume;
 import org.junit.BeforeClass;
@@ -279,6 +282,7 @@
   @Test
   public void testNioFileDesugaredLib() throws Exception {
     String verbosity = "2";
+    Path relativeExecutionDirectory = createRelativeExecutionDirectory();
     DesugaredLibraryTestCompileResult<?> compileResult =
         testForDesugaredLibrary(
                 parameters, libraryDesugaringSpecification, compilationSpecification)
@@ -287,6 +291,7 @@
             .addProgramFiles(testNGSupportProgramFiles())
             .addProgramClassFileData(getTestNGMainRunner())
             .compile()
+            .withRelativeExecutionDirectory(relativeExecutionDirectory)
             .withArt6Plus64BitsLib();
     int success = 0;
     List<String> failingClasses = new ArrayList<>();
@@ -330,6 +335,7 @@
       assertEquals(29, success);
       assertEquals(0, failingClasses.size());
     }
+    clearDirectory(relativeExecutionDirectory);
   }
 
   @Test
@@ -338,6 +344,7 @@
         "The package java.nio was not present on older devices, all tests fail.",
         parameters.getDexRuntimeVersion().isOlderThan(Version.V8_1_0));
     String verbosity = "2";
+    Path relativeExecutionDirectory = createRelativeExecutionDirectory();
     D8TestCompileResult compileResult =
         testForD8(parameters.getBackend())
             .addProgramFiles(TEST_UTIL_JAR)
@@ -346,6 +353,7 @@
             .addProgramClassFileData(getTestNGMainRunner())
             .addLibraryFiles(libraryDesugaringSpecification.getLibraryFiles())
             .compile()
+            .withRelativeExecutionDirectory(relativeExecutionDirectory)
             .withArt6Plus64BitsLib();
     for (String mainTestClass : SUCCESSFUL_MAIN_TESTS) {
       compileResult.run(parameters.getRuntime(), mainTestClass).assertSuccess();
@@ -358,5 +366,28 @@
           "Failure in " + testNGTestClass + "\n" + result,
           result.getStdOut().contains(StringUtils.lines(testNGTestClass + ": SUCCESS")));
     }
+    clearDirectory(relativeExecutionDirectory);
+  }
+
+  private static void clearDirectory(Path executionDirectory) throws IOException {
+    try (Stream<Path> pathStream = Files.walk(executionDirectory)) {
+      pathStream
+          .sorted(Comparator.reverseOrder())
+          .map(Path::toFile)
+          .forEach(f -> assertTrue(f.delete()));
+    }
+    assertFalse(Files.exists(executionDirectory));
+  }
+
+  private Path createRelativeExecutionDirectory() throws IOException {
+    // We need to create a relative directory (in r8) so it can be used as execution directory.
+    return Files.createDirectories(
+        Paths.get(
+            "jdknio_"
+                + compilationSpecification.toString()
+                + "_"
+                + parameters.getDexRuntimeVersion().toString()
+                + "_"
+                + parameters.getApiLevel().toString()));
   }
 }
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 4cba5a7..148e5dd 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
@@ -196,7 +196,7 @@
               R8.class.getTypeName(),
               builder -> builder.appendArtOption("--64").appendArtOption("-Xmx512m"),
               parameters.getRuntime().asDex().getVm(),
-              true,
+              null,
               ImmutableList.builder()
                   .add("--output")
                   .add(commandLinePathFor(outputThroughDex))
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java
index 221a5c7..95ad7e0 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java
@@ -178,6 +178,12 @@
     return this;
   }
 
+  public DesugaredLibraryTestCompileResult<T> withRelativeExecutionDirectory(
+      Path executionDirectory) {
+    runnableCompiledResult.withRelativeExecutionDirectory(executionDirectory);
+    return this;
+  }
+
   public DesugaredLibraryTestCompileResult<T> withArt6Plus64BitsLib() {
     runnableCompiledResult.withArt6Plus64BitsLib();
     return this;