Add a test for handling of `InconsistentFilesystemException` from underlying packages in `PrepareDepsOfTargetsUnderDirectoryFunction`.

This adds coverage for the crash fixed in https://github.com/bazelbuild/bazel/commit/43a5a035572c1fb52155c8957f80f29617fa81a4.

PiperOrigin-RevId: 436348711
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
index faff3c5..211193b 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -378,6 +378,8 @@
     srcs = ["PrepareDepsOfTargetsUnderDirectoryFunctionTest.java"],
     deps = [
         "//src/main/java/com/google/devtools/build/lib/cmdline",
+        "//src/main/java/com/google/devtools/build/lib/io:inconsistent_filesystem_exception",
+        "//src/main/java/com/google/devtools/build/lib/io:process_package_directory_exception",
         "//src/main/java/com/google/devtools/build/lib/pkgcache",
         "//src/main/java/com/google/devtools/build/lib/skyframe:collect_packages_under_directory_value",
         "//src/main/java/com/google/devtools/build/lib/skyframe:prepare_deps_of_targets_under_directory_value",
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfTargetsUnderDirectoryFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfTargetsUnderDirectoryFunctionTest.java
index 7d42e06..16901aa 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfTargetsUnderDirectoryFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfTargetsUnderDirectoryFunctionTest.java
@@ -25,8 +25,13 @@
 import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.RepositoryName;
+import com.google.devtools.build.lib.io.InconsistentFilesystemException;
+import com.google.devtools.build.lib.io.ProcessPackageDirectoryException;
 import com.google.devtools.build.lib.pkgcache.FilteringPolicies;
 import com.google.devtools.build.lib.pkgcache.FilteringPolicy;
+import com.google.devtools.build.lib.vfs.DelegateFileSystem;
+import com.google.devtools.build.lib.vfs.FileStatus;
+import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.Root;
@@ -36,6 +41,9 @@
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.build.skyframe.WalkableGraph;
 import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.annotation.Nullable;
+import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -46,6 +54,29 @@
 @RunWith(JUnit4.class)
 public class PrepareDepsOfTargetsUnderDirectoryFunctionTest extends BuildViewTestCase {
 
+  private final ConcurrentHashMap<PathFragment, FileStatus> injectedStats =
+      new ConcurrentHashMap<>();
+
+  @Override
+  protected FileSystem createFileSystem() {
+    return new DelegateFileSystem(super.createFileSystem()) {
+      @Override
+      protected FileStatus statIfFound(PathFragment path, boolean followSymlinks)
+          throws IOException {
+        @Nullable FileStatus injectedStat = injectedStats.remove(path);
+        if (injectedStat != null) {
+          return injectedStat;
+        }
+        return super.statIfFound(path, followSymlinks);
+      }
+    };
+  }
+
+  @After
+  public void checkNoInjectedStatsLeft() {
+    assertThat(injectedStats).isEmpty();
+  }
+
   private static SkyKey createCollectPackagesKey(
       Path root, PathFragment rootRelativePath, ImmutableSet<PathFragment> excludedPaths) {
     RootedPath rootedPath = RootedPath.toRootedPath(Root.fromPath(root), rootRelativePath);
@@ -74,17 +105,23 @@
         RepositoryName.MAIN, rootedPath, excludedPaths, filteringPolicy);
   }
 
-  private EvaluationResult<?> getEvaluationResult(SkyKey... keys) throws InterruptedException {
+  private EvaluationResult<PrepareDepsOfTargetsUnderDirectoryValue> getAndCheckEvaluationResult(
+      SkyKey... keys) throws InterruptedException {
+    EvaluationResult<PrepareDepsOfTargetsUnderDirectoryValue> evaluationResult =
+        getEvaluationResult(keys);
+    assertThatEvaluationResult(evaluationResult).hasNoError();
+    return evaluationResult;
+  }
+
+  private EvaluationResult<PrepareDepsOfTargetsUnderDirectoryValue> getEvaluationResult(
+      SkyKey... keys) throws InterruptedException {
     EvaluationContext evaluationContext =
         EvaluationContext.newBuilder()
             .setKeepGoing(false)
             .setNumThreads(SequencedSkyframeExecutor.DEFAULT_THREAD_COUNT)
             .setEventHandler(reporter)
             .build();
-    EvaluationResult<PrepareDepsOfTargetsUnderDirectoryValue> evaluationResult =
-        skyframeExecutor.getEvaluator().evaluate(ImmutableList.copyOf(keys), evaluationContext);
-    assertThatEvaluationResult(evaluationResult).hasNoError();
-    return evaluationResult;
+    return skyframeExecutor.getEvaluator().evaluate(ImmutableList.copyOf(keys), evaluationContext);
   }
 
   @Test
@@ -94,7 +131,7 @@
 
     // When package "a" is evaluated,
     SkyKey key = createPrepDepsKey(rootDirectory, PathFragment.create("a"));
-    EvaluationResult<?> evaluationResult = getEvaluationResult(key);
+    EvaluationResult<?> evaluationResult = getAndCheckEvaluationResult(key);
     WalkableGraph graph = Preconditions.checkNotNull(evaluationResult.getWalkableGraph());
 
     // Then the TransitiveTraversalValue for "@//a:a" is evaluated,
@@ -124,7 +161,7 @@
             PathFragment.create("a"),
             ImmutableSet.of(),
             FilteringPolicies.FILTER_TESTS);
-    EvaluationResult<?> evaluationResult = getEvaluationResult(key);
+    EvaluationResult<?> evaluationResult = getAndCheckEvaluationResult(key);
     WalkableGraph graph = Preconditions.checkNotNull(evaluationResult.getWalkableGraph());
 
     // Then the TransitiveTraversalValue for "@//a:a" is not evaluated,
@@ -163,7 +200,7 @@
     SkyKey collectkey =
         createCollectPackagesKey(
             rootDirectory, PathFragment.create("a"), ImmutableSet.of(excludedPathFragment));
-    EvaluationResult<?> evaluationResult = getEvaluationResult(key, collectkey);
+    EvaluationResult<?> evaluationResult = getAndCheckEvaluationResult(key, collectkey);
     CollectPackagesUnderDirectoryValue value =
         (CollectPackagesUnderDirectoryValue)
             evaluationResult
@@ -216,7 +253,7 @@
     SkyKey key = createPrepDepsKey(rootDirectory, PathFragment.create("a"), excludedPaths);
     SkyKey collectKey =
         createCollectPackagesKey(rootDirectory, PathFragment.create("a"), excludedPaths);
-    EvaluationResult<?> evaluationResult = getEvaluationResult(key, collectKey);
+    EvaluationResult<?> evaluationResult = getAndCheckEvaluationResult(key, collectKey);
     CollectPackagesUnderDirectoryValue value =
         (CollectPackagesUnderDirectoryValue)
             evaluationResult
@@ -259,4 +296,18 @@
     // And no package is under "a/b/d".
     assertThat(abValue.getSubdirectoryTransitivelyContainsPackagesOrErrors().get(abd)).isFalse();
   }
+
+  @Test
+  public void testInconsistentFileSystemExceptionFailsWithProperError() throws Exception {
+    Path buildFile = scratch.file("a/b/BUILD", "sh_library(name='b')");
+    SkyKey key = createPrepDepsKey(rootDirectory, PathFragment.create("a"));
+    // Inject a "file" stat for "a/b" directory to trigger a InconsistentFilesystemException.
+    injectedStats.put(buildFile.getParentDirectory().asFragment(), buildFile.stat());
+
+    EvaluationResult<?> evaluationResult = getEvaluationResult(key);
+
+    Exception e = evaluationResult.getError(key).getException();
+    assertThat(e).isInstanceOf(ProcessPackageDirectoryException.class);
+    assertThat(e).hasCauseThat().isInstanceOf(InconsistentFilesystemException.class);
+  }
 }