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/BUILD b/src/test/java/com/google/devtools/build/lib/packages/BUILD
index 61ef029..12a0a08 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/packages/BUILD
@@ -70,6 +70,7 @@
"//src/main/java/com/google/devtools/build/lib/exec:test_policy",
"//src/main/java/com/google/devtools/build/lib/packages",
"//src/main/java/com/google/devtools/build/lib/packages:exec_group",
+ "//src/main/java/com/google/devtools/build/lib/packages:globber",
"//src/main/java/com/google/devtools/build/lib/pkgcache",
"//src/main/java/com/google/devtools/build/lib/runtime/commands",
"//src/main/java/com/google/devtools/build/lib/skyframe:tests_for_target_pattern_value",
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) {
diff --git a/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java b/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
index d3df2e4..ee56bb8 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
@@ -1264,7 +1264,7 @@
executorService,
-1,
ThreadStateReceiver.NULL_INSTANCE);
- assertThat(globCache.globUnsorted(include, exclude, false, true))
+ assertThat(globCache.globUnsorted(include, exclude, Globber.Operation.FILES_AND_DIRS, true))
.containsExactlyElementsIn(expected);
} finally {
executorService.shutdownNow();
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 9a591cf..969be1e 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -170,6 +170,7 @@
"//src/main/java/com/google/devtools/build/lib/exec:single_build_file_cache",
"//src/main/java/com/google/devtools/build/lib/io:inconsistent_filesystem_exception",
"//src/main/java/com/google/devtools/build/lib/packages",
+ "//src/main/java/com/google/devtools/build/lib/packages:globber",
"//src/main/java/com/google/devtools/build/lib/packages/semantics",
"//src/main/java/com/google/devtools/build/lib/pkgcache",
"//src/main/java/com/google/devtools/build/lib/pkgcache:QueryTransitivePackagePreloader",
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/GlobDescriptorTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/GlobDescriptorTest.java
index 7eac47f..d2e7753 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/GlobDescriptorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/GlobDescriptorTest.java
@@ -17,6 +17,7 @@
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.packages.Globber;
import com.google.devtools.build.lib.skyframe.serialization.testutils.FsUtils;
import com.google.devtools.build.lib.skyframe.serialization.testutils.SerializationTester;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -38,13 +39,13 @@
Root.fromPath(FsUtils.TEST_FILESYSTEM.getPath("/packageRoot")),
PathFragment.create("subdir"),
"pattern",
- /*excludeDirs=*/ false),
+ Globber.Operation.FILES_AND_DIRS),
GlobDescriptor.create(
PackageIdentifier.create("@bar", PathFragment.create("//foo")),
Root.fromPath(FsUtils.TEST_FILESYSTEM.getPath("/anotherPackageRoot")),
PathFragment.create("anotherSubdir"),
"pattern",
- /*excludeDirs=*/ true))
+ Globber.Operation.FILES))
.setVerificationFunction(GlobDescriptorTest::verifyEquivalent);
FsUtils.addDependencies(serializationTester);
serializationTester.runTests();
@@ -62,22 +63,24 @@
Root.fromPath(FsUtils.TEST_FILESYSTEM.getPath("/packageRoot")),
PathFragment.create("subdir"),
"pattern",
- /*excludeDirs=*/ false);
+ Globber.Operation.FILES_AND_DIRS);
- GlobDescriptor sameCopy = GlobDescriptor.create(
- original.getPackageId(),
- original.getPackageRoot(),
- original.getSubdir(),
- original.getPattern(),
- original.excludeDirs());
+ GlobDescriptor sameCopy =
+ GlobDescriptor.create(
+ original.getPackageId(),
+ original.getPackageRoot(),
+ original.getSubdir(),
+ original.getPattern(),
+ original.globberOperation());
assertThat(sameCopy).isSameInstanceAs(original);
- GlobDescriptor diffCopy = GlobDescriptor.create(
- original.getPackageId(),
- original.getPackageRoot(),
- original.getSubdir(),
- original.getPattern(),
- !original.excludeDirs());
+ GlobDescriptor diffCopy =
+ GlobDescriptor.create(
+ original.getPackageId(),
+ original.getPackageRoot(),
+ original.getSubdir(),
+ original.getPattern(),
+ Globber.Operation.FILES);
assertThat(diffCopy).isNotEqualTo(original);
}
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 e451622..c66ec8a 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
@@ -32,6 +32,7 @@
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.events.NullEventHandler;
import com.google.devtools.build.lib.io.InconsistentFilesystemException;
+import com.google.devtools.build.lib.packages.Globber;
import com.google.devtools.build.lib.packages.RuleClassProvider;
import com.google.devtools.build.lib.packages.WorkspaceFileValue;
import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
@@ -332,12 +333,17 @@
// Each "equality group" forms a set of elements that are all equals() to one another,
// and also produce the same hashCode.
new EqualsTester()
- .addEqualityGroup(runGlob(false, "no-such-file")) // Matches nothing.
- .addEqualityGroup(runGlob(false, "BUILD"), runGlob(true, "BUILD")) // Matches BUILD.
- .addEqualityGroup(runGlob(false, "**")) // Matches lots of things.
.addEqualityGroup(
- runGlob(false, "f*o/bar*"),
- runGlob(false, "foo/bar*")) // Matches foo/bar and foo/barnacle.
+ runGlob("no-such-file", Globber.Operation.FILES_AND_DIRS)) // Matches nothing.
+ .addEqualityGroup(
+ runGlob("BUILD", Globber.Operation.FILES_AND_DIRS),
+ runGlob("BUILD", Globber.Operation.FILES)) // Matches BUILD.
+ .addEqualityGroup(
+ runGlob("**", Globber.Operation.FILES_AND_DIRS)) // Matches lots of things.
+ .addEqualityGroup(
+ runGlob("f*o/bar*", Globber.Operation.FILES_AND_DIRS),
+ runGlob(
+ "foo/bar*", Globber.Operation.FILES_AND_DIRS)) // Matches foo/bar and foo/barnacle.
.testEquals();
}
@@ -417,11 +423,11 @@
}
private void assertGlobMatches(String pattern, String... expecteds) throws Exception {
- assertGlobMatches(false, pattern, expecteds);
+ assertGlobMatches(pattern, Globber.Operation.FILES_AND_DIRS, expecteds);
}
- private void assertGlobMatches(boolean excludeDirs, String pattern, String... expecteds)
- throws Exception {
+ private void assertGlobMatches(
+ String pattern, Globber.Operation globberOperation, String... expecteds) throws Exception {
// The order requirement is not strictly necessary -- a change to GlobFunction semantics that
// changes the output order is fine, but we require that the order be the same here to detect
// potential non-determinism in output order, which would be bad.
@@ -430,27 +436,28 @@
// directories.
assertThat(
Iterables.transform(
- runGlob(excludeDirs, pattern).getMatches().toList(), Functions.toStringFunction()))
+ runGlob(pattern, globberOperation).getMatches().toList(),
+ Functions.toStringFunction()))
.containsExactlyElementsIn(ImmutableList.copyOf(expecteds))
.inOrder();
}
private void assertGlobWithoutDirsMatches(String pattern, String... expecteds) throws Exception {
- assertGlobMatches(true, pattern, expecteds);
+ assertGlobMatches(pattern, Globber.Operation.FILES, expecteds);
}
private void assertGlobsEqual(String pattern1, String pattern2) throws Exception {
- GlobValue value1 = runGlob(false, pattern1);
- GlobValue value2 = runGlob(false, pattern2);
+ GlobValue value1 = runGlob(pattern1, Globber.Operation.FILES_AND_DIRS);
+ GlobValue value2 = runGlob(pattern2, Globber.Operation.FILES_AND_DIRS);
new EqualsTester()
.addEqualityGroup(value1, value2)
.testEquals();
}
- private GlobValue runGlob(boolean excludeDirs, String pattern) throws Exception {
+ private GlobValue runGlob(String pattern, Globber.Operation globberOperation) throws Exception {
SkyKey skyKey =
GlobValue.key(
- PKG_ID, Root.fromPath(root), pattern, excludeDirs, PathFragment.EMPTY_FRAGMENT);
+ PKG_ID, Root.fromPath(root), pattern, globberOperation, PathFragment.EMPTY_FRAGMENT);
EvaluationResult<SkyValue> result =
evaluator.evaluate(ImmutableList.of(skyKey), EVALUATION_OPTIONS);
if (result.hasError()) {
@@ -533,7 +540,11 @@
InvalidGlobPatternException.class,
() ->
GlobValue.key(
- PKG_ID, Root.fromPath(root), pattern, false, PathFragment.EMPTY_FRAGMENT));
+ PKG_ID,
+ Root.fromPath(root),
+ pattern,
+ Globber.Operation.FILES_AND_DIRS,
+ PathFragment.EMPTY_FRAGMENT));
}
/**
@@ -681,7 +692,12 @@
differencer.inject(ImmutableMap.of(FileValue.key(pkgRootedPath), pkgDirValue));
String expectedMessage = "/root/workspace/pkg is no longer an existing directory";
SkyKey skyKey =
- GlobValue.key(PKG_ID, Root.fromPath(root), "*/foo", false, PathFragment.EMPTY_FRAGMENT);
+ GlobValue.key(
+ PKG_ID,
+ Root.fromPath(root),
+ "*/foo",
+ Globber.Operation.FILES_AND_DIRS,
+ PathFragment.EMPTY_FRAGMENT);
EvaluationResult<GlobValue> result =
evaluator.evaluate(ImmutableList.of(skyKey), EVALUATION_OPTIONS);
assertThat(result.hasError()).isTrue();
@@ -705,7 +721,12 @@
DirectoryListingStateValue.key(fooBarDirRootedPath), fooBarDirListingValue));
String expectedMessage = "/root/workspace/pkg/foo/bar/wiz is no longer an existing directory.";
SkyKey skyKey =
- GlobValue.key(PKG_ID, Root.fromPath(root), "**/wiz", false, PathFragment.EMPTY_FRAGMENT);
+ GlobValue.key(
+ PKG_ID,
+ Root.fromPath(root),
+ "**/wiz",
+ Globber.Operation.FILES_AND_DIRS,
+ PathFragment.EMPTY_FRAGMENT);
EvaluationResult<GlobValue> result =
evaluator.evaluate(ImmutableList.of(skyKey), EVALUATION_OPTIONS);
assertThat(result.hasError()).isTrue();
@@ -776,7 +797,11 @@
"readdir and stat disagree about whether " + fileRootedPath.asPath() + " is a symlink";
SkyKey skyKey =
GlobValue.key(
- PKG_ID, Root.fromPath(root), "foo/bar/wiz/*", false, PathFragment.EMPTY_FRAGMENT);
+ PKG_ID,
+ Root.fromPath(root),
+ "foo/bar/wiz/*",
+ Globber.Operation.FILES_AND_DIRS,
+ PathFragment.EMPTY_FRAGMENT);
EvaluationResult<GlobValue> result =
evaluator.evaluate(ImmutableList.of(skyKey), EVALUATION_OPTIONS);
assertThat(result.hasError()).isTrue();
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/BUILD b/src/test/java/com/google/devtools/build/lib/vfs/BUILD
index 76fdd0d..f764760 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/vfs/BUILD
@@ -67,6 +67,7 @@
"//src/test/java/com/google/devtools/build/lib/testutil:TestThread",
"//src/test/java/com/google/devtools/build/lib/testutil:TestUtils",
"//src/test/java/com/google/devtools/build/lib/vfs/util",
+ "//src/test/java/com/google/devtools/build/lib/vfs/util:test_glob_path_discriminator",
"//third_party:guava",
"//third_party:guava-testlib",
"//third_party:junit4",
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 28f6ab5..0463f57 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
@@ -16,12 +16,12 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
-import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.devtools.build.lib.testutil.TestUtils;
import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
+import com.google.devtools.build.lib.vfs.util.TestUnixGlobPathDiscriminator;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
@@ -37,15 +37,14 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Tests {@link UnixGlob}
- */
+/** Tests {@link UnixGlob} */
@RunWith(JUnit4.class)
public class GlobTest {
@@ -55,7 +54,7 @@
private Path throwOnStat = null;
@Before
- public final void initializeFileSystem() throws Exception {
+ public final void initializeFileSystem() throws Exception {
fs =
new InMemoryFileSystem(DigestHashFunction.SHA256) {
@Override
@@ -77,10 +76,12 @@
}
};
tmpPath = fs.getPath("/globtmp");
- for (String dir : ImmutableList.of("foo/bar/wiz",
- "foo/barnacle/wiz",
- "food/barnacle/wiz",
- "fool/barnacle/wiz")) {
+
+ final ImmutableList<String> directories =
+ ImmutableList.of(
+ "foo/bar/wiz", "foo/barnacle/wiz", "food/barnacle/wiz", "fool/barnacle/wiz");
+
+ for (String dir : directories) {
FileSystemUtils.createDirectoryAndParents(tmpPath.getRelative(dir));
}
FileSystemUtils.createEmptyFile(tmpPath.getRelative("foo/bar/wiz/file"));
@@ -93,7 +94,7 @@
@Test
public void testQuestionMarkMatch() throws Exception {
- assertGlobMatches("foo?", /* => */"food", "fool");
+ assertGlobMatches("foo?", /* => */ "food", "fool");
}
@Test
@@ -103,48 +104,48 @@
@Test
public void testStartsWithStar() throws Exception {
- assertGlobMatches("*oo", /* => */"foo");
+ assertGlobMatches("*oo", /* => */ "foo");
}
@Test
public void testStartsWithStarWithMiddleStar() throws Exception {
- assertGlobMatches("*f*o", /* => */"foo");
+ assertGlobMatches("*f*o", /* => */ "foo");
}
@Test
public void testEndsWithStar() throws Exception {
- assertGlobMatches("foo*", /* => */"foo", "food", "fool");
+ assertGlobMatches("foo*", /* => */ "foo", "food", "fool");
}
@Test
public void testEndsWithStarWithMiddleStar() throws Exception {
- assertGlobMatches("f*oo*", /* => */"foo", "food", "fool");
+ assertGlobMatches("f*oo*", /* => */ "foo", "food", "fool");
}
@Test
public void testMiddleStar() throws Exception {
- assertGlobMatches("f*o", /* => */"foo");
+ assertGlobMatches("f*o", /* => */ "foo");
}
@Test
public void testTwoMiddleStars() throws Exception {
- assertGlobMatches("f*o*o", /* => */"foo");
+ assertGlobMatches("f*o*o", /* => */ "foo");
}
@Test
public void testSingleStarPatternWithNamedChild() throws Exception {
- assertGlobMatches("*/bar", /* => */"foo/bar");
+ assertGlobMatches("*/bar", /* => */ "foo/bar");
}
@Test
public void testSingleStarPatternWithChildGlob() throws Exception {
- assertGlobMatches("*/bar*", /* => */
- "foo/bar", "foo/barnacle", "food/barnacle", "fool/barnacle");
+ assertGlobMatches(
+ "*/bar*", /* => */ "foo/bar", "foo/barnacle", "food/barnacle", "fool/barnacle");
}
@Test
public void testSingleStarAsChildGlob() throws Exception {
- assertGlobMatches("foo/*/wiz", /* => */"foo/bar/wiz", "foo/barnacle/wiz");
+ assertGlobMatches("foo/*/wiz", /* => */ "foo/bar/wiz", "foo/barnacle/wiz");
}
@Test
@@ -160,10 +161,93 @@
}
@Test
+ public void testFilteredResults_noDirs() throws Exception {
+
+ assertThat(
+ new UnixGlob.Builder(tmpPath)
+ .addPatterns("**")
+ .setPathDiscriminator(
+ new TestUnixGlobPathDiscriminator(
+ p -> /*traversalPredicate=*/ true,
+ /*resultPredicate=*/ (p, isDir) -> !isDir))
+ .globInterruptible())
+ .containsExactlyElementsIn(resolvePaths("foo/bar/wiz/file"));
+ }
+
+ @Test
+ public void testFilteredResults_noFiles() throws Exception {
+ assertThat(
+ new UnixGlob.Builder(tmpPath)
+ .addPatterns("**")
+ .setPathDiscriminator(
+ new TestUnixGlobPathDiscriminator(
+ /*traversalPredicate=*/ p -> true,
+ /*resultPredicate=*/ (p, isDir) -> isDir))
+ .globInterruptible())
+ .containsExactlyElementsIn(
+ resolvePaths(
+ "",
+ "foo",
+ "foo/bar",
+ "foo/bar/wiz",
+ "foo/barnacle",
+ "foo/barnacle/wiz",
+ "food",
+ "food/barnacle",
+ "food/barnacle/wiz",
+ "fool",
+ "fool/barnacle",
+ "fool/barnacle/wiz"));
+ }
+
+ @Test
+ public void testFilteredResults_pathMatch() throws Exception {
+
+ Path wanted = tmpPath.getRelative("food/barnacle/wiz");
+
+ assertThat(
+ new UnixGlob.Builder(tmpPath)
+ .addPatterns("**")
+ .setPathDiscriminator(
+ new TestUnixGlobPathDiscriminator(
+ /*traversalPredicate=*/ p -> true,
+ /*resultPredicate=*/ (path, isDir) -> path.equals(wanted)))
+ .globInterruptible())
+ .containsExactly(wanted);
+ }
+
+ @Test
+ public void testTraversal_onlyFoo() throws Exception {
+ // Use a directory traversal filter to only walk the root dir and "foo", but not "fool or "food"
+ // So we'll end up the directories, "fool" and "food", but not sub-dirs.
+ assertThat(
+ new UnixGlob.Builder(tmpPath)
+ .addPatterns("**")
+ .setPathDiscriminator(
+ new TestUnixGlobPathDiscriminator(
+ /*traversalPredicate=*/ path ->
+ path.equals(tmpPath)
+ || path.getPathString().contains("foo/")
+ || path.getPathString().endsWith("foo"),
+ /*resultPredicate=*/ (x, isDir) -> true))
+ .globInterruptible())
+ .containsExactlyElementsIn(
+ resolvePaths(
+ "",
+ "foo",
+ "foo/bar",
+ "foo/bar/wiz",
+ "foo/bar/wiz/file",
+ "foo/barnacle",
+ "foo/barnacle/wiz",
+ "fool",
+ "food"));
+ }
+
+ @Test
public void testGlobWithNonExistentBase() throws Exception {
- Collection<Path> globResult = UnixGlob.forPath(fs.getPath("/does/not/exist"))
- .addPattern("*.txt")
- .globInterruptible();
+ Collection<Path> globResult =
+ UnixGlob.forPath(fs.getPath("/does/not/exist")).addPattern("*.txt").globInterruptible();
assertThat(globResult).isEmpty();
}
@@ -172,27 +256,19 @@
assertGlobMatches("foo/bar/wiz/file/*" /* => nothing */);
}
- private void assertGlobMatches(String pattern, String... expecteds)
- throws Exception {
+ private void assertGlobMatches(String pattern, String... expecteds) throws Exception {
assertGlobMatches(Collections.singleton(pattern), expecteds);
}
- private void assertGlobMatches(Collection<String> pattern,
- String... expecteds)
- throws Exception {
- assertThat(
- new UnixGlob.Builder(tmpPath)
- .addPatterns(pattern)
- .globInterruptible())
- .containsExactlyElementsIn(resolvePaths(expecteds));
+ private void assertGlobMatches(Collection<String> pattern, String... expecteds) throws Exception {
+ assertThat(new UnixGlob.Builder(tmpPath).addPatterns(pattern).globInterruptible())
+ .containsExactlyElementsIn(resolvePaths(expecteds));
}
private Set<Path> resolvePaths(String... relativePaths) {
Set<Path> expectedFiles = new HashSet<>();
for (String expected : relativePaths) {
- Path file = expected.equals(".")
- ? tmpPath
- : tmpPath.getRelative(expected);
+ Path file = expected.equals(".") ? tmpPath : tmpPath.getRelative(expected);
expectedFiles.add(file);
}
return expectedFiles;
@@ -270,9 +346,7 @@
assertIllegalPattern("foo//bar");
}
- /**
- * Tests that globs can contain Java regular expression special characters
- */
+ /** Tests that globs can contain Java regular expression special characters */
@Test
public void testSpecialRegexCharacter() throws Exception {
Path tmpPath2 = fs.getPath("/globtmp2");
@@ -357,19 +431,18 @@
@Test
public void testMultiplePatternsWithOverlap() throws Exception {
- assertGlobMatchesAnyOrder(Lists.newArrayList("food", "foo?"),
- "food", "fool");
- assertGlobMatchesAnyOrder(Lists.newArrayList("food", "?ood", "f??d"),
- "food");
- assertThat(resolvePaths("food", "fool", "foo")).containsExactlyElementsIn(
- new UnixGlob.Builder(tmpPath).addPatterns("food", "xxx", "*").glob());
-
+ assertGlobMatchesAnyOrder(Lists.newArrayList("food", "foo?"), "food", "fool");
+ assertGlobMatchesAnyOrder(Lists.newArrayList("food", "?ood", "f??d"), "food");
+ assertThat(resolvePaths("food", "fool", "foo"))
+ .containsExactlyElementsIn(
+ new UnixGlob.Builder(tmpPath).addPatterns("food", "xxx", "*").glob());
}
- private void assertGlobMatchesAnyOrder(ArrayList<String> patterns,
- String... paths) throws Exception {
- assertThat(resolvePaths(paths)).containsExactlyElementsIn(
- new UnixGlob.Builder(tmpPath).addPatterns(patterns).globInterruptible());
+ private void assertGlobMatchesAnyOrder(ArrayList<String> patterns, String... paths)
+ throws Exception {
+ assertThat(resolvePaths(paths))
+ .containsExactlyElementsIn(
+ new UnixGlob.Builder(tmpPath).addPatterns(patterns).globInterruptible());
}
private void assertIllegalPattern(String pattern) throws Exception {
@@ -419,16 +492,20 @@
Predicate<Path> interrupterPredicate =
new Predicate<Path>() {
@Override
- public boolean apply(Path input) {
+ public boolean test(Path input) {
mainThread.interrupt();
return true;
}
};
+ UnixGlobPathDiscriminator interrupterDiscriminator =
+ new TestUnixGlobPathDiscriminator(
+ /*traversalPredicate=*/ interrupterPredicate, /*resultPredicate=*/ (x, isDir) -> true);
+
Future<?> globResult =
new UnixGlob.Builder(tmpPath)
.addPattern("**")
- .setDirectoryFilter(interrupterPredicate)
+ .setPathDiscriminator(interrupterDiscriminator)
.setExecutor(executor)
.globAsync();
assertThrows(InterruptedException.class, () -> globResult.get());
@@ -450,20 +527,25 @@
final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
final AtomicBoolean sentInterrupt = new AtomicBoolean(false);
- Predicate<Path> interrupterPredicate = new Predicate<Path>() {
- @Override
- public boolean apply(Path input) {
- if (!sentInterrupt.getAndSet(true)) {
- mainThread.interrupt();
- }
- return true;
- }
- };
+ Predicate<Path> interrupterPredicate =
+ new Predicate<Path>() {
+ @Override
+ public boolean test(Path input) {
+ if (!sentInterrupt.getAndSet(true)) {
+ mainThread.interrupt();
+ }
+ return true;
+ }
+ };
+
+ UnixGlobPathDiscriminator interrupterDiscriminator =
+ new TestUnixGlobPathDiscriminator(
+ /*traversalPredicate=*/ interrupterPredicate, /*resultPredicate=*/ (x, isDir) -> true);
List<Path> result =
new UnixGlob.Builder(tmpPath)
.addPatterns("**", "*")
- .setDirectoryFilter(interrupterPredicate)
+ .setPathDiscriminator(interrupterDiscriminator)
.setExecutor(executor)
.glob();
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/NativePathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/NativePathTest.java
index ab515c0..d7e0bd3 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/NativePathTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/NativePathTest.java
@@ -20,6 +20,7 @@
import com.google.common.testing.EqualsTester;
import com.google.devtools.build.lib.testutil.TestUtils;
import com.google.devtools.build.lib.vfs.util.FileSystems;
+import com.google.devtools.build.lib.vfs.util.TestUnixGlobPathDiscriminator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -133,16 +134,17 @@
Collection<Path> onlyFiles =
UnixGlob.forPath(fs.getPath(tmpDir.getPath()))
- .addPattern("*")
- .setExcludeDirectories(true)
- .globInterruptible();
+ .addPattern("*")
+ .setPathDiscriminator(
+ new TestUnixGlobPathDiscriminator(p -> true, (p, isDir) -> !isDir))
+ .globInterruptible();
assertPathSet(onlyFiles, aFile.getPath());
Collection<Path> directoriesToo =
UnixGlob.forPath(fs.getPath(tmpDir.getPath()))
- .addPattern("*")
- .setExcludeDirectories(false)
- .globInterruptible();
+ .addPattern("*")
+ .setPathDiscriminator(new TestUnixGlobPathDiscriminator(p -> true, (p, isDir) -> true))
+ .globInterruptible();
assertPathSet(directoriesToo, aFile.getPath(), aDirectory.getPath());
}
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/RecursiveGlobTest.java b/src/test/java/com/google/devtools/build/lib/vfs/RecursiveGlobTest.java
index 1c2ac8a..07abb57 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/RecursiveGlobTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/RecursiveGlobTest.java
@@ -20,6 +20,7 @@
import com.google.common.collect.Lists;
import com.google.devtools.build.lib.clock.BlazeClock;
import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
+import com.google.devtools.build.lib.vfs.util.TestUnixGlobPathDiscriminator;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -136,10 +137,12 @@
@Test
public void testRecursiveGlobsAreOptimized() throws Exception {
- long numGlobTasks = new UnixGlob.Builder(tmpPath)
- .addPattern("**")
- .setExcludeDirectories(false)
- .globInterruptibleAndReturnNumGlobTasksForTesting();
+ long numGlobTasks =
+ new UnixGlob.Builder(tmpPath)
+ .addPattern("**")
+ .setPathDiscriminator(
+ new TestUnixGlobPathDiscriminator(p -> true, (p, isDir) -> !isDir))
+ .globInterruptibleAndReturnNumGlobTasksForTesting();
// The old glob implementation used to use 41 total glob tasks.
// Yes, checking for an exact value here is super brittle, but it lets us catch performance
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/util/BUILD b/src/test/java/com/google/devtools/build/lib/vfs/util/BUILD
index 379d2ea..30ef7d8 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/util/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/vfs/util/BUILD
@@ -26,7 +26,10 @@
java_library(
name = "util_internal",
testonly = 1,
- srcs = glob(["*.java"]),
+ srcs = [
+ "FileSystems.java",
+ "FsApparatus.java",
+ ],
deps = [
"//src/main/java/com/google/devtools/build/lib/clock",
"//src/main/java/com/google/devtools/build/lib/util:os",
@@ -38,3 +41,13 @@
"//src/test/java/com/google/devtools/build/lib/testutil:TestUtils",
],
)
+
+java_library(
+ name = "test_glob_path_discriminator",
+ testonly = 1,
+ srcs = ["TestUnixGlobPathDiscriminator.java"],
+ deps = [
+ "//src/main/java/com/google/devtools/build/lib/vfs",
+ "//third_party:error_prone_annotations",
+ ],
+)
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/util/TestUnixGlobPathDiscriminator.java b/src/test/java/com/google/devtools/build/lib/vfs/util/TestUnixGlobPathDiscriminator.java
new file mode 100644
index 0000000..50d81d6
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/vfs/util/TestUnixGlobPathDiscriminator.java
@@ -0,0 +1,47 @@
+// Copyright 2022 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.util;
+
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.UnixGlobPathDiscriminator;
+import com.google.errorprone.annotations.CheckReturnValue;
+import java.util.function.BiPredicate;
+import java.util.function.Predicate;
+
+/**
+ * Test version of UnixGlobPathDiscriminator that accepts predicate/bipredicate for handling
+ * specific use-cases without creating a new class.
+ */
+@CheckReturnValue
+public final class TestUnixGlobPathDiscriminator implements UnixGlobPathDiscriminator {
+
+ private final Predicate<Path> traversalPredicate;
+ private final BiPredicate<Path, Boolean> resultPredicate;
+
+ public TestUnixGlobPathDiscriminator(
+ Predicate<Path> traversalPredicate, BiPredicate<Path, Boolean> resultPredicate) {
+ this.traversalPredicate = traversalPredicate;
+ this.resultPredicate = resultPredicate;
+ }
+
+ @Override
+ public boolean shouldTraverseDirectory(Path path) {
+ return traversalPredicate.test(path);
+ }
+
+ @Override
+ public boolean shouldIncludePathInResult(Path path, boolean isDirectory) {
+ return resultPredicate.test(path, isDirectory);
+ }
+}