diff options
Diffstat (limited to 'jimfs/src/test/java/com/google/common/jimfs/FileTreeTest.java')
-rw-r--r-- | jimfs/src/test/java/com/google/common/jimfs/FileTreeTest.java | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/jimfs/src/test/java/com/google/common/jimfs/FileTreeTest.java b/jimfs/src/test/java/com/google/common/jimfs/FileTreeTest.java new file mode 100644 index 0000000..54f590d --- /dev/null +++ b/jimfs/src/test/java/com/google/common/jimfs/FileTreeTest.java @@ -0,0 +1,463 @@ +/* + * Copyright 2013 Google Inc. + * + * 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.common.jimfs; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.jimfs.TestUtils.regularFile; +import static com.google.common.truth.Truth.assertThat; +import static java.nio.file.LinkOption.NOFOLLOW_LINKS; +import static org.junit.Assert.fail; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import java.io.IOException; +import java.nio.file.LinkOption; +import java.nio.file.NoSuchFileException; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import org.checkerframework.checker.nullness.compatqual.NullableDecl; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests for {@link FileTree}. + * + * @author Colin Decker + */ +@RunWith(JUnit4.class) +public class FileTreeTest { + + /* + * Directory structure. Each file should have a unique name. + * + * / + * work/ + * one/ + * two/ + * three/ + * eleven + * four/ + * five -> /foo + * six -> ../one + * loop -> ../four/loop + * foo/ + * bar/ + * $ + * a/ + * b/ + * c/ + */ + + /** + * This path service is for unix-like paths, with the exception that it recognizes $ and ! as + * roots in addition to /, allowing for up to three roots. When creating a {@linkplain + * PathType#toUriPath URI path}, we prefix the path with / to differentiate between a path like + * "$foo/bar" and one like "/$foo/bar". They would become "/$foo/bar" and "//$foo/bar" + * respectively. + */ + private final PathService pathService = + PathServiceTest.fakePathService( + new PathType(true, '/') { + @Override + public ParseResult parsePath(String path) { + String root = null; + if (path.matches("^[/$!].*")) { + root = path.substring(0, 1); + path = path.substring(1); + } + return new ParseResult(root, Splitter.on('/').omitEmptyStrings().split(path)); + } + + @Override + public String toString(@NullableDecl String root, Iterable<String> names) { + root = Strings.nullToEmpty(root); + return root + Joiner.on('/').join(names); + } + + @Override + public String toUriPath(String root, Iterable<String> names, boolean directory) { + // need to add extra / to differentiate between paths "/$foo/bar" and "$foo/bar". + return "/" + toString(root, names); + } + + @Override + public ParseResult parseUriPath(String uriPath) { + checkArgument( + uriPath.matches("^/[/$!].*"), "uriPath (%s) must start with // or /$ or /!"); + return parsePath(uriPath.substring(1)); // skip leading / + } + }, + false); + + private FileTree fileTree; + private File workingDirectory; + private final Map<String, File> files = new HashMap<>(); + + @Before + public void setUp() { + Directory root = Directory.createRoot(0, Name.simple("/")); + files.put("/", root); + + Directory otherRoot = Directory.createRoot(2, Name.simple("$")); + files.put("$", otherRoot); + + Map<Name, Directory> roots = new HashMap<>(); + roots.put(Name.simple("/"), root); + roots.put(Name.simple("$"), otherRoot); + + fileTree = new FileTree(roots); + + workingDirectory = createDirectory("/", "work"); + + createDirectory("work", "one"); + createDirectory("one", "two"); + createFile("one", "eleven"); + createDirectory("two", "three"); + createDirectory("work", "four"); + createSymbolicLink("four", "five", "/foo"); + createSymbolicLink("four", "six", "../one"); + createSymbolicLink("four", "loop", "../four/loop"); + createDirectory("/", "foo"); + createDirectory("foo", "bar"); + createDirectory("$", "a"); + createDirectory("a", "b"); + createDirectory("b", "c"); + } + + // absolute lookups + + @Test + public void testLookup_root() throws IOException { + assertExists(lookup("/"), "/", "/"); + assertExists(lookup("$"), "$", "$"); + } + + @Test + public void testLookup_nonExistentRoot() throws IOException { + try { + lookup("!"); + fail(); + } catch (NoSuchFileException expected) { + } + + try { + lookup("!a"); + fail(); + } catch (NoSuchFileException expected) { + } + } + + @Test + public void testLookup_absolute() throws IOException { + assertExists(lookup("/work"), "/", "work"); + assertExists(lookup("/work/one/two/three"), "two", "three"); + assertExists(lookup("$a"), "$", "a"); + assertExists(lookup("$a/b/c"), "b", "c"); + } + + @Test + public void testLookup_absolute_notExists() throws IOException { + try { + lookup("/a/b"); + fail(); + } catch (NoSuchFileException expected) { + } + + try { + lookup("/work/one/foo/bar"); + fail(); + } catch (NoSuchFileException expected) { + } + + try { + lookup("$c/d"); + fail(); + } catch (NoSuchFileException expected) { + } + + try { + lookup("$a/b/c/d/e"); + fail(); + } catch (NoSuchFileException expected) { + } + } + + @Test + public void testLookup_absolute_parentExists() throws IOException { + assertParentExists(lookup("/a"), "/"); + assertParentExists(lookup("/foo/baz"), "foo"); + assertParentExists(lookup("$c"), "$"); + assertParentExists(lookup("$a/b/c/d"), "c"); + } + + @Test + public void testLookup_absolute_nonDirectoryIntermediateFile() throws IOException { + try { + lookup("/work/one/eleven/twelve"); + fail(); + } catch (NoSuchFileException expected) { + } + + try { + lookup("/work/one/eleven/twelve/thirteen/fourteen"); + fail(); + } catch (NoSuchFileException expected) { + } + } + + @Test + public void testLookup_absolute_intermediateSymlink() throws IOException { + assertExists(lookup("/work/four/five/bar"), "foo", "bar"); + assertExists(lookup("/work/four/six/two/three"), "two", "three"); + + // NOFOLLOW_LINKS doesn't affect intermediate symlinks + assertExists(lookup("/work/four/five/bar", NOFOLLOW_LINKS), "foo", "bar"); + assertExists(lookup("/work/four/six/two/three", NOFOLLOW_LINKS), "two", "three"); + } + + @Test + public void testLookup_absolute_intermediateSymlink_parentExists() throws IOException { + assertParentExists(lookup("/work/four/five/baz"), "foo"); + assertParentExists(lookup("/work/four/six/baz"), "one"); + } + + @Test + public void testLookup_absolute_finalSymlink() throws IOException { + assertExists(lookup("/work/four/five"), "/", "foo"); + assertExists(lookup("/work/four/six"), "work", "one"); + } + + @Test + public void testLookup_absolute_finalSymlink_nofollowLinks() throws IOException { + assertExists(lookup("/work/four/five", NOFOLLOW_LINKS), "four", "five"); + assertExists(lookup("/work/four/six", NOFOLLOW_LINKS), "four", "six"); + assertExists(lookup("/work/four/loop", NOFOLLOW_LINKS), "four", "loop"); + } + + @Test + public void testLookup_absolute_symlinkLoop() { + try { + lookup("/work/four/loop"); + fail(); + } catch (IOException expected) { + } + + try { + lookup("/work/four/loop/whatever"); + fail(); + } catch (IOException expected) { + } + } + + @Test + public void testLookup_absolute_withDotsInPath() throws IOException { + assertExists(lookup("/."), "/", "/"); + assertExists(lookup("/./././."), "/", "/"); + assertExists(lookup("/work/./one/./././two/three"), "two", "three"); + assertExists(lookup("/work/./one/./././two/././three"), "two", "three"); + assertExists(lookup("/work/./one/./././two/three/././."), "two", "three"); + } + + @Test + public void testLookup_absolute_withDotDotsInPath() throws IOException { + assertExists(lookup("/.."), "/", "/"); + assertExists(lookup("/../../.."), "/", "/"); + assertExists(lookup("/work/.."), "/", "/"); + assertExists(lookup("/work/../work/one/two/../two/three"), "two", "three"); + assertExists(lookup("/work/one/two/../../four/../one/two/three/../three"), "two", "three"); + assertExists(lookup("/work/one/two/three/../../two/three/.."), "one", "two"); + assertExists(lookup("/work/one/two/three/../../two/three/../.."), "work", "one"); + } + + @Test + public void testLookup_absolute_withDotDotsInPath_afterSymlink() throws IOException { + assertExists(lookup("/work/four/five/.."), "/", "/"); + assertExists(lookup("/work/four/six/.."), "/", "work"); + } + + // relative lookups + + @Test + public void testLookup_relative() throws IOException { + assertExists(lookup("one"), "work", "one"); + assertExists(lookup("one/two/three"), "two", "three"); + } + + @Test + public void testLookup_relative_notExists() throws IOException { + try { + lookup("a/b"); + fail(); + } catch (NoSuchFileException expected) { + } + + try { + lookup("one/foo/bar"); + fail(); + } catch (NoSuchFileException expected) { + } + } + + @Test + public void testLookup_relative_parentExists() throws IOException { + assertParentExists(lookup("a"), "work"); + assertParentExists(lookup("one/two/four"), "two"); + } + + @Test + public void testLookup_relative_nonDirectoryIntermediateFile() throws IOException { + try { + lookup("one/eleven/twelve"); + fail(); + } catch (NoSuchFileException expected) { + } + + try { + lookup("one/eleven/twelve/thirteen/fourteen"); + fail(); + } catch (NoSuchFileException expected) { + } + } + + @Test + public void testLookup_relative_intermediateSymlink() throws IOException { + assertExists(lookup("four/five/bar"), "foo", "bar"); + assertExists(lookup("four/six/two/three"), "two", "three"); + + // NOFOLLOW_LINKS doesn't affect intermediate symlinks + assertExists(lookup("four/five/bar", NOFOLLOW_LINKS), "foo", "bar"); + assertExists(lookup("four/six/two/three", NOFOLLOW_LINKS), "two", "three"); + } + + @Test + public void testLookup_relative_intermediateSymlink_parentExists() throws IOException { + assertParentExists(lookup("four/five/baz"), "foo"); + assertParentExists(lookup("four/six/baz"), "one"); + } + + @Test + public void testLookup_relative_finalSymlink() throws IOException { + assertExists(lookup("four/five"), "/", "foo"); + assertExists(lookup("four/six"), "work", "one"); + } + + @Test + public void testLookup_relative_finalSymlink_nofollowLinks() throws IOException { + assertExists(lookup("four/five", NOFOLLOW_LINKS), "four", "five"); + assertExists(lookup("four/six", NOFOLLOW_LINKS), "four", "six"); + assertExists(lookup("four/loop", NOFOLLOW_LINKS), "four", "loop"); + } + + @Test + public void testLookup_relative_symlinkLoop() { + try { + lookup("four/loop"); + fail(); + } catch (IOException expected) { + } + + try { + lookup("four/loop/whatever"); + fail(); + } catch (IOException expected) { + } + } + + @Test + public void testLookup_relative_emptyPath() throws IOException { + assertExists(lookup(""), "/", "work"); + } + + @Test + public void testLookup_relative_withDotsInPath() throws IOException { + assertExists(lookup("."), "/", "work"); + assertExists(lookup("././."), "/", "work"); + assertExists(lookup("./one/./././two/three"), "two", "three"); + assertExists(lookup("./one/./././two/././three"), "two", "three"); + assertExists(lookup("./one/./././two/three/././."), "two", "three"); + } + + @Test + public void testLookup_relative_withDotDotsInPath() throws IOException { + assertExists(lookup(".."), "/", "/"); + assertExists(lookup("../../.."), "/", "/"); + assertExists(lookup("../work"), "/", "work"); + assertExists(lookup("../../work"), "/", "work"); + assertExists(lookup("../foo"), "/", "foo"); + assertExists(lookup("../work/one/two/../two/three"), "two", "three"); + assertExists(lookup("one/two/../../four/../one/two/three/../three"), "two", "three"); + assertExists(lookup("one/two/three/../../two/three/.."), "one", "two"); + assertExists(lookup("one/two/three/../../two/three/../.."), "work", "one"); + } + + @Test + public void testLookup_relative_withDotDotsInPath_afterSymlink() throws IOException { + assertExists(lookup("four/five/.."), "/", "/"); + assertExists(lookup("four/six/.."), "/", "work"); + } + + private DirectoryEntry lookup(String path, LinkOption... options) throws IOException { + JimfsPath pathObj = pathService.parsePath(path); + return fileTree.lookUp(workingDirectory, pathObj, Options.getLinkOptions(options)); + } + + private void assertExists(DirectoryEntry entry, String parent, String file) { + assertThat(entry.exists()).isTrue(); + assertThat(entry.name()).isEqualTo(Name.simple(file)); + assertThat(entry.directory()).isEqualTo(files.get(parent)); + assertThat(entry.file()).isEqualTo(files.get(file)); + } + + private void assertParentExists(DirectoryEntry entry, String parent) { + assertThat(entry.exists()).isFalse(); + assertThat(entry.directory()).isEqualTo(files.get(parent)); + + try { + entry.file(); + fail(); + } catch (IllegalStateException expected) { + } + } + + private File createDirectory(String parent, String name) { + Directory dir = (Directory) files.get(parent); + Directory newFile = Directory.create(new Random().nextInt()); + dir.link(Name.simple(name), newFile); + files.put(name, newFile); + return newFile; + } + + private File createFile(String parent, String name) { + Directory dir = (Directory) files.get(parent); + File newFile = regularFile(0); + dir.link(Name.simple(name), newFile); + files.put(name, newFile); + return newFile; + } + + private File createSymbolicLink(String parent, String name, String target) { + Directory dir = (Directory) files.get(parent); + File newFile = SymbolicLink.create(new Random().nextInt(), pathService.parsePath(target)); + dir.link(Name.simple(name), newFile); + files.put(name, newFile); + return newFile; + } +} |