| // Copyright 2014 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 static org.junit.Assert.fail; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.common.collect.Lists; | |
| import com.google.devtools.build.lib.util.BlazeClock; | |
| import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; | |
| import org.junit.Before; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.JUnit4; | |
| import java.util.Collection; | |
| import java.util.HashSet; | |
| import java.util.Set; | |
| /** | |
| * Tests {@link UnixGlob} recursive globs. | |
| */ | |
| @RunWith(JUnit4.class) | |
| public class RecursiveGlobTest { | |
| private Path tmpPath; | |
| private FileSystem fileSystem; | |
| @Before | |
| public final void initializeFileSystem() throws Exception { | |
| fileSystem = new InMemoryFileSystem(BlazeClock.instance()); | |
| tmpPath = fileSystem.getPath("/rglobtmp"); | |
| for (String dir : ImmutableList.of("foo/bar/wiz", | |
| "foo/baz/wiz", | |
| "foo/baz/quip/wiz", | |
| "food/baz/wiz", | |
| "fool/baz/wiz")) { | |
| FileSystemUtils.createDirectoryAndParents(tmpPath.getRelative(dir)); | |
| } | |
| FileSystemUtils.createEmptyFile(tmpPath.getRelative("foo/bar/wiz/file")); | |
| } | |
| @Test | |
| public void testDoubleStar() throws Exception { | |
| assertGlobMatches("**", ".", "foo", "foo/bar", "foo/bar/wiz", "foo/baz", "foo/baz/quip", | |
| "foo/baz/quip/wiz", "foo/baz/wiz", "foo/bar/wiz/file", "food", "food/baz", | |
| "food/baz/wiz", "fool", "fool/baz", "fool/baz/wiz"); | |
| } | |
| @Test | |
| public void testDoubleDoubleStar() throws Exception { | |
| assertGlobMatches("**/**", ".", "foo", "foo/bar", "foo/bar/wiz", "foo/baz", "foo/baz/quip", | |
| "foo/baz/quip/wiz", "foo/baz/wiz", "foo/bar/wiz/file", "food", "food/baz", | |
| "food/baz/wiz", "fool", "fool/baz", "fool/baz/wiz"); | |
| } | |
| @Test | |
| public void testDirectoryWithDoubleStar() throws Exception { | |
| assertGlobMatches("foo/**", "foo", "foo/bar", "foo/bar/wiz", "foo/baz", "foo/baz/quip", | |
| "foo/baz/quip/wiz", "foo/baz/wiz", "foo/bar/wiz/file"); | |
| } | |
| @Test | |
| public void testIllegalPatterns() throws Exception { | |
| for (String prefix : Lists.newArrayList("", "*/", "**/", "ba/")) { | |
| String suffix = ("/" + prefix).substring(0, prefix.length()); | |
| for (String pattern : Lists.newArrayList("**fo", "fo**", "**fo**", "fo**fo", "fo**fo**fo")) { | |
| assertIllegalWildcard(prefix + pattern); | |
| assertIllegalWildcard(pattern + suffix); | |
| } | |
| } | |
| } | |
| @Test | |
| public void testDoubleStarPatternWithNamedChild() throws Exception { | |
| assertGlobMatches("**/bar", "foo/bar"); | |
| } | |
| @Test | |
| public void testDoubleStarPatternWithChildGlob() throws Exception { | |
| assertGlobMatches("**/ba*", | |
| "foo/bar", "foo/baz", "food/baz", "fool/baz"); | |
| } | |
| @Test | |
| public void testDoubleStarAsChildGlob() throws Exception { | |
| assertGlobMatches("foo/**/wiz", "foo/bar/wiz", "foo/baz/quip/wiz", "foo/baz/wiz"); | |
| } | |
| @Test | |
| public void testDoubleStarUnderNonexistentDirectory() throws Exception { | |
| assertGlobMatches("not-there/**" /* => nothing */); | |
| } | |
| @Test | |
| public void testDoubleStarGlobWithNonExistentBase() throws Exception { | |
| Collection<Path> globResult = UnixGlob.forPath(fileSystem.getPath("/does/not/exist")) | |
| .addPattern("**") | |
| .globInterruptible(); | |
| assertThat(globResult).isEmpty(); | |
| } | |
| @Test | |
| public void testDoubleStarUnderFile() throws Exception { | |
| assertGlobMatches("foo/bar/wiz/file/**" /* => nothing */); | |
| } | |
| private void assertGlobMatches(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); | |
| expectedFiles.add(file); | |
| } | |
| return expectedFiles; | |
| } | |
| @Test | |
| public void testRecursiveGlobsAreOptimized() throws Exception { | |
| long numGlobTasks = new UnixGlob.Builder(tmpPath) | |
| .addPattern("**") | |
| .setExcludeDirectories(false) | |
| .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 | |
| // regressions. In other words, if you're a developer reading this comment because this test | |
| // case is failing, you should be very sure you know what you're doing before you change the | |
| // expectation of the test. | |
| assertThat(numGlobTasks).isEqualTo(28); | |
| } | |
| private void assertIllegalWildcard(String pattern) | |
| throws Exception { | |
| try { | |
| new UnixGlob.Builder(tmpPath) | |
| .addPattern(pattern) | |
| .globInterruptible(); | |
| fail(); | |
| } catch (IllegalArgumentException e) { | |
| assertThat(e.getMessage()).containsMatch("recursive wildcard must be its own segment"); | |
| } | |
| } | |
| } |