Automated rollback of commit 16465d7613348e39e0bcdb22697cf67d257a3d91.

*** Reason for rollback ***

We need to reconsider this.

https://devblogs.microsoft.com/commandline/per-directory-case-sensitivity-and-wsl/ says case-sensitivity can be set per-directory. Requiring correct casing for every directory would make Bazel behave the same on Linux and Windows, and work with WSL-created paths.

*** Original change description ***

glob() now supports case-insensitive mode

The behavior is triggered by
FileSystem.isGlobCaseSensitive() returning false.

None of the production FileSystem implementaitions
return false yet, only some test implementations.

Motivation is to support case-insensitive glob()
on Windows.

See https://github.com/bazelbuild/bazel/issues/8705 and https://github.com/bazelbuild/bazel/issues/8759.

Next we need to add an incompatible flag that
enables this behavior, and add a relevant bit to
the WindowsFileSystem. See https://github.com/bazelbuild/bazel/issues/8767

We must also warn the user somehow if enabling
this feature would change the result of some
globs. A potential approach would be to glob
case-sensitively and case-insensitively at the
same time and warn the user if the results are
different.

PiperOrigin-RevId: 256556254
diff --git a/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java b/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
index db58cfc..8d1bb74 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
@@ -255,13 +255,7 @@
       }
       results.addAll(items);
     }
-
-    // TODO(laszlocsomor): set `caseSensitive` from the value of
-    // `--incompatible_windows_case_insensitive_glob` or from FileSystem.isGlobCaseSensitive()
-    // See https://github.com/bazelbuild/bazel/issues/8767
-    final boolean caseSensitive = true;
-
-    UnixGlob.removeExcludes(results, excludes, caseSensitive);
+    UnixGlob.removeExcludes(results, excludes);
     if (!allowEmpty && results.isEmpty()) {
       throw new BadGlobException(
           "all files in the glob have been excluded, but allow_empty is set to False.");
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java
index e40c76f..cabb54f 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java
@@ -43,10 +43,6 @@
  */
 public final class GlobFunction implements SkyFunction {
 
-  // TODO(laszlocsomor): we might need to create another regexPatternCache for case-insensitive
-  // glob() mode to avoid reading wrong cache results when the user changes the
-  // --incompatible_windows_case_ignoring_glob flag value between builds.
-  // See https://github.com/bazelbuild/bazel/issues/8767.
   private final ConcurrentHashMap<String, Pattern> regexPatternCache = new ConcurrentHashMap<>();
 
   private final boolean alwaysUseDirListing;
@@ -156,11 +152,6 @@
         }
       }
 
-      // TODO(laszlocsomor): set `caseSensitive` from the value of
-      // `--incompatible_windows_case_insensitive_glob` or from FileSystem.isGlobCaseSensitive()
-      // See https://github.com/bazelbuild/bazel/issues/8767
-      final boolean caseSensitive = true;
-
       // Now that we have the directory listing, we do three passes over it so as to maximize
       // skyframe batching:
       // (1) Process every dirent, keeping track of values we need to request if the dirent cannot
@@ -178,7 +169,7 @@
       for (Dirent dirent : listingValue.getDirents()) {
         Dirent.Type direntType = dirent.getType();
         String fileName = dirent.getName();
-        if (!UnixGlob.matches(patternHead, fileName, regexPatternCache, caseSensitive)) {
+        if (!UnixGlob.matches(patternHead, fileName, regexPatternCache)) {
           continue;
         }
 
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
index cfbe006..23a6c77 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
@@ -1011,13 +1011,7 @@
         if (legacyIncludesToken != null) {
           matches.addAll(delegate.fetch(legacyIncludesToken));
         }
-
-        // TODO(laszlocsomor): set `caseSensitive` from the value of
-        // `--incompatible_windows_case_insensitive_glob` or from FileSystem.isGlobCaseSensitive()
-        // See https://github.com/bazelbuild/bazel/issues/8767
-        final boolean caseSensitive = true;
-
-        UnixGlob.removeExcludes(matches, excludes, caseSensitive);
+        UnixGlob.removeExcludes(matches, excludes);
         List<String> result = new ArrayList<>(matches);
         // Skyframe glob results are unsorted. And we used a LegacyGlobber that doesn't sort.
         // Therefore, we want to unconditionally sort here.
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
index 7f6b6e0..abaa6b1 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
@@ -138,22 +138,6 @@
   public abstract boolean isFilePathCaseSensitive();
 
   /**
-   * Returns true if glob() is case-sensitive.
-   *
-   * <p>When glob() is case-sensitive, it will only match (or exclude) file "Foo" if the include (or
-   * exclude) pattern uses the same upper-case and lower-case letters.
-   *
-   * <p>When glob() is case-insensitive, it will match (or exclude) the file "Foo" even if the
-   * include (or exclude) pattern uses a different casing such as "foO".
-   */
-  // TODO(laszlocsomor): After `--incompatible_windows_case_insensitive_glob` is flipped to true,
-  // remove this method and all references to it and replace call sites with
-  // isFilePathCaseSensitive(). See https://github.com/bazelbuild/bazel/issues/8767
-  public boolean isGlobCaseSensitive() {
-    return true;
-  }
-
-  /**
    * Returns the type of the file system path belongs to.
    *
    * <p>The string returned is obtained directly from the operating system, so
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java b/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java
index a6355ef..d9175a2 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java
@@ -15,7 +15,6 @@
 package com.google.devtools.build.lib.vfs;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Ascii;
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
@@ -156,9 +155,9 @@
     return null;
   }
 
-  /** Calls {@link #matches(String, String, Map) matches(pattern, str, null, boolean)} */
-  public static boolean matches(String pattern, String str, boolean caseSensitive) {
-    return matches(pattern, str, null, caseSensitive);
+  /** Calls {@link #matches(String, String, Map) matches(pattern, str, null)} */
+  public static boolean matches(String pattern, String str) {
+    return matches(pattern, str, null);
   }
 
   /**
@@ -170,8 +169,7 @@
    * @param patternCache a cache from patterns to compiled Pattern objects, or {@code null} to skip
    *     caching
    */
-  public static boolean matches(
-      String pattern, String str, Map<String, Pattern> patternCache, boolean caseSensitive) {
+  public static boolean matches(String pattern, String str, Map<String, Pattern> patternCache) {
     if (pattern.length() == 0 || str.length() == 0) {
       return false;
     }
@@ -193,30 +191,30 @@
 
     // Common case: *.xyz
     if (pattern.charAt(0) == '*' && pattern.lastIndexOf('*') == 0) {
-      return endsWithCase(str, pattern.substring(1), caseSensitive);
+      return str.endsWith(pattern.substring(1));
     }
     // Common case: xyz*
     int lastIndex = pattern.length() - 1;
     // The first clause of this if statement is unnecessary, but is an
     // optimization--charAt runs faster than indexOf.
     if (pattern.charAt(lastIndex) == '*' && pattern.indexOf('*') == lastIndex) {
-      return startsWithCase(str, pattern.substring(0, lastIndex), caseSensitive);
+      return str.startsWith(pattern.substring(0, lastIndex));
     }
 
     Pattern regex =
         patternCache == null
-            ? makePatternFromWildcard(pattern, caseSensitive)
-            : patternCache.computeIfAbsent(pattern, p -> makePatternFromWildcard(p, caseSensitive));
+            ? makePatternFromWildcard(pattern)
+            : patternCache.computeIfAbsent(pattern, p -> makePatternFromWildcard(p));
     return regex.matcher(str).matches();
   }
 
   /**
-   * Returns a regular expression implementing a matcher for "pattern", in which "*" and "?" are
-   * wildcards.
+   * Returns a regular expression implementing a matcher for "pattern", in which
+   * "*" and "?" are wildcards.
    *
    * <p>e.g. "foo*bar?.java" -> "foo.*bar.\\.java"
    */
-  private static Pattern makePatternFromWildcard(String pattern, boolean caseSensitive) {
+  private static Pattern makePatternFromWildcard(String pattern) {
     StringBuilder regexp = new StringBuilder();
     for (int i = 0, len = pattern.length(); i < len; i++) {
       char c = pattern.charAt(i);
@@ -249,15 +247,7 @@
           regexp.append(c);
           break;
         default:
-          if (caseSensitive || !isAlphaAscii(c)) {
-            regexp.append(c);
-          } else {
-            regexp
-                .append('[')
-                .append(Ascii.toUpperCase(c))
-                .append(Ascii.toLowerCase(c))
-                .append(']');
-          }
+          regexp.append(c);
           break;
       }
     }
@@ -568,6 +558,7 @@
       if (baseStat == null || patterns.isEmpty()) {
         return Futures.immediateFuture(Collections.<Path>emptyList());
       }
+
       List<String[]> splitPatterns = checkAndSplitPatterns(patterns);
 
       // We do a dumb loop, even though it will likely duplicate logical work (note that the
@@ -576,7 +567,6 @@
       // glob [*/*.java, sub/*.java, */*.txt]).
       pendingOps.incrementAndGet();
       try {
-        final boolean caseSensitive = base.getFileSystem().isGlobCaseSensitive();
         for (String[] splitPattern : splitPatterns) {
           int numRecursivePatterns = 0;
           for (String pattern : splitPattern) {
@@ -584,12 +574,9 @@
               ++numRecursivePatterns;
             }
           }
-          GlobTaskContext context =
-              numRecursivePatterns > 1
-                  ? new RecursiveGlobTaskContext(
-                      splitPattern, excludeDirectories, caseSensitive, dirPred, syscalls)
-                  : new GlobTaskContext(
-                      splitPattern, excludeDirectories, caseSensitive, dirPred, syscalls);
+          GlobTaskContext context = numRecursivePatterns > 1
+              ? new RecursiveGlobTaskContext(splitPattern, excludeDirectories, dirPred, syscalls)
+              : new GlobTaskContext(splitPattern, excludeDirectories, dirPred, syscalls);
           context.queueGlob(base, baseStat.isDirectory(), 0);
         }
       } finally {
@@ -698,19 +685,16 @@
     private class GlobTaskContext {
       private final String[] patternParts;
       private final boolean excludeDirectories;
-      private final boolean caseSensitive;
       private final Predicate<Path> dirPred;
       private final FilesystemCalls syscalls;
 
       GlobTaskContext(
           String[] patternParts,
           boolean excludeDirectories,
-          boolean caseSensitive,
           Predicate<Path> dirPred,
           FilesystemCalls syscalls) {
         this.patternParts = patternParts;
         this.excludeDirectories = excludeDirectories;
-        this.caseSensitive = caseSensitive;
         this.dirPred = dirPred;
         this.syscalls = syscalls;
       }
@@ -760,10 +744,9 @@
       private RecursiveGlobTaskContext(
           String[] patternParts,
           boolean excludeDirectories,
-          boolean caseSensitive,
           Predicate<Path> dirPred,
           FilesystemCalls syscalls) {
-        super(patternParts, excludeDirectories, caseSensitive, dirPred, syscalls);
+        super(patternParts, excludeDirectories, dirPred, syscalls);
       }
 
       @Override
@@ -837,7 +820,7 @@
           // The file is a special file (fifo, etc.). No need to even match against the pattern.
           continue;
         }
-        if (matches(pattern, dent.getName(), cache, context.caseSensitive)) {
+        if (matches(pattern, dent.getName(), cache)) {
           Path child = base.getChild(dent.getName());
 
           if (childType == Dirent.Type.SYMLINK) {
@@ -885,17 +868,12 @@
    * Filters out exclude patterns from a Set of paths. Common cases such as wildcard-free patterns
    * or suffix patterns are special-cased to make this function efficient.
    */
-  public static void removeExcludes(
-      Set<String> paths, Collection<String> excludes, boolean caseSensitive) {
+  public static void removeExcludes(Set<String> paths, Collection<String> excludes) {
     ArrayList<String> complexPatterns = new ArrayList<>(excludes.size());
     Map<String, List<String>> starstarSlashStarHeadTailPairs = new HashMap<>();
     for (String exclude : excludes) {
       if (isWildcardFree(exclude)) {
-        if (caseSensitive) {
-          paths.remove(exclude);
-        } else {
-          paths.removeIf(p -> Ascii.equalsIgnoreCase(p, exclude));
-        }
+        paths.remove(exclude);
         continue;
       }
       int patternPos = exclude.indexOf("**/*");
@@ -912,9 +890,9 @@
     for (Map.Entry<String, List<String>> headTailPair : starstarSlashStarHeadTailPairs.entrySet()) {
       paths.removeIf(
           path -> {
-            if (startsWithCase(path, headTailPair.getKey(), caseSensitive)) {
+            if (path.startsWith(headTailPair.getKey())) {
               for (String tail : headTailPair.getValue()) {
-                if (endsWithCase(path, tail, caseSensitive)) {
+                if (path.endsWith(tail)) {
                   return true;
                 }
               }
@@ -931,7 +909,7 @@
         path -> {
           String[] segments = Iterables.toArray(Splitter.on('/').split(path), String.class);
           for (String[] splitPattern : splitPatterns) {
-            if (matchesPattern(splitPattern, segments, 0, 0, patternCache, caseSensitive)) {
+            if (matchesPattern(splitPattern, segments, 0, 0, patternCache)) {
               return true;
             }
           }
@@ -941,25 +919,19 @@
 
   /** Returns true if {@code pattern} matches {@code path} starting from the given segments. */
   private static boolean matchesPattern(
-      String[] pattern,
-      String[] path,
-      int i,
-      int j,
-      Map<String, Pattern> patternCache,
-      boolean caseSensitive) {
+      String[] pattern, String[] path, int i, int j, Map<String, Pattern> patternCache) {
     if (i == pattern.length) {
       return j == path.length;
     }
     if (pattern[i].equals("**")) {
-      return matchesPattern(pattern, path, i + 1, j, patternCache, caseSensitive)
-          || (j < path.length
-              && matchesPattern(pattern, path, i, j + 1, patternCache, caseSensitive));
+      return matchesPattern(pattern, path, i + 1, j, patternCache)
+          || (j < path.length && matchesPattern(pattern, path, i, j + 1, patternCache));
     }
     if (j == path.length) {
       return false;
     }
-    if (matches(pattern[i], path[j], patternCache, caseSensitive)) {
-      return matchesPattern(pattern, path, i + 1, j + 1, patternCache, caseSensitive);
+    if (matches(pattern[i], path[j], patternCache)) {
+      return matchesPattern(pattern, path, i + 1, j + 1, patternCache);
     }
     return false;
   }
@@ -967,28 +939,4 @@
   private static boolean isWildcardFree(String pattern) {
     return !pattern.contains("*") && !pattern.contains("?");
   }
-
-  @VisibleForTesting
-  static boolean startsWithCase(String s, String p, boolean caseSensitive) {
-    if (caseSensitive) {
-      return s.startsWith(p);
-    } else {
-      return s.length() >= p.length() && s.regionMatches(true, 0, p, 0, p.length());
-    }
-  }
-
-  @VisibleForTesting
-  static boolean endsWithCase(String s, String p, boolean caseSensitive) {
-    if (caseSensitive) {
-      return s.endsWith(p);
-    } else {
-      return s.length() >= p.length()
-          && s.regionMatches(true, s.length() - p.length(), p, 0, p.length());
-    }
-  }
-
-  @VisibleForTesting
-  static boolean isAlphaAscii(char c) {
-    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
-  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
index abb7c63..5b3669b 100644
--- a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
@@ -106,13 +106,6 @@
   }
 
   @Override
-  public boolean isGlobCaseSensitive() {
-    // TODO(laszlocsomor): return the opposite of `--incompatible_windows_case_insensitive_glob`
-    // here. See https://github.com/bazelbuild/bazel/issues/8767
-    return true;
-  }
-
-  @Override
   protected boolean fileIsSymbolicLink(File file) {
     try {
       if (isSymlinkOrJunction(file)) {
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 8199ba1..fbf4d42 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -9,7 +9,6 @@
 CROSS_PLATFORM_WINDOWS_TESTS = [
     "util/DependencySetWindowsTest.java",
     "vfs/PathFragmentWindowsTest.java",
-    "vfs/WindowsGlobTest.java",
     "vfs/WindowsPathTest.java",
 ]
 
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
index e30cb90..3d85f63 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
@@ -544,7 +544,7 @@
 
   @Test
   public void testMatchesCallWithNoCache() {
-    assertThat(UnixGlob.matches("*a*b", "CaCb", null, true)).isTrue();
+    assertThat(UnixGlob.matches("*a*b", "CaCb", null)).isTrue();
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java b/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java
index efa15f5..7eb2a66 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java
@@ -285,7 +285,7 @@
 
   @Test
   public void testMatchesCallWithNoCache() {
-    assertThat(UnixGlob.matches("*a*b", "CaCb", null, true)).isTrue();
+    assertThat(UnixGlob.matches("*a*b", "CaCb", null)).isTrue();
   }
 
   @Test
@@ -297,11 +297,11 @@
   public void testMatcherMethodRecursiveBelowDir() throws Exception {
     FileSystemUtils.createEmptyFile(tmpPath.getRelative("foo/file"));
     String pattern = "foo/**/*";
-    assertThat(UnixGlob.matches(pattern, "foo/bar", true)).isTrue();
-    assertThat(UnixGlob.matches(pattern, "foo/bar/baz", true)).isTrue();
-    assertThat(UnixGlob.matches(pattern, "foo", true)).isFalse();
-    assertThat(UnixGlob.matches(pattern, "foob", true)).isFalse();
-    assertThat(UnixGlob.matches("**/foo", "foo", true)).isTrue();
+    assertThat(UnixGlob.matches(pattern, "foo/bar")).isTrue();
+    assertThat(UnixGlob.matches(pattern, "foo/bar/baz")).isTrue();
+    assertThat(UnixGlob.matches(pattern, "foo")).isFalse();
+    assertThat(UnixGlob.matches(pattern, "foob")).isFalse();
+    assertThat(UnixGlob.matches("**/foo", "foo")).isTrue();
   }
 
   @Test
@@ -440,7 +440,7 @@
 
   private Collection<String> removeExcludes(ImmutableList<String> paths, String... excludes) {
     HashSet<String> pathSet = new HashSet<>(paths);
-    UnixGlob.removeExcludes(pathSet, ImmutableList.copyOf(excludes), true);
+    UnixGlob.removeExcludes(pathSet, ImmutableList.copyOf(excludes));
     return pathSet;
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/WindowsGlobTest.java b/src/test/java/com/google/devtools/build/lib/vfs/WindowsGlobTest.java
deleted file mode 100644
index 1303b4b..0000000
--- a/src/test/java/com/google/devtools/build/lib/vfs/WindowsGlobTest.java
+++ /dev/null
@@ -1,392 +0,0 @@
-// Copyright 2019 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.vfs;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.devtools.build.lib.clock.BlazeClock;
-import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicReference;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Tests glob() on Windows (when glob is case-insensitive). */
-@RunWith(JUnit4.class)
-public class WindowsGlobTest {
-
-  private static void assertMatches(
-      UnixGlob.FilesystemCalls fsCalls, Path root, String pattern, String... expecteds)
-      throws IOException {
-    AtomicReference<UnixGlob.FilesystemCalls> sysCalls = new AtomicReference<>(fsCalls);
-    assertThat(
-            Iterables.transform(
-                new UnixGlob.Builder(root)
-                    .addPattern(pattern)
-                    .setFilesystemCalls(sysCalls)
-                    .setExcludeDirectories(true)
-                    .glob(),
-                // Convert Paths to Strings, otherwise they'd be compared with the host system's
-                // Path
-                // comparison semantics.
-                a -> a.relativeTo(root).toString()))
-        .containsExactlyElementsIn(expecteds);
-  }
-
-  private static void assertExcludes(
-      Collection<String> unfiltered,
-      String exclusionPattern,
-      boolean caseSensitive,
-      Collection<String> expected) {
-    Set<String> matched = new HashSet<>(unfiltered);
-    UnixGlob.removeExcludes(matched, ImmutableList.of(exclusionPattern), caseSensitive);
-    assertThat(matched).containsExactlyElementsIn(expected);
-  }
-
-  @Test
-  public void testMatches() throws Exception {
-    assertThat(UnixGlob.matches("Foo/**", "Foo/Bar/a.txt", null, true)).isTrue();
-    assertThat(UnixGlob.matches("Foo/**", "Foo/Bar/a.txt", null, false)).isTrue();
-
-    assertThat(UnixGlob.matches("foo/**", "Foo/Bar/a.txt", null, true)).isFalse();
-    assertThat(UnixGlob.matches("foo/**", "Foo/Bar/a.txt", null, false)).isTrue();
-
-    assertThat(UnixGlob.matches("F*o*o/**", "Foo/Bar/a.txt", null, true)).isTrue();
-    assertThat(UnixGlob.matches("F*o*o/**", "Foo/Bar/a.txt", null, false)).isTrue();
-
-    assertThat(UnixGlob.matches("f*o*o/**", "Foo/Bar/a.txt", null, true)).isFalse();
-    assertThat(UnixGlob.matches("f*o*o/**", "Foo/Bar/a.txt", null, false)).isTrue();
-
-    assertThat(UnixGlob.matches("Foo/**", "Foo/Bar/a.txt", null, true)).isTrue();
-    assertThat(UnixGlob.matches("Foo/**", "Foo/Bar/a.txt", null, false)).isTrue();
-  }
-
-  @Test
-  public void testExcludes() throws Exception {
-    assertExcludes(
-        Arrays.asList("Foo/Bar/a.txt", "Foo/Bar/b.dat"),
-        "Foo/**/*.dat",
-        true,
-        Arrays.asList("Foo/Bar/a.txt"));
-    assertExcludes(
-        Arrays.asList("Foo/Bar/a.txt", "Foo/Bar/b.dat"),
-        "Foo/**/*.dat",
-        false,
-        Arrays.asList("Foo/Bar/a.txt"));
-
-    assertExcludes(
-        Arrays.asList("Foo/Bar/a.txt", "Foo/Bar/b.dat"),
-        "foo/**/*.dat",
-        true,
-        Arrays.asList("Foo/Bar/a.txt", "Foo/Bar/b.dat"));
-    assertExcludes(
-        Arrays.asList("Foo/Bar/a.txt", "Foo/Bar/b.dat"),
-        "foo/**/*.dat",
-        false,
-        Arrays.asList("Foo/Bar/a.txt"));
-  }
-
-  private enum MockStat implements FileStatus {
-    FILE(true, false),
-    DIR(false, true),
-    UNKNOWN(false, false);
-
-    private final boolean isFile;
-    private final boolean isDir;
-
-    private MockStat(boolean isFile, boolean isDir) {
-      this.isFile = isFile;
-      this.isDir = isDir;
-    }
-
-    @Override
-    public boolean isFile() {
-      return isFile;
-    }
-
-    @Override
-    public boolean isDirectory() {
-      return isDir;
-    }
-
-    @Override
-    public boolean isSymbolicLink() {
-      return false;
-    }
-
-    @Override
-    public boolean isSpecialFile() {
-      return false;
-    }
-
-    @Override
-    public long getSize() throws IOException {
-      return 0;
-    }
-
-    @Override
-    public long getLastModifiedTime() throws IOException {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public long getLastChangeTime() throws IOException {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public long getNodeId() throws IOException {
-      throw new UnsupportedOperationException();
-    }
-  }
-
-  private static FileSystem mockFs(boolean caseSensitive) throws IOException {
-    FileSystem fs =
-        new InMemoryFileSystem(BlazeClock.instance()) {
-          @Override
-          public boolean isFilePathCaseSensitive() {
-            return caseSensitive;
-          }
-
-          @Override
-          public boolean isGlobCaseSensitive() {
-            return isFilePathCaseSensitive();
-          }
-        };
-    fs.getPath("/globtmp/Foo/Bar").createDirectoryAndParents();
-    FileSystemUtils.writeContentAsLatin1(fs.getPath("/globtmp/Foo/Bar/a.txt"), "foo");
-    FileSystemUtils.writeContentAsLatin1(fs.getPath("/globtmp/Foo/Bar/b.dat"), "bar");
-    return fs;
-  }
-
-  private static Map<String, Collection<Dirent>> mockDirents(Path root) {
-    // Map keys must be Strings not Paths, lest they follow the host OS' case-sensitivity policy.
-    Map<String, Collection<Dirent>> d =
-        root.getFileSystem().isGlobCaseSensitive()
-            ? new HashMap<>()
-            : new TreeMap<>((String x, String y) -> x.compareToIgnoreCase(y));
-    d.put(
-        root.getParentDirectory().toString(),
-        ImmutableList.of(new Dirent(root.getBaseName(), Dirent.Type.DIRECTORY)));
-    d.put(root.toString(), ImmutableList.of(new Dirent("Foo", Dirent.Type.DIRECTORY)));
-    d.put(
-        root.getRelative("Foo").toString(),
-        ImmutableList.of(new Dirent("Bar", Dirent.Type.DIRECTORY)));
-    d.put(
-        root.getRelative("Foo/Bar").toString(),
-        ImmutableList.of(
-            new Dirent("a.txt", Dirent.Type.FILE), new Dirent("b.dat", Dirent.Type.FILE)));
-    return d;
-  }
-
-  private static Map<String, FileStatus> mockStats(Path root) {
-    // Map keys must be Strings not Paths, lest they follow the host OS' case-sensitivity policy.
-    Map<String, FileStatus> d =
-        root.getFileSystem().isGlobCaseSensitive()
-            ? new HashMap<>()
-            : new TreeMap<>((String x, String y) -> x.compareToIgnoreCase(y));
-    d.put(root.getParentDirectory().toString(), MockStat.DIR);
-    d.put(root.toString(), MockStat.DIR);
-    d.put(root.getRelative("Foo").toString(), MockStat.DIR);
-    d.put(root.getRelative("Foo/Bar").toString(), MockStat.DIR);
-    d.put(root.getRelative("Foo/Bar/a.txt").toString(), MockStat.FILE);
-    d.put(root.getRelative("Foo/Bar/b.dat").toString(), MockStat.FILE);
-    return d;
-  }
-
-  private static UnixGlob.FilesystemCalls mockFsCalls(Path root) {
-    return new UnixGlob.FilesystemCalls() {
-      // These maps use case-sensitive or case-insensitive key comparison depending on
-      // root.getFileSystem().isGlobCaseSensitive()
-      private final Map<String, Collection<Dirent>> dirents = mockDirents(root);
-      private final Map<String, FileStatus> stats = mockStats(root);
-
-      @Override
-      public Collection<Dirent> readdir(Path path) throws IOException {
-        String p = path.toString();
-        if (dirents.containsKey(p)) {
-          return dirents.get(p);
-        }
-        throw new IOException(p);
-      }
-
-      @Override
-      public FileStatus statIfFound(Path path, Symlinks symlinks) throws IOException {
-        String p = path.toString();
-        if (stats.containsKey(p)) {
-          return stats.get(p);
-        }
-        return MockStat.UNKNOWN;
-      }
-
-      @Override
-      public Dirent.Type getType(Path path, Symlinks symlinks) throws IOException {
-        String p = path.toString();
-        if (dirents.containsKey(p)) {
-          for (Dirent d : dirents.get(p)) {
-            if (d.getName().equals(path.getBaseName())) {
-              return d.getType();
-            }
-          }
-        }
-        throw new IOException(p);
-      }
-    };
-  }
-
-  @Test
-  public void testFoo() throws Exception {
-    FileSystem unixFs = mockFs(/* caseSensitive */ true);
-    FileSystem winFs = mockFs(/* caseSensitive */ false);
-
-    Path unixRoot = unixFs.getPath("/globtmp");
-    Path winRoot = winFs.getPath("/globtmp");
-
-    Path unixRoot2 = unixFs.getPath("/globTMP");
-    Path winRoot2 = winFs.getPath("/globTMP");
-
-    UnixGlob.FilesystemCalls unixFsCalls = mockFsCalls(unixRoot);
-    UnixGlob.FilesystemCalls winFsCalls = mockFsCalls(winRoot);
-
-    // Try a simple, non-recursive glob that matches no files. (Directories are exluded.)
-    assertMatches(unixFsCalls, unixRoot, "Foo/*");
-    assertMatches(winFsCalls, winRoot, "Foo/*");
-
-    // Try a simple, non-recursive glob that should match some files.
-    assertMatches(unixFsCalls, unixRoot, "Foo/*/*", "Foo/Bar/a.txt", "Foo/Bar/b.dat");
-    assertMatches(winFsCalls, winRoot, "Foo/*/*", "Foo/Bar/a.txt", "Foo/Bar/b.dat");
-
-    // Try a recursive glob.
-    assertMatches(unixFsCalls, unixRoot, "Foo/**", "Foo/Bar/a.txt", "Foo/Bar/b.dat");
-    assertMatches(winFsCalls, winRoot, "Foo/**", "Foo/Bar/a.txt", "Foo/Bar/b.dat");
-
-    // Try a recursive glob, but use incorrect casing in the pattern.
-    // The case-insensitive glob should match, but the results retain the casing of the pattern
-    // ("foO") because the glob logic checks its existence with 'statIfFound', and uses the name
-    // from the pattern and not from the filesystem.
-    // The **-matched parts use the actual casing ("Bar") because the glob does a 'readdir' to get
-    // these.
-    assertMatches(unixFsCalls, unixRoot, "foO/**");
-    assertMatches(winFsCalls, winRoot, "foO/**", "foO/Bar/a.txt", "foO/Bar/b.dat");
-
-    // Try the same with another path component in the glob pattern. The casing of that pattern
-    // should be retained in the results.
-    assertMatches(unixFsCalls, unixRoot, "foO/baR/*");
-    assertMatches(winFsCalls, winRoot, "foO/baR/*", "foO/baR/a.txt", "foO/baR/b.dat");
-
-    // Even if the root's casing is incorrect, the case-insensitive glob should match it.
-    assertMatches(unixFsCalls, unixRoot2, "**");
-    assertMatches(winFsCalls, winRoot2, "**", "Foo/Bar/a.txt", "Foo/Bar/b.dat");
-
-    // Try again the "wrong" root with a "wrong" first component. The result should retain the
-    // casing.
-    assertMatches(unixFsCalls, unixRoot2, "foO/**");
-    assertMatches(winFsCalls, winRoot2, "foO/**", "foO/Bar/a.txt", "foO/Bar/b.dat");
-
-    // Try the same with more "wrong" path components in the pattern. The results should retain all
-    // the casing.
-    assertMatches(unixFsCalls, unixRoot2, "foO/baR/A.TXT");
-    assertMatches(winFsCalls, winRoot2, "foO/baR/A.TXT", "foO/baR/A.TXT");
-
-    // Try a so-called "complex" pattern in the directory name. The glob logic creates a regex and
-    // matches it against the result of a 'readdir', so the result retains the filesystem casing.
-    assertMatches(unixFsCalls, unixRoot, "Foo/*R/*");
-    assertMatches(winFsCalls, winRoot, "Foo/*R/*", "Foo/Bar/a.txt", "Foo/Bar/b.dat");
-
-    // Try the same for a file name pattern. Again, the filesystem casing is used because the
-    // matching is done with a regex.
-    assertMatches(unixFsCalls, unixRoot, "Foo/Bar/*.TXT");
-    assertMatches(winFsCalls, winRoot, "Foo/Bar/*.TXT", "Foo/Bar/a.txt");
-
-    // Try the same with a recursive pattern.
-    assertMatches(unixFsCalls, unixRoot, "F*o*o/**", "Foo/Bar/a.txt", "Foo/Bar/b.dat");
-    assertMatches(winFsCalls, winRoot, "F*o*o/**", "Foo/Bar/a.txt", "Foo/Bar/b.dat");
-
-    // Try the same with wrong casing.
-    assertMatches(unixFsCalls, unixRoot, "f*o*O/**");
-    assertMatches(winFsCalls, winRoot, "f*o*O/**", "Foo/Bar/a.txt", "Foo/Bar/b.dat");
-
-    // A "complex" first pattern with the right casing, but wrongly-cased root.
-    assertMatches(unixFsCalls, unixRoot2, "F*o*o/**");
-    assertMatches(winFsCalls, winRoot2, "F*o*o/**", "Foo/Bar/a.txt", "Foo/Bar/b.dat");
-
-    // A "complex" first pattern with the wrong casing and wrongly-cased root.
-    assertMatches(unixFsCalls, unixRoot2, "f*o*O/**");
-    assertMatches(winFsCalls, winRoot2, "f*o*O/**", "Foo/Bar/a.txt", "Foo/Bar/b.dat");
-  }
-
-  @Test
-  public void testStartsWithCase() {
-    assertThat(UnixGlob.startsWithCase("", "", true)).isTrue();
-    assertThat(UnixGlob.startsWithCase("", "", false)).isTrue();
-
-    assertThat(UnixGlob.startsWithCase("Foo", "", true)).isTrue();
-    assertThat(UnixGlob.startsWithCase("Foo", "", false)).isTrue();
-
-    assertThat(UnixGlob.startsWithCase("", "Foo", true)).isFalse();
-    assertThat(UnixGlob.startsWithCase("", "Foo", false)).isFalse();
-
-    assertThat(UnixGlob.startsWithCase("Fo", "Foo", true)).isFalse();
-    assertThat(UnixGlob.startsWithCase("Fo", "Foo", false)).isFalse();
-
-    assertThat(UnixGlob.startsWithCase("Foo", "Foo", true)).isTrue();
-    assertThat(UnixGlob.startsWithCase("Foo", "Foo", false)).isTrue();
-
-    assertThat(UnixGlob.startsWithCase("Foox", "Foo", true)).isTrue();
-    assertThat(UnixGlob.startsWithCase("Foox", "Foo", false)).isTrue();
-
-    assertThat(UnixGlob.startsWithCase("xFoo", "Foo", true)).isFalse();
-    assertThat(UnixGlob.startsWithCase("xFoo", "Foo", false)).isFalse();
-
-    assertThat(UnixGlob.startsWithCase("Foox", "foO", true)).isFalse();
-    assertThat(UnixGlob.startsWithCase("Foox", "foO", false)).isTrue();
-  }
-
-  @Test
-  public void testEndsWithCase() {
-    assertThat(UnixGlob.endsWithCase("", "", true)).isTrue();
-    assertThat(UnixGlob.endsWithCase("", "", false)).isTrue();
-
-    assertThat(UnixGlob.endsWithCase("Foo", "", true)).isTrue();
-    assertThat(UnixGlob.endsWithCase("Foo", "", false)).isTrue();
-
-    assertThat(UnixGlob.endsWithCase("", "Foo", true)).isFalse();
-    assertThat(UnixGlob.endsWithCase("", "Foo", false)).isFalse();
-
-    assertThat(UnixGlob.endsWithCase("Fo", "Foo", true)).isFalse();
-    assertThat(UnixGlob.endsWithCase("Fo", "Foo", false)).isFalse();
-
-    assertThat(UnixGlob.endsWithCase("Foo", "Foo", true)).isTrue();
-    assertThat(UnixGlob.endsWithCase("Foo", "Foo", false)).isTrue();
-
-    assertThat(UnixGlob.endsWithCase("Foox", "Foo", true)).isFalse();
-    assertThat(UnixGlob.endsWithCase("Foox", "Foo", false)).isFalse();
-
-    assertThat(UnixGlob.endsWithCase("xFoo", "Foo", true)).isTrue();
-    assertThat(UnixGlob.endsWithCase("xFoo", "Foo", false)).isTrue();
-
-    assertThat(UnixGlob.endsWithCase("xFoo", "foO", true)).isFalse();
-    assertThat(UnixGlob.endsWithCase("xFoo", "foO", false)).isTrue();
-  }
-}