aboutsummaryrefslogtreecommitdiff
path: root/jimfs/src/test/java/com/google/common/jimfs/FileTreeTest.java
diff options
context:
space:
mode:
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.java463
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;
+ }
+}