Make UnionFileSystem accept all paths Bazel can throw at it. Instead of relying on a character-by-character StringTrie, segment paths based on PathFragments. This means UnionFS can accept any path that Bazel stores internally, removing the ASCII limitations. This also means removing the ability to have a filesystem bound for sub-PathFragments, /foo/barbar, /foo/barqux could have the same filesystem bound at /foo/bar. This feature was tested for when a use case was envisioned, but it was never used, so removing it is safe. RELNOTES: None. PiperOrigin-RevId: 170054656
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD index 2f783f9..b8f6e9c 100644 --- a/src/main/java/com/google/devtools/build/lib/BUILD +++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -94,7 +94,6 @@ name = "base-util", srcs = [ "util/StringCanonicalizer.java", - "util/StringTrie.java", "util/VarInt.java", ], deps = [
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java index b904e1d..2bbdb48 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
@@ -808,7 +808,7 @@ } } - private static FileSystem fileSystemImplementation() { + private static FileSystem defaultFileSystemImplementation() { if ("0".equals(System.getProperty("io.bazel.EnableJni"))) { // Ignore UnixFileSystem, to be used for bootstrapping. return OS.getCurrent() == OS.WINDOWS ? new WindowsFileSystem() : new JavaIoFileSystem(); @@ -943,7 +943,7 @@ } if (fs == null) { - fs = fileSystemImplementation(); + fs = defaultFileSystemImplementation(); } Path.setFileSystemForSerialization(fs);
diff --git a/src/main/java/com/google/devtools/build/lib/util/StringTrie.java b/src/main/java/com/google/devtools/build/lib/util/StringTrie.java deleted file mode 100644 index 0b5d6e4..0000000 --- a/src/main/java/com/google/devtools/build/lib/util/StringTrie.java +++ /dev/null
@@ -1,87 +0,0 @@ -// 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.util; - -/** - * A trie that operates on path segments of an input string instead of individual characters. - * - * <p>Only accepts strings that contain only low-ASCII characters (0-127) - * - * @param <T> the type of the values - */ -public class StringTrie<T> { - private static final int RANGE = 128; - - @SuppressWarnings("unchecked") - private static class Node<T> { - private Node() { - children = new Node[RANGE]; - } - - private T value; - private Node<T> children[]; - } - - private final Node<T> root; - - public StringTrie() { - root = new Node<T>(); - } - - /** - * Puts a value in the trie. - */ - public void put(CharSequence key, T value) { - Node<T> current = root; - - for (int i = 0; i < key.length(); i++) { - char ch = key.charAt(i); - Preconditions.checkState(ch < RANGE); - Node<T> next = current.children[ch]; - if (next == null) { - next = new Node<T>(); - current.children[ch] = next; - } - - current = next; - } - - current.value = value; - } - - /** - * Gets a value from the trie. If there is an entry with the same key, that will be returned, - * otherwise, the value corresponding to the key that matches the longest prefix of the input. - */ - public T get(String key) { - Node<T> current = root; - T lastValue = current.value; - - for (int i = 0; i < key.length(); i++) { - char ch = key.charAt(i); - Preconditions.checkState(ch < RANGE); - - current = current.children[ch]; - if (current == null) { - break; - } - - if (current.value != null) { - lastValue = current.value; - } - } - - return lastValue; - } -}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java index cc9aa19..c47a5f7 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
@@ -58,6 +58,9 @@ /** An empty path fragment. */ public static final PathFragment EMPTY_FRAGMENT = create(""); + /** The path fragment representing the root directory. */ + public static final PathFragment ROOT_FRAGMENT = create(ROOT_DIR); + /** * A helper object for manipulating the various internal {@link PathFragment} implementations. *
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/PathTrie.java b/src/main/java/com/google/devtools/build/lib/vfs/PathTrie.java new file mode 100644 index 0000000..86bc226 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/vfs/PathTrie.java
@@ -0,0 +1,82 @@ +// 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 com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible; +import com.google.devtools.build.lib.util.Preconditions; +import java.util.HashMap; +import java.util.Map; + +/** + * A trie that operates on path segments. + * + * @param <T> the type of the values. + */ +@ThreadCompatible +public class PathTrie<T> { + @SuppressWarnings("unchecked") + private static class Node<T> { + private Node() { + children = new HashMap<>(); + } + + private T value; + private Map<String, Node<T>> children; + } + + private final Node<T> root; + + public PathTrie() { + root = new Node<T>(); + } + + /** + * Puts a value in the trie. + * + * @param key must be an absolute path. + */ + public void put(PathFragment key, T value) { + Preconditions.checkArgument(key.isAbsolute(), "PathTrie only accepts absolute paths as keys."); + Node<T> current = root; + for (String segment : key.getSegments()) { + current.children.putIfAbsent(segment, new Node<T>()); + current = current.children.get(segment); + } + current.value = value; + } + + /** + * Gets a value from the trie. If there is an entry with the same key, that will be returned, + * otherwise, the value corresponding to the key that matches the longest prefix of the input. + */ + public T get(PathFragment key) { + Node<T> current = root; + T lastValue = current.value; + + for (String segment : key.getSegments()) { + if (current.children.containsKey(segment)) { + current = current.children.get(segment); + // Track the values of increasing matching prefixes. + if (current.value != null) { + lastValue = current.value; + } + } else { + // We've reached the longest prefix, no further to go. + break; + } + } + + return lastValue; + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java index d1d82d9..de65cf7 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java
@@ -17,8 +17,6 @@ import com.google.common.collect.Lists; import com.google.devtools.build.lib.concurrent.ThreadSafety; import com.google.devtools.build.lib.util.Preconditions; -import com.google.devtools.build.lib.util.StringTrie; -import com.google.devtools.build.lib.vfs.FileSystem.HashFunction; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -28,21 +26,20 @@ /** * Presents a unified view of multiple virtual {@link FileSystem} instances, to which requests are - * delegated based on a {@link PathFragment} prefix mapping. - * If multiple prefixes apply to a given path, the *longest* (i.e. most specific) match is used. - * The order in which the delegates are specified does not influence the mapping. + * delegated based on a {@link PathFragment} prefix mapping. If multiple prefixes apply to a given + * path, the *longest* (i.e. most specific) match is used. The order in which the delegates are + * specified does not influence the mapping. * - * <p>Paths are preserved absolutely, contrary to how "mount" works, e.g.: - * /foo/bar maps to /foo/bar on the delegate, even if it is mounted at /foo. + * <p>Paths are preserved absolutely, contrary to how "mount" works, e.g.: /foo/bar maps to /foo/bar + * on the delegate, even if it is mounted at /foo. * - * <p>For example: - * "/in" maps to InFileSystem, "/" maps to OtherFileSystem. - * Reading from "/in/base/BUILD" through the UnionFileSystem will delegate the read operation to - * InFileSystem, which will read "/in/base/BUILD" relative to its root. - * ("mount" behavior would remap it to "/base/BUILD" on the delegate). + * <p>For example: "/in" maps to InFileSystem, "/" maps to OtherFileSystem. Reading from + * "/in/base/BUILD" through the UnionFileSystem will delegate the read operation to InFileSystem, + * which will read "/in/base/BUILD" relative to its root. ("mount" behavior would remap it to + * "/base/BUILD" on the delegate). * - * <p>Intra-filesystem symbolic links are resolved to their ultimate targets. - * Cross-filesystem links are not currently supported. + * <p>Intra-filesystem symbolic links are resolved to their ultimate targets. Cross-filesystem links + * are not currently supported. */ @ThreadSafety.ThreadSafe public class UnionFileSystem extends FileSystem { @@ -50,7 +47,7 @@ // Prefix trie index, allowing children to easily inherit prefix mappings // of their parents. // This does not currently handle unicode filenames. - private StringTrie<FileSystem> pathDelegate; + private final PathTrie<FileSystem> pathDelegate; // True iff the filesystem can be modified. If false, mutating operations // will throw UnsupportedOperationExceptions. @@ -61,37 +58,35 @@ private final boolean isCaseSensitive; /** - * Creates a new modifiable UnionFileSystem with prefix mappings - * specified by a map. + * Creates a new modifiable UnionFileSystem with prefix mappings specified by a map. * * @param prefixMapping map of path prefixes to {@link FileSystem}s */ - public UnionFileSystem(Map<PathFragment, FileSystem> prefixMapping, - FileSystem rootFileSystem) { + public UnionFileSystem(Map<PathFragment, FileSystem> prefixMapping, FileSystem rootFileSystem) { this(prefixMapping, rootFileSystem, /* readOnly */ false); } /** - * Creates a new modifiable or read-only UnionFileSystem with prefix mappings - * specified by a map. + * Creates a new modifiable or read-only UnionFileSystem with prefix mappings specified by a map. * - * @param prefixMapping map of path prefixes to delegate {@link FileSystem}s + * @param prefixMapping map of path prefixes to delegate {@link FileSystem} instances to use for + * paths of that prefix. Note that all prefixes must be absolute paths. * @param rootFileSystem root for default requests; i.e. mapping of "/" * @param readOnly if true, mutating operations will throw */ - public UnionFileSystem(Map<PathFragment, FileSystem> prefixMapping, - FileSystem rootFileSystem, boolean readOnly) { + public UnionFileSystem( + Map<PathFragment, FileSystem> prefixMapping, FileSystem rootFileSystem, boolean readOnly) { super(); Preconditions.checkNotNull(prefixMapping); Preconditions.checkNotNull(rootFileSystem); Preconditions.checkArgument(rootFileSystem != this, "Circular root filesystem."); Preconditions.checkArgument( !prefixMapping.containsKey(PathFragment.EMPTY_FRAGMENT), - "Attempted to specify an explicit root prefix mapping; " + - "please use the rootFileSystem argument instead."); + "Attempted to specify an explicit root prefix mapping; " + + "please use the rootFileSystem argument instead."); this.readOnly = readOnly; - this.pathDelegate = new StringTrie<>(); + this.pathDelegate = new PathTrie<>(); this.isCaseSensitive = rootFileSystem.isFilePathCaseSensitive(); for (Map.Entry<PathFragment, FileSystem> prefix : prefixMapping.entrySet()) { @@ -102,29 +97,24 @@ PathFragment prefixPath = prefix.getKey(); // Extra slash prevents within-directory mappings, which Path can't handle. - String path = prefixPath.getPathString(); - pathDelegate.put(path, delegate); + pathDelegate.put(prefixPath, delegate); } - pathDelegate.put(PathFragment.EMPTY_FRAGMENT.getPathString(), rootFileSystem); + pathDelegate.put(PathFragment.ROOT_FRAGMENT, rootFileSystem); } /** - * Retrieves the filesystem delegate of a path mapping. - * Does not follow symlinks (but you can call on a path preprocessed with - * {@link #resolveSymbolicLinks} to support this use case). + * Retrieves the filesystem delegate of a path mapping. Does not follow symlinks (but you can call + * on a path preprocessed with {@link #resolveSymbolicLinks} to support this use case). * * @param path the {@link Path} to map to a filesystem * @throws IllegalArgumentException if no delegate exists for the path */ protected FileSystem getDelegate(Path path) { Preconditions.checkNotNull(path); - - String pathString = path.getPathString(); - FileSystem immediateDelegate = pathDelegate.get(pathString); + FileSystem immediateDelegate = pathDelegate.get(path.asFragment()); // Should never actually happen if the root delegate is present. - Preconditions.checkArgument(immediateDelegate != null, "No delegate filesystem exists for %s", - pathString); + Preconditions.checkNotNull(immediateDelegate, "No delegate filesystem exists for %s", path); return immediateDelegate; } @@ -135,8 +125,8 @@ } /** - * Follow a symbolic link once using the appropriate delegate filesystem, also - * resolving parent directory symlinks. + * Follow a symbolic link once using the appropriate delegate filesystem, also resolving parent + * directory symlinks. * * @param path {@link Path} to the symbolic link */ @@ -157,7 +147,7 @@ private void checkModifiable() { if (!supportsModifications()) { throw new UnsupportedOperationException( - "Modifications to this " + getClass().getSimpleName() + " are disabled."); + String.format("Modifications to this %s are disabled.", getClass().getSimpleName())); } } @@ -311,9 +301,8 @@ } /** - * Retrieves the directory entries for the specified path under the assumption - * that {@code resolvedPath} is the resolved path of {@code path} in one of the - * underlying file systems. + * Retrieves the directory entries for the specified path under the assumption that {@code + * resolvedPath} is the resolved path of {@code path} in one of the underlying file systems. * * @param path the {@link Path} whose children are to be retrieved */ @@ -409,16 +398,18 @@ FileSystem sourceDelegate = getDelegate(sourcePath); if (!sourceDelegate.supportsModifications()) { throw new UnsupportedOperationException( - "The filesystem for the source path " - + sourcePath.getPathString() + " does not support modifications."); + String.format( + "The filesystem for the source path %s does not support modifications.", + sourcePath.getPathString())); } sourcePath = adjustPath(sourcePath, sourceDelegate); FileSystem targetDelegate = getDelegate(targetPath); if (!targetDelegate.supportsModifications()) { throw new UnsupportedOperationException( - "The filesystem for the target path " - + targetPath.getPathString() + " does not support modifications."); + String.format( + "The filesystem for the target path %s does not support modifications.", + targetPath.getPathString())); } targetPath = adjustPath(targetPath, targetDelegate); @@ -435,8 +426,7 @@ } @Override - protected void createFSDependentHardLink(Path linkPath, Path originalPath) - throws IOException { + protected void createFSDependentHardLink(Path linkPath, Path originalPath) throws IOException { checkModifiable(); FileSystem originalDelegate = getDelegate(originalPath);
diff --git a/src/test/java/com/google/devtools/build/lib/util/StringTrieTest.java b/src/test/java/com/google/devtools/build/lib/util/StringTrieTest.java deleted file mode 100644 index 5f2faf1..0000000 --- a/src/test/java/com/google/devtools/build/lib/util/StringTrieTest.java +++ /dev/null
@@ -1,69 +0,0 @@ -// 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.util; - -import static com.google.common.truth.Truth.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** - * Unit tests for {@link StringTrie}. - */ -@RunWith(JUnit4.class) -public class StringTrieTest { - @Test - public void empty() { - StringTrie<Integer> cut = new StringTrie<>(); - assertThat(cut.get("")).isNull(); - assertThat(cut.get("a")).isNull(); - assertThat(cut.get("ab")).isNull(); - } - - @Test - public void simple() { - StringTrie<Integer> cut = new StringTrie<>(); - cut.put("a", 1); - cut.put("b", 2); - - assertThat(cut.get("")).isNull(); - assertThat(cut.get("a").intValue()).isEqualTo(1); - assertThat(cut.get("ab").intValue()).isEqualTo(1); - assertThat(cut.get("abc").intValue()).isEqualTo(1); - - assertThat(cut.get("b").intValue()).isEqualTo(2); - } - - @Test - public void ancestors() { - StringTrie<Integer> cut = new StringTrie<>(); - cut.put("abc", 3); - assertThat(cut.get("")).isNull(); - assertThat(cut.get("a")).isNull(); - assertThat(cut.get("ab")).isNull(); - assertThat(cut.get("abc").intValue()).isEqualTo(3); - assertThat(cut.get("abcd").intValue()).isEqualTo(3); - - cut.put("a", 1); - assertThat(cut.get("a").intValue()).isEqualTo(1); - assertThat(cut.get("ab").intValue()).isEqualTo(1); - assertThat(cut.get("abc").intValue()).isEqualTo(3); - - cut.put("", 0); - assertThat(cut.get("").intValue()).isEqualTo(0); - assertThat(cut.get("b").intValue()).isEqualTo(0); - assertThat(cut.get("a").intValue()).isEqualTo(1); - } -}
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathTrieTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathTrieTest.java new file mode 100644 index 0000000..0807b4a --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/vfs/PathTrieTest.java
@@ -0,0 +1,78 @@ +// 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 com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link PathTrie}. */ +@RunWith(JUnit4.class) +public class PathTrieTest { + @Test + public void empty() { + PathTrie<Integer> pathTrie = new PathTrie<>(); + assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull(); + assertThat(pathTrie.get(PathFragment.create("/x"))).isNull(); + assertThat(pathTrie.get(PathFragment.create("/x/y"))).isNull(); + } + + @Test + public void simpleBranches() { + PathTrie<Integer> pathTrie = new PathTrie<>(); + pathTrie.put(PathFragment.create("/a"), 1); + pathTrie.put(PathFragment.create("/b"), 2); + + assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull(); + assertThat(pathTrie.get(PathFragment.create("/a"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/a/b"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/a/b/c"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/b"))).isEqualTo(2); + assertThat(pathTrie.get(PathFragment.create("/b/c"))).isEqualTo(2); + } + + @Test + public void nestedDirectories() { + PathTrie<Integer> pathTrie = new PathTrie<>(); + pathTrie.put(PathFragment.create("/a/b/c"), 3); + assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull(); + assertThat(pathTrie.get(PathFragment.create("/a"))).isNull(); + assertThat(pathTrie.get(PathFragment.create("/a/b"))).isNull(); + assertThat(pathTrie.get(PathFragment.create("/a/b/c"))).isEqualTo(3); + assertThat(pathTrie.get(PathFragment.create("/a/b/c/d"))).isEqualTo(3); + + pathTrie.put(PathFragment.create("/a"), 1); + assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull(); + assertThat(pathTrie.get(PathFragment.create("/b"))).isNull(); + assertThat(pathTrie.get(PathFragment.create("/a"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/a/b"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/a/b/c"))).isEqualTo(3); + assertThat(pathTrie.get(PathFragment.create("/a/b/c/d"))).isEqualTo(3); + + pathTrie.put(PathFragment.ROOT_FRAGMENT, 0); + assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isEqualTo(0); + assertThat(pathTrie.get(PathFragment.create("/b"))).isEqualTo(0); + assertThat(pathTrie.get(PathFragment.create("/a"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/a/b"))).isEqualTo(1); + } + + @Test + public void unrootedDirectoriesError() { + PathTrie<Integer> pathTrie = new PathTrie<>(); + assertThrows(IllegalArgumentException.class, () -> pathTrie.put(PathFragment.create("a"), 1)); + } +}
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java index 1952898..780a529 100644 --- a/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java +++ b/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java
@@ -31,8 +31,8 @@ import org.junit.runners.JUnit4; /** - * Tests for the UnionFileSystem, both of generic FileSystem functionality - * (inherited) and tests of UnionFileSystem-specific behavior. + * Tests for the UnionFileSystem, both of generic FileSystem functionality (inherited) and tests of + * UnionFileSystem-specific behavior. */ @RunWith(JUnit4.class) public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { @@ -57,10 +57,12 @@ } private UnionFileSystem createDefaultUnionFileSystem(boolean readOnly) { - return new UnionFileSystem(ImmutableMap.<PathFragment, FileSystem>of( - PathFragment.create("/in"), inDelegate, - PathFragment.create("/out"), outDelegate), - defaultDelegate, readOnly); + return new UnionFileSystem( + ImmutableMap.<PathFragment, FileSystem>of( + PathFragment.create("/in"), inDelegate, + PathFragment.create("/out"), outDelegate), + defaultDelegate, + readOnly); } @Override @@ -116,9 +118,12 @@ // on path prefixes, including non-canonical paths. @Test public void testPrefixDelegation() throws Exception { - unionfs = new UnionFileSystem(ImmutableMap.<PathFragment, FileSystem>of( - PathFragment.create("/foo"), inDelegate, - PathFragment.create("/foo/bar"), outDelegate), defaultDelegate); + unionfs = + new UnionFileSystem( + ImmutableMap.<PathFragment, FileSystem>of( + PathFragment.create("/foo"), inDelegate, + PathFragment.create("/foo/bar"), outDelegate), + defaultDelegate); assertThat(unionfs.getDelegate(unionfs.getPath("/foo/foo.txt"))).isSameAs(inDelegate); assertThat(unionfs.getDelegate(unionfs.getPath("/foo/bar/foo.txt"))).isSameAs(outDelegate); @@ -177,8 +182,8 @@ // FileSystemTest.setUp() silently creates the test root on the filesystem... Path testDirUnderRoot = unionfs.getPath(workingDir.asFragment().subFragment(0, 1)); - assertThat(unionfs.getDirectoryEntries(unionfs.getRootDirectory())).containsExactly(foo, bar, - out, testDirUnderRoot); + assertThat(unionfs.getDirectoryEntries(unionfs.getRootDirectory())) + .containsExactly(foo, bar, out, testDirUnderRoot); assertThat(unionfs.getDirectoryEntries(out)).containsExactly(outFile); assertThat(defaultDelegate).isSameAs(unionfs.getDelegate(foo)); @@ -249,53 +254,18 @@ .isSameAs(unionfs); } - // Prefix mappings can apply to files starting with a prefix within a directory. - @Test - public void testWithinDirectoryMapping() throws Exception { - unionfs = new UnionFileSystem(ImmutableMap.<PathFragment, FileSystem>of( - PathFragment.create("/fruit/a"), inDelegate, - PathFragment.create("/fruit/b"), outDelegate), defaultDelegate); - assertThat(unionfs.createDirectory(unionfs.getPath("/fruit"))).isTrue(); - assertThat(defaultDelegate.getPath("/fruit").isDirectory()).isTrue(); - assertThat(inDelegate.getPath("/fruit").createDirectory()).isTrue(); - assertThat(outDelegate.getPath("/fruit").createDirectory()).isTrue(); - - Path apple = unionfs.getPath("/fruit/apple"); - Path banana = unionfs.getPath("/fruit/banana"); - Path cherry = unionfs.getPath("/fruit/cherry"); - unionfs.createDirectory(apple); - unionfs.createDirectory(banana); - assertThat(unionfs.getDelegate(apple)).isSameAs(inDelegate); - assertThat(unionfs.getDelegate(banana)).isSameAs(outDelegate); - assertThat(unionfs.getDelegate(cherry)).isSameAs(defaultDelegate); - - FileSystemUtils.writeContentAsLatin1(apple.getRelative("table"), "penny"); - FileSystemUtils.writeContentAsLatin1(banana.getRelative("nana"), "nanana"); - FileSystemUtils.writeContentAsLatin1(cherry, "garcia"); - - assertThat( - new String( - FileSystemUtils.readContentAsLatin1(inDelegate.getPath("/fruit/apple/table")))) - .isEqualTo("penny"); - assertThat( - new String( - FileSystemUtils.readContentAsLatin1(outDelegate.getPath("/fruit/banana/nana")))) - .isEqualTo("nanana"); - assertThat( - new String( - FileSystemUtils.readContentAsLatin1(defaultDelegate.getPath("/fruit/cherry")))) - .isEqualTo("garcia"); - } - // Write using the VFS through a UnionFileSystem and check that the file can // be read back in the same location using standard Java IO. // There is a similar test in UnixFileSystem, but this is essential to ensure // that paths aren't being remapped in some nasty way on the underlying FS. @Test public void testDelegateOperationsReflectOnLocalFilesystem() throws Exception { - unionfs = new UnionFileSystem(ImmutableMap.<PathFragment, FileSystem>of( - workingDir.getParentDirectory().asFragment(), new UnixFileSystem()), - defaultDelegate, false); + unionfs = + new UnionFileSystem( + ImmutableMap.<PathFragment, FileSystem>of( + workingDir.getParentDirectory().asFragment(), new UnixFileSystem()), + defaultDelegate, + false); // This is a child of the current tmpdir, and doesn't exist on its own. // It would be created in setup(), but of course, that didn't use a UnixFileSystem. unionfs.createDirectory(workingDir); @@ -314,8 +284,11 @@ // Regression test for [UnionFS: Directory creation across mapping fails.] @Test public void testCreateParentsAcrossMapping() throws Exception { - unionfs = new UnionFileSystem(ImmutableMap.<PathFragment, FileSystem>of( - PathFragment.create("/out/dir"), outDelegate), defaultDelegate, false); + unionfs = + new UnionFileSystem( + ImmutableMap.<PathFragment, FileSystem>of(PathFragment.create("/out/dir"), outDelegate), + defaultDelegate, + false); Path outDir = unionfs.getPath("/out/dir/biz/bang"); FileSystemUtils.createDirectoryAndParents(outDir); assertThat(outDir.isDirectory()).isTrue();