Part 1 of the Implementation for new 'subpackages()` built-in helper function.

Design proposal: https://docs.google.com/document/d/13UOT0GoQofxDW40ILzH2sWpUOmuYy6QZ7CUmhej9vgk/edit#

This CL modifies the globber infrastructure to support an additional mode of listing sub-directories.

* Add new Globber Operation enum allowing, Globber implementations to
  discriminate between glob, glob w/directories and the future sub-packages
  use-case.

* Modify UnixGlob to replace Predicate and bools with UnixGlobPathDiscriminator interface for:
  a) Determining whether to traverse a sub-directory (previously was lambda)
  b) function for determing what entries to include in the List<Path> produced by UnixGlob.globAsync.

  These allow relatively simple re-use of the same logic for both subpackages and glob

4) Add a few tests for UnixGlob to ensure both cases continue to work as expected.

PiperOrigin-RevId: 421125424
diff --git a/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java b/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java
index b830b661..786942f 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java
@@ -142,7 +142,7 @@
   @Test
   public void testIgnoredDirectory() throws Exception {
     createCache(PathFragment.create("isolated/foo"));
-    List<Path> paths = cache.safeGlobUnsorted("**/*.js", true).get();
+    List<Path> paths = cache.safeGlobUnsorted("**/*.js", Globber.Operation.FILES).get();
     assertPathsAre(
         paths,
         "/workspace/isolated/first.js",
@@ -153,7 +153,7 @@
 
   @Test
   public void testSafeGlob() throws Exception {
-    List<Path> paths = cache.safeGlobUnsorted("*.js", false).get();
+    List<Path> paths = cache.safeGlobUnsorted("*.js", Globber.Operation.FILES_AND_DIRS).get();
     assertPathsAre(paths,
         "/workspace/isolated/first.js", "/workspace/isolated/second.js");
   }
@@ -161,7 +161,9 @@
   @Test
   public void testSafeGlobInvalidPattern() throws Exception {
     String invalidPattern = "Foo?.txt";
-    assertThrows(BadGlobException.class, () -> cache.safeGlobUnsorted(invalidPattern, false).get());
+    assertThrows(
+        BadGlobException.class,
+        () -> cache.safeGlobUnsorted(invalidPattern, Globber.Operation.FILES_AND_DIRS).get());
   }
 
   @Test
@@ -181,38 +183,53 @@
     assertThat(cache.getKeySet()).isEmpty();
 
     cache.getGlobUnsorted("*.java");
-    assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false));
+    assertThat(cache.getKeySet())
+        .containsExactly(Pair.of("*.java", Globber.Operation.FILES_AND_DIRS));
 
     cache.getGlobUnsorted("*.java");
-    assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false));
+    assertThat(cache.getKeySet())
+        .containsExactly(Pair.of("*.java", Globber.Operation.FILES_AND_DIRS));
 
     cache.getGlobUnsorted("*.js");
-    assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false), Pair.of("*.js", false));
+    assertThat(cache.getKeySet())
+        .containsExactly(
+            Pair.of("*.java", Globber.Operation.FILES_AND_DIRS),
+            Pair.of("*.js", Globber.Operation.FILES_AND_DIRS));
 
-    cache.getGlobUnsorted("*.java", true);
-    assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false), Pair.of("*.js", false),
-        Pair.of("*.java", true));
+    cache.getGlobUnsorted("*.java", Globber.Operation.FILES);
+    assertThat(cache.getKeySet())
+        .containsExactly(
+            Pair.of("*.java", Globber.Operation.FILES_AND_DIRS),
+            Pair.of("*.js", Globber.Operation.FILES_AND_DIRS),
+            Pair.of("*.java", Globber.Operation.FILES));
 
     assertThrows(BadGlobException.class, () -> cache.getGlobUnsorted("invalid?"));
-    assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false), Pair.of("*.js", false),
-        Pair.of("*.java", true));
+    assertThat(cache.getKeySet())
+        .containsExactly(
+            Pair.of("*.java", Globber.Operation.FILES_AND_DIRS),
+            Pair.of("*.js", Globber.Operation.FILES_AND_DIRS),
+            Pair.of("*.java", Globber.Operation.FILES));
 
     cache.getGlobUnsorted("foo/first.*");
-    assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false), Pair.of("*.java", true),
-        Pair.of("*.js", false), Pair.of("foo/first.*", false));
+    assertThat(cache.getKeySet())
+        .containsExactly(
+            Pair.of("*.java", Globber.Operation.FILES_AND_DIRS),
+            Pair.of("*.java", Globber.Operation.FILES),
+            Pair.of("*.js", Globber.Operation.FILES_AND_DIRS),
+            Pair.of("foo/first.*", Globber.Operation.FILES_AND_DIRS));
   }
 
   @Test
   public void testGlob() throws Exception {
-    assertEmpty(cache.globUnsorted(list("*.java"), NONE, false, true));
+    assertEmpty(cache.globUnsorted(list("*.java"), NONE, Globber.Operation.FILES, true));
 
-    assertThat(cache.globUnsorted(list("*.*"), NONE, false, true))
+    assertThat(cache.globUnsorted(list("*.*"), NONE, Globber.Operation.FILES, true))
         .containsExactly("first.js", "first.txt", "second.js", "second.txt");
 
-    assertThat(cache.globUnsorted(list("*.*"), list("first.js"), false, true))
+    assertThat(cache.globUnsorted(list("*.*"), list("first.js"), Globber.Operation.FILES, true))
         .containsExactly("first.txt", "second.js", "second.txt");
 
-    assertThat(cache.globUnsorted(list("*.txt", "first.*"), NONE, false, true))
+    assertThat(cache.globUnsorted(list("*.txt", "first.*"), NONE, Globber.Operation.FILES, true))
         .containsExactly("first.txt", "second.txt", "first.js");
   }
 
@@ -225,13 +242,17 @@
 
   @Test
   public void testSingleFileExclude_star() throws Exception {
-    assertThat(cache.globUnsorted(list("*"), list("first.txt"), false, true))
+    assertThat(
+            cache.globUnsorted(
+                list("*"), list("first.txt"), Globber.Operation.FILES_AND_DIRS, true))
         .containsExactly("BUILD", "bar", "first.js", "foo", "second.js", "second.txt");
   }
 
   @Test
   public void testSingleFileExclude_starStar() throws Exception {
-    assertThat(cache.globUnsorted(list("**"), list("first.txt"), false, true))
+    assertThat(
+            cache.globUnsorted(
+                list("**"), list("first.txt"), Globber.Operation.FILES_AND_DIRS, true))
         .containsExactly(
             "BUILD",
             "bar",
@@ -247,48 +268,78 @@
 
   @Test
   public void testExcludeAll_star() throws Exception {
-    assertThat(cache.globUnsorted(list("*"), list("*"), false, true)).isEmpty();
+    assertThat(cache.globUnsorted(list("*"), list("*"), Globber.Operation.FILES_AND_DIRS, true))
+        .isEmpty();
   }
 
   @Test
   public void testExcludeAll_star_noMatchesAnyway() throws Exception {
-    assertThat(cache.globUnsorted(list("nope"), list("*"), false, true)).isEmpty();
+    assertThat(cache.globUnsorted(list("nope"), list("*"), Globber.Operation.FILES_AND_DIRS, true))
+        .isEmpty();
   }
 
   @Test
   public void testExcludeAll_starStar() throws Exception {
-    assertThat(cache.globUnsorted(list("**"), list("**"), false, true)).isEmpty();
+    assertThat(cache.globUnsorted(list("**"), list("**"), Globber.Operation.FILES_AND_DIRS, true))
+        .isEmpty();
   }
 
   @Test
   public void testExcludeAll_manual() throws Exception {
-    assertThat(cache.globUnsorted(list("**"), list("*", "*/*", "*/*/*"), false, true)).isEmpty();
+    assertThat(
+            cache.globUnsorted(
+                list("**"), list("*", "*/*", "*/*/*"), Globber.Operation.FILES_AND_DIRS, true))
+        .isEmpty();
   }
 
   @Test
   public void testSingleFileExcludeDoesntMatch() throws Exception {
-    assertThat(cache.globUnsorted(list("first.txt"), list("nope.txt"), false, true))
+    assertThat(
+            cache.globUnsorted(
+                list("first.txt"), list("nope.txt"), Globber.Operation.FILES_AND_DIRS, true))
         .containsExactly("first.txt");
   }
 
   @Test
   public void testExcludeDirectory() throws Exception {
-    assertThat(cache.globUnsorted(list("foo/*"), NONE, true, true))
+    assertThat(cache.globUnsorted(list("foo/*"), NONE, Globber.Operation.FILES, true))
         .containsExactly("foo/first.js", "foo/second.js");
-    assertThat(cache.globUnsorted(list("foo/*"), list("foo"), false, true))
+    assertThat(
+            cache.globUnsorted(list("foo/*"), list("foo"), Globber.Operation.FILES_AND_DIRS, true))
         .containsExactly("foo/first.js", "foo/second.js");
   }
 
   @Test
   public void testChildGlobWithChildExclude() throws Exception {
-    assertThat(cache.globUnsorted(list("foo/*"), list("foo/*"), false, true)).isEmpty();
     assertThat(
-            cache.globUnsorted(list("foo/first.js", "foo/second.js"), list("foo/*"), false, true))
+            cache.globUnsorted(
+                list("foo/*"), list("foo/*"), Globber.Operation.FILES_AND_DIRS, true))
         .isEmpty();
-    assertThat(cache.globUnsorted(list("foo/first.js"), list("foo/first.js"), false, true))
+    assertThat(
+            cache.globUnsorted(
+                list("foo/first.js", "foo/second.js"),
+                list("foo/*"),
+                Globber.Operation.FILES_AND_DIRS,
+                true))
         .isEmpty();
-    assertThat(cache.globUnsorted(list("foo/first.js"), list("*/first.js"), false, true)).isEmpty();
-    assertThat(cache.globUnsorted(list("foo/first.js"), list("*/*"), false, true)).isEmpty();
+    assertThat(
+            cache.globUnsorted(
+                list("foo/first.js"), list("foo/first.js"), Globber.Operation.FILES_AND_DIRS, true))
+        .isEmpty();
+    assertThat(
+            cache.globUnsorted(
+                list("foo/first.js"), list("*/first.js"), Globber.Operation.FILES_AND_DIRS, true))
+        .isEmpty();
+    assertThat(
+            cache.globUnsorted(
+                list("foo/first.js"), list("*/*"), Globber.Operation.FILES_AND_DIRS, true))
+        .isEmpty();
+  }
+
+  @Test
+  public void testSubpackages() throws Exception {
+    assertThat(cache.globUnsorted(list("**"), list(), Globber.Operation.SUBPACKAGES, true))
+        .containsExactly("sub");
   }
 
   private void assertEmpty(Collection<?> glob) {