Rollback of commit 59180a4ea66b1395b5b85defd732859ecae919ea.

*** Reason for rollback ***

Break bazel-tests and many other jobs on CI.
http://ci.bazel.io/job/bazel-tests/BAZEL_VERSION=HEAD,PLATFORM_NAME=linux-x86_64/651/console

*** Original change description ***

Add SpawnInputExpander helper class to arrange runfiles for spawn strategies

This new class is a combination of SpawnHelper and our internal code; the
plan is to migrate all spawn strategies to the new class. The strict flag
should be enabled by default, but that's a breaking change, so we need to do
it later.

- Use it in SandboxStrategy.
- Add ActionInput.getExecPath to return a PathFragment; this avoids lots of
  back and forth between path fragments and strings.

This is a step towards #159...

***

--
PiperOrigin-RevId: 150610616
MOS_MIGRATED_REVID=150610616
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionInput.java b/src/main/java/com/google/devtools/build/lib/actions/ActionInput.java
index 90d4c5f..a21ed85 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionInput.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionInput.java
@@ -14,8 +14,6 @@
 
 package com.google.devtools.build.lib.actions;
 
-import com.google.devtools.build.lib.vfs.PathFragment;
-
 /**
  * Represents an input file to a build action, with an appropriate relative path and digest
  * value.
@@ -34,13 +32,6 @@
  */
 public interface ActionInput {
 
-  /**
-   * @return the relative path to the input file.
-   */
-  String getExecPathString();
-
-  /**
-   * @return the relative path to the input file.
-   */
-  PathFragment getExecPath();
+  /** @return the relative path to the input file. */
+  public String getExecPathString();
 }
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionInputHelper.java b/src/main/java/com/google/devtools/build/lib/actions/ActionInputHelper.java
index c63fd5b..4ca4a1b 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionInputHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionInputHelper.java
@@ -69,7 +69,6 @@
    */
   private static class BasicActionInput implements ActionInput {
     private final String path;
-
     public BasicActionInput(String path) {
       this.path = Preconditions.checkNotNull(path);
     }
@@ -80,11 +79,6 @@
     }
 
     @Override
-    public PathFragment getExecPath() {
-      return new PathFragment(path);
-    }
-
-    @Override
     public int hashCode() {
       return path.hashCode();
     }
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
index bd54b0f..4b7adbc 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
@@ -344,7 +344,6 @@
     return root;
   }
 
-  @Override
   public final PathFragment getExecPath() {
     return execPath;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java b/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
index 04eaf12..a087ee4 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
@@ -134,8 +134,8 @@
     private final Artifact artifact;
 
     private SymlinkEntry(PathFragment path, Artifact artifact) {
-      this.path = Preconditions.checkNotNull(path);
-      this.artifact = Preconditions.checkNotNull(artifact);
+      this.path = path;
+      this.artifact = artifact;
     }
 
     public PathFragment getPath() {
@@ -146,12 +146,10 @@
       return artifact;
     }
 
-    @Override
     public boolean isImmutable() {
       return true;
     }
 
-    @Override
     public void write(Appendable buffer, char quotationMark) {
       Printer.append(buffer, "SymlinkEntry(path = ");
       Printer.write(buffer, getPath().toString(), quotationMark);
@@ -433,11 +431,11 @@
    * Returns the symlinks as a map from PathFragment to Artifact.
    *
    * @param eventHandler Used for throwing an error if we have an obscuring runlink within the
-   *    normal source tree entries, or runfile conflicts. May be null, in which case obscuring
-   *    symlinks are silently discarded, and conflicts are overwritten.
+   *     normal source tree entries, or runfile conflicts. May be null, in which case obscuring
+   *     symlinks are silently discarded, and conflicts are overwritten.
    * @param location Location for eventHandler warnings. Ignored if eventHandler is null.
    * @return Map<PathFragment, Artifact> path fragment to artifact, of normal source tree entries
-   *    and elements that live outside the source tree. Null values represent empty input files.
+   *     and elements that live outside the source tree. Null values represent empty input files.
    */
   public Map<PathFragment, Artifact> getRunfilesInputs(EventHandler eventHandler, Location location)
       throws IOException {
@@ -852,13 +850,13 @@
      * Adds a symlink.
      */
     public Builder addSymlink(PathFragment link, Artifact target) {
+      Preconditions.checkNotNull(link);
+      Preconditions.checkNotNull(target);
       symlinksBuilder.add(new SymlinkEntry(link, target));
       return this;
     }
 
-    /**
-     * Adds several symlinks. Neither keys nor values may be null.
-     */
+    /** Adds several symlinks. */
     public Builder addSymlinks(Map<PathFragment, Artifact> symlinks) {
       for (Map.Entry<PathFragment, Artifact> symlink : symlinks.entrySet()) {
         symlinksBuilder.add(new SymlinkEntry(symlink.getKey(), symlink.getValue()));
@@ -878,13 +876,13 @@
      * Adds a root symlink.
      */
     public Builder addRootSymlink(PathFragment link, Artifact target) {
+      Preconditions.checkNotNull(link);
+      Preconditions.checkNotNull(target);
       rootSymlinksBuilder.add(new SymlinkEntry(link, target));
       return this;
     }
 
-    /**
-     * Adds several root symlinks. Neither keys nor values may be null.
-     */
+    /** Adds several root symlinks. */
     public Builder addRootSymlinks(Map<PathFragment, Artifact> symlinks) {
       for (Map.Entry<PathFragment, Artifact> symlink : symlinks.entrySet()) {
         rootSymlinksBuilder.add(new SymlinkEntry(symlink.getKey(), symlink.getValue()));
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java
deleted file mode 100644
index 4c45aa9..0000000
--- a/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright 2017 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package com.google.devtools.build.lib.exec;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.io.LineProcessor;
-import com.google.devtools.build.lib.actions.ActionInput;
-import com.google.devtools.build.lib.actions.ActionInputFileCache;
-import com.google.devtools.build.lib.actions.ActionInputHelper;
-import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
-import com.google.devtools.build.lib.actions.RunfilesSupplier;
-import com.google.devtools.build.lib.actions.Spawn;
-import com.google.devtools.build.lib.analysis.AnalysisUtils;
-import com.google.devtools.build.lib.rules.fileset.FilesetActionContext;
-import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.PathFragment;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-/**
- * A helper class for spawn strategies to turn runfiles suppliers into input mappings. This class
- * performs no I/O operations, but only rearranges the files according to how the runfiles should be
- * laid out.
- */
-public class SpawnInputExpander {
-  public static final ActionInput EMPTY_FILE = ActionInputHelper.fromPath("/dev/null");
-
-  private final boolean strict;
-
-  /**
-   * Creates a new instance. If strict is true, then the expander checks for directories in runfiles
-   * and throws an exception if it finds any. Otherwise it silently ignores directories in runfiles
-   * and adds a mapping for them. At this time, directories in filesets are always silently added
-   * as mappings.
-   *
-   * <p>Directories in inputs are a correctness issue: Bazel only tracks dependencies at the action
-   * level, and it does not track dependencies on directories. Making a directory available to a
-   * spawn even though it's contents are not tracked as dependencies leads to incorrect incremental
-   * builds, since changes to the contents do not trigger action invalidation.
-   *
-   * <p>As such, all spawn strategies should always be strict and not make directories available to
-   * the subprocess. However, that's a breaking change, and therefore we make it depend on this flag
-   * for now.
-   */
-  public SpawnInputExpander(boolean strict) {
-    this.strict = strict;
-  }
-
-  private void addMapping(
-      Map<PathFragment, ActionInput> inputMappings,
-      PathFragment targetLocation,
-      ActionInput input) {
-    if (!inputMappings.containsKey(targetLocation)) {
-      inputMappings.put(targetLocation, input);
-    }
-  }
-
-  /** Adds runfiles inputs from runfilesSupplier to inputMappings. */
-  @VisibleForTesting
-  void addRunfilesToInputs(
-      Map<PathFragment, ActionInput> inputMap,
-      RunfilesSupplier runfilesSupplier,
-      ActionInputFileCache actionFileCache) throws IOException {
-    Map<PathFragment, Map<PathFragment, Artifact>> rootsAndMappings = null;
-    rootsAndMappings = runfilesSupplier.getMappings();
-
-    for (Entry<PathFragment, Map<PathFragment, Artifact>> rootAndMappings :
-        rootsAndMappings.entrySet()) {
-      PathFragment root = rootAndMappings.getKey();
-      for (Entry<PathFragment, Artifact> mapping : rootAndMappings.getValue().entrySet()) {
-        PathFragment targetPrefix = root.getRelative(mapping.getKey());
-        Artifact localArtifact = mapping.getValue();
-        if (localArtifact != null) {
-          if (strict && !actionFileCache.isFile(localArtifact)) {
-            throw new IOException("Not a file: " + localArtifact.getPath().getPathString());
-          }
-          addMapping(inputMap, targetPrefix, localArtifact);
-        } else {
-          addMapping(inputMap, targetPrefix, EMPTY_FILE);
-        }
-      }
-    }
-  }
-
-  /**
-   * Parses the fileset manifest file, adding to the inputMappings where
-   * appropriate. Lines referring to directories are recursed.
-   */
-  @VisibleForTesting
-  void parseFilesetManifest(
-      Map<PathFragment, ActionInput> inputMappings, Artifact manifest, String workspaceName)
-          throws IOException {
-    Path file = manifest.getRoot().getPath().getRelative(
-        AnalysisUtils.getManifestPathFromFilesetPath(manifest.getExecPath()).getPathString());
-    FileSystemUtils.asByteSource(file).asCharSource(UTF_8)
-        .readLines(new ManifestLineProcessor(inputMappings, workspaceName, manifest.getExecPath()));
-  }
-
-  private final class ManifestLineProcessor implements LineProcessor<Object> {
-    private final Map<PathFragment, ActionInput> inputMappings;
-    private final String workspaceName;
-    private final PathFragment targetPrefix;
-    private int lineNum = 0;
-
-    ManifestLineProcessor(
-        Map<PathFragment, ActionInput> inputMappings,
-        String workspaceName,
-        PathFragment targetPrefix) {
-      this.inputMappings = inputMappings;
-      this.workspaceName = workspaceName;
-      this.targetPrefix = targetPrefix;
-    }
-
-    @Override
-    public boolean processLine(String line) throws IOException {
-      if (++lineNum % 2 == 0) {
-        // Digest line, skip.
-        return true;
-      }
-      if (line.isEmpty()) {
-        return true;
-      }
-
-      ActionInput artifact;
-      PathFragment location;
-      int pos = line.indexOf(' ');
-      if (pos == -1) {
-        location = new PathFragment(line);
-        artifact = EMPTY_FILE;
-      } else {
-        String targetPath = line.substring(pos + 1);
-        if (targetPath.charAt(0) != '/') {
-          throw new IOException(String.format("runfiles target is not absolute: %s", targetPath));
-        }
-        artifact = targetPath.isEmpty() ? EMPTY_FILE : ActionInputHelper.fromPath(targetPath);
-
-        location = new PathFragment(line.substring(0, pos));
-        if (!workspaceName.isEmpty()) {
-          if (!location.getSegment(0).equals(workspaceName)) {
-            throw new IOException(
-                String.format(
-                    "fileset manifest line must start with '%s': '%s'", workspaceName, location));
-          } else {
-            // Erase "<workspaceName>/".
-            location = location.subFragment(1, location.segmentCount());
-          }
-        }
-      }
-
-      addMapping(inputMappings, targetPrefix.getRelative(location), artifact);
-      return true;
-    }
-
-    @Override
-    public Object getResult() {
-      return null; // Unused.
-    }
-  }
-
-  private void addInputs(
-      Map<PathFragment, ActionInput> inputMap, Spawn spawn, ArtifactExpander artifactExpander) {
-    List<ActionInput> inputs =
-        ActionInputHelper.expandArtifacts(spawn.getInputFiles(), artifactExpander);
-    for (ActionInput input : inputs) {
-      inputMap.put(input.getExecPath(), input);
-    }
-  }
-
-  /**
-   * Convert the inputs of the given spawn to a map from exec-root relative paths to action inputs.
-   * In some cases, this generates empty files, for which it uses {@link #EMPTY_FILE}.
-   */
-  public SortedMap<PathFragment, ActionInput> getInputMapping(
-      Spawn spawn, ArtifactExpander artifactExpander, ActionInputFileCache actionInputFileCache,
-      FilesetActionContext filesetContext)
-          throws IOException {
-    TreeMap<PathFragment, ActionInput> inputMap = new TreeMap<>();
-    addInputs(inputMap, spawn, artifactExpander);
-    addRunfilesToInputs(
-        inputMap, spawn.getRunfilesSupplier(), actionInputFileCache);
-    for (Artifact manifest : spawn.getFilesetManifests()) {
-      parseFilesetManifest(inputMap, manifest, filesetContext.getWorkspaceName());
-    }
-    return inputMap;
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java
index a8da48e..2743778 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java
@@ -17,8 +17,6 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet.Builder;
 import com.google.devtools.build.lib.actions.ActionExecutionContext;
-import com.google.devtools.build.lib.actions.ActionInput;
-import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.EnvironmentalExecException;
 import com.google.devtools.build.lib.actions.ExecException;
 import com.google.devtools.build.lib.actions.SandboxedSpawnActionContext;
@@ -30,17 +28,12 @@
 import com.google.devtools.build.lib.buildtool.BuildRequest;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.EventHandler;
-import com.google.devtools.build.lib.exec.SpawnInputExpander;
-import com.google.devtools.build.lib.rules.fileset.FilesetActionContext;
 import com.google.devtools.build.lib.util.io.OutErr;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicReference;
 
 /** Abstract common ancestor for sandbox strategies implementing the common parts. */
@@ -51,7 +44,7 @@
   private final Path execRoot;
   private final boolean verboseFailures;
   private final SandboxOptions sandboxOptions;
-  private final SpawnInputExpander spawnInputExpander;
+  private final SpawnHelpers spawnHelpers;
 
   public SandboxStrategy(
       BuildRequest buildRequest,
@@ -63,7 +56,7 @@
     this.execRoot = blazeDirs.getExecRoot();
     this.verboseFailures = verboseFailures;
     this.sandboxOptions = sandboxOptions;
-    this.spawnInputExpander = new SpawnInputExpander(/*strict=*/false);
+    this.spawnHelpers = new SpawnHelpers(blazeDirs.getExecRoot());
   }
 
   protected void runSpawn(
@@ -152,33 +145,7 @@
   public Map<PathFragment, Path> getMounts(Spawn spawn, ActionExecutionContext executionContext)
       throws ExecException {
     try {
-      Map<PathFragment, ActionInput> inputMap = spawnInputExpander
-          .getInputMapping(
-              spawn,
-              executionContext.getArtifactExpander(),
-              executionContext.getActionInputFileCache(),
-              executionContext.getExecutor().getContext(FilesetActionContext.class));
-      Map<PathFragment, Path> mounts = new TreeMap<>();
-      for (Map.Entry<PathFragment, ActionInput> e : inputMap.entrySet()) {
-        mounts.put(e.getKey(), execRoot.getRelative(e.getValue().getExecPath()));
-      }
-
-      // ActionInputHelper#expandArtifacts above expands empty TreeArtifacts into an empty list.
-      // However, actions that accept TreeArtifacts as inputs generally expect that the empty
-      // directory is created. So here we explicitly mount the directories of the TreeArtifacts as
-      // inputs.
-      for (ActionInput input : spawn.getInputFiles()) {
-        if (input instanceof Artifact && ((Artifact) input).isTreeArtifact()) {
-          List<Artifact> containedArtifacts = new ArrayList<>();
-          executionContext.getArtifactExpander().expand((Artifact) input, containedArtifacts);
-          // Attempting to mount a non-empty directory results in ERR_DIRECTORY_NOT_EMPTY, so we
-          // only mount empty TreeArtifacts as directories.
-          if (containedArtifacts.isEmpty()) {
-            inputMap.put(input.getExecPath(), input);
-          }
-        }
-      }
-      return mounts;
+      return spawnHelpers.getMounts(spawn, executionContext);
     } catch (IOException e) {
       throw new EnvironmentalExecException("Could not prepare mounts for sandbox execution", e);
     }
diff --git a/src/test/java/com/google/devtools/build/lib/exec/DigestTest.java b/src/test/java/com/google/devtools/build/lib/exec/DigestTest.java
index 27edca0..b610641 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/DigestTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/DigestTest.java
@@ -23,7 +23,6 @@
 import com.google.devtools.build.lib.testutil.Suite;
 import com.google.devtools.build.lib.testutil.TestSpec;
 import com.google.devtools.build.lib.util.Pair;
-import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.protobuf.ByteString;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -62,7 +61,7 @@
         "8b1a9953c4611296a827abf8c47804d7",
         Digest.fromContent("Hello".getBytes(UTF_8)).toStringUtf8());
 
-    assertEquals(UGLY_DIGEST, Digest.fromContent(UGLY.getBytes(UTF_8)).toStringUtf8());
+    assertEquals(UGLY_DIGEST, Digest.fromContent(UGLY.getBytes()).toStringUtf8());
 
     // ByteBuffer digest not idempotent because ByteBuffer manages a "position" internally.
     ByteBuffer buffer = ByteBuffer.wrap(UGLY.getBytes(UTF_8));
@@ -107,11 +106,6 @@
               public String getExecPathString() {
                 throw new UnsupportedOperationException();
               }
-
-              @Override
-              public PathFragment getExecPath() {
-                throw new UnsupportedOperationException();
-              }
             });
     assertEquals(UGLY_DIGEST, result.first.toStringUtf8());
     assertEquals(UGLY.length(), result.second.longValue());
diff --git a/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java b/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java
deleted file mode 100644
index fdfa655..0000000
--- a/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright 2017 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package com.google.devtools.build.lib.exec;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-import com.google.common.collect.Maps;
-import com.google.devtools.build.lib.actions.ActionInput;
-import com.google.devtools.build.lib.actions.ActionInputFileCache;
-import com.google.devtools.build.lib.actions.ActionInputHelper;
-import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.EmptyRunfilesSupplier;
-import com.google.devtools.build.lib.actions.Root;
-import com.google.devtools.build.lib.actions.RunfilesSupplier;
-import com.google.devtools.build.lib.analysis.Runfiles;
-import com.google.devtools.build.lib.analysis.RunfilesSupplierImpl;
-import com.google.devtools.build.lib.vfs.FileSystem;
-import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.PathFragment;
-import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.Mockito;
-
-/**
- * Tests for {@link SpawnInputExpander}.
- */
-@RunWith(JUnit4.class)
-public class SpawnInputExpanderTest {
-  private FileSystem fs;
-  private SpawnInputExpander expander;
-  private Map<PathFragment, ActionInput> inputMappings;
-
-  @Before
-  public final void createSpawnInputExpander() throws Exception  {
-    fs = new InMemoryFileSystem();
-    expander = new SpawnInputExpander(/*strict=*/true);
-    inputMappings = Maps.newHashMap();
-  }
-
-  private void scratchFile(String file, String... lines) throws Exception {
-    Path path = fs.getPath(file);
-    FileSystemUtils.createDirectoryAndParents(path.getParentDirectory());
-    FileSystemUtils.writeLinesAs(path, StandardCharsets.UTF_8, lines);
-  }
-
-  @Test
-  public void testEmptyRunfiles() throws Exception {
-    RunfilesSupplier supplier = EmptyRunfilesSupplier.INSTANCE;
-    expander.addRunfilesToInputs(inputMappings, supplier, null);
-    assertThat(inputMappings).isEmpty();
-  }
-
-  @Test
-  public void testRunfilesSingleFile() throws Exception {
-    Artifact artifact =
-        new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root")));
-    Runfiles runfiles = new Runfiles.Builder("workspace").addArtifact(artifact).build();
-    RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles);
-    ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class);
-    Mockito.when(mockCache.isFile(artifact)).thenReturn(true);
-
-    expander.addRunfilesToInputs(inputMappings, supplier, mockCache);
-    assertThat(inputMappings).hasSize(1);
-    assertThat(inputMappings)
-        .containsEntry(new PathFragment("runfiles/workspace/dir/file"), artifact);
-  }
-
-  @Test
-  public void testRunfilesDirectoryStrict() throws Exception {
-    Artifact artifact =
-        new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root")));
-    Runfiles runfiles = new Runfiles.Builder("workspace").addArtifact(artifact).build();
-    RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles);
-    ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class);
-    Mockito.when(mockCache.isFile(artifact)).thenReturn(false);
-
-    try {
-      expander.addRunfilesToInputs(inputMappings, supplier, mockCache);
-      fail();
-    } catch (IOException expected) {
-      assertThat(expected.getMessage().contains("Not a file: /root/dir/file")).isTrue();
-    }
-  }
-
-  @Test
-  public void testRunfilesDirectoryNonStrict() throws Exception {
-    Artifact artifact =
-        new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root")));
-    Runfiles runfiles = new Runfiles.Builder("workspace").addArtifact(artifact).build();
-    RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles);
-    ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class);
-    Mockito.when(mockCache.isFile(artifact)).thenReturn(false);
-
-    expander = new SpawnInputExpander(/*strict=*/false);
-    expander.addRunfilesToInputs(inputMappings, supplier, mockCache);
-    assertThat(inputMappings).hasSize(1);
-    assertThat(inputMappings)
-        .containsEntry(new PathFragment("runfiles/workspace/dir/file"), artifact);
-  }
-
-  @Test
-  public void testRunfilesTwoFiles() throws Exception {
-    Artifact artifact1 =
-        new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root")));
-    Artifact artifact2 =
-        new Artifact(fs.getPath("/root/dir/baz"), Root.asSourceRoot(fs.getPath("/root")));
-    Runfiles runfiles = new Runfiles.Builder("workspace")
-        .addArtifact(artifact1)
-        .addArtifact(artifact2)
-        .build();
-    RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles);
-    ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class);
-    Mockito.when(mockCache.isFile(artifact1)).thenReturn(true);
-    Mockito.when(mockCache.isFile(artifact2)).thenReturn(true);
-
-    expander.addRunfilesToInputs(inputMappings, supplier, mockCache);
-    assertThat(inputMappings).hasSize(2);
-    assertThat(inputMappings)
-        .containsEntry(new PathFragment("runfiles/workspace/dir/file"), artifact1);
-    assertThat(inputMappings)
-        .containsEntry(new PathFragment("runfiles/workspace/dir/baz"), artifact2);
-  }
-
-  @Test
-  public void testRunfilesSymlink() throws Exception {
-    Artifact artifact =
-        new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root")));
-    Runfiles runfiles = new Runfiles.Builder("workspace")
-        .addSymlink(new PathFragment("symlink"), artifact).build();
-    RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles);
-    ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class);
-    Mockito.when(mockCache.isFile(artifact)).thenReturn(true);
-
-    expander.addRunfilesToInputs(inputMappings, supplier, mockCache);
-    assertThat(inputMappings).hasSize(1);
-    assertThat(inputMappings)
-        .containsEntry(new PathFragment("runfiles/workspace/symlink"), artifact);
-  }
-
-  @Test
-  public void testRunfilesRootSymlink() throws Exception {
-    Artifact artifact =
-        new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root")));
-    Runfiles runfiles = new Runfiles.Builder("workspace")
-        .addRootSymlink(new PathFragment("symlink"), artifact).build();
-    RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles);
-    ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class);
-    Mockito.when(mockCache.isFile(artifact)).thenReturn(true);
-
-    expander.addRunfilesToInputs(inputMappings, supplier, mockCache);
-    assertThat(inputMappings).hasSize(2);
-    assertThat(inputMappings).containsEntry(new PathFragment("runfiles/symlink"), artifact);
-    // If there's no other entry, Runfiles adds an empty file in the workspace to make sure the
-    // directory gets created.
-    assertThat(inputMappings)
-        .containsEntry(
-            new PathFragment("runfiles/workspace/.runfile"), SpawnInputExpander.EMPTY_FILE);
-  }
-
-  @Test
-  public void testEmptyManifest() throws Exception {
-    // See AnalysisUtils for the mapping from "foo" to "_foo/MANIFEST".
-    scratchFile("/root/_foo/MANIFEST");
-
-    Artifact artifact =
-        new Artifact(fs.getPath("/root/foo"), Root.asSourceRoot(fs.getPath("/root")));
-    expander.parseFilesetManifest(inputMappings, artifact, "workspace");
-    assertThat(inputMappings).isEmpty();
-  }
-
-  @Test
-  public void testManifestWithSingleFile() throws Exception {
-    // See AnalysisUtils for the mapping from "foo" to "_foo/MANIFEST".
-    scratchFile(
-        "/root/_foo/MANIFEST",
-        "workspace/bar /dir/file",
-        "<some digest>");
-
-    Artifact artifact =
-        new Artifact(fs.getPath("/root/foo"), Root.asSourceRoot(fs.getPath("/root")));
-    expander.parseFilesetManifest(inputMappings, artifact, "workspace");
-    assertThat(inputMappings).hasSize(1);
-    assertThat(inputMappings)
-        .containsEntry(new PathFragment("foo/bar"), ActionInputHelper.fromPath("/dir/file"));
-  }
-
-  @Test
-  public void testManifestWithTwoFiles() throws Exception {
-    // See AnalysisUtils for the mapping from "foo" to "_foo/MANIFEST".
-    scratchFile(
-        "/root/_foo/MANIFEST",
-        "workspace/bar /dir/file",
-        "<some digest>",
-        "workspace/baz /dir/file",
-        "<some digest>");
-
-    Artifact artifact =
-        new Artifact(fs.getPath("/root/foo"), Root.asSourceRoot(fs.getPath("/root")));
-    expander.parseFilesetManifest(inputMappings, artifact, "workspace");
-    assertThat(inputMappings).hasSize(2);
-    assertThat(inputMappings)
-        .containsEntry(new PathFragment("foo/bar"), ActionInputHelper.fromPath("/dir/file"));
-    assertThat(inputMappings)
-        .containsEntry(new PathFragment("foo/baz"), ActionInputHelper.fromPath("/dir/file"));
-  }
-
-  @Test
-  public void testManifestWithDirectory() throws Exception {
-    // See AnalysisUtils for the mapping from "foo" to "_foo/MANIFEST".
-    scratchFile(
-        "/root/_foo/MANIFEST",
-        "workspace/bar /some",
-        "<some digest>");
-
-    Artifact artifact =
-        new Artifact(fs.getPath("/root/foo"), Root.asSourceRoot(fs.getPath("/root")));
-    expander.parseFilesetManifest(inputMappings, artifact, "workspace");
-    assertThat(inputMappings).hasSize(1);
-    assertThat(inputMappings)
-        .containsEntry(
-            new PathFragment("foo/bar"), ActionInputHelper.fromPath("/some"));
-  }
-}