| // 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.assertThrows; | |
| import com.google.devtools.build.lib.util.OS; | |
| import com.google.devtools.build.lib.util.StringEncoding; | |
| import com.google.devtools.build.lib.vfs.FileSystem.NotASymlinkException; | |
| import java.io.FileNotFoundException; | |
| import java.io.IOException; | |
| import java.nio.file.Files; | |
| import java.util.Collection; | |
| import org.junit.Before; | |
| import org.junit.Test; | |
| /** | |
| * This class handles the generic tests that any filesystem must pass. | |
| * | |
| * <p>Each filesystem-test should inherit from this class, thereby obtaining | |
| * all the tests. | |
| */ | |
| public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { | |
| protected Path xLinkToFile; | |
| protected Path xLinkToLinkToFile; | |
| protected Path xLinkToDirectory; | |
| protected Path xDanglingLink; | |
| @Before | |
| public final void createSymbolicLinks() throws Exception { | |
| // % ls -lR | |
| // -rw-rw-r-- xFile | |
| // drwxrwxr-x xNonEmptyDirectory | |
| // -rw-rw-r-- xNonEmptyDirectory/foo | |
| // drwxrwxr-x xEmptyDirectory | |
| // lrwxrwxr-x xLinkToFile -> xFile | |
| // lrwxrwxr-x xLinkToDirectory -> xEmptyDirectory | |
| // lrwxrwxr-x xLinkToLinkToFile -> xLinkToFile | |
| // lrwxrwxr-x xDanglingLink -> xNothing | |
| xLinkToFile = absolutize("xLinkToFile"); | |
| xLinkToLinkToFile = absolutize("xLinkToLinkToFile"); | |
| xLinkToDirectory = absolutize("xLinkToDirectory"); | |
| xDanglingLink = absolutize("xDanglingLink"); | |
| createSymbolicLink(xLinkToFile, xFile); | |
| createSymbolicLink(xLinkToLinkToFile, xLinkToFile); | |
| createSymbolicLink(xLinkToDirectory, xEmptyDirectory); | |
| createSymbolicLink(xDanglingLink, xNothing); | |
| } | |
| @Test | |
| public void testCreateLinkToFile() throws IOException { | |
| Path newPath = xEmptyDirectory.getChild("new-file"); | |
| FileSystemUtils.createEmptyFile(newPath); | |
| Path linkPath = xEmptyDirectory.getChild("some-link"); | |
| createSymbolicLink(linkPath, newPath); | |
| assertThat(linkPath.isSymbolicLink()).isTrue(); | |
| assertThat(linkPath.isFile()).isTrue(); | |
| assertThat(linkPath.isFile(Symlinks.NOFOLLOW)).isFalse(); | |
| assertThat(linkPath.isFile(Symlinks.FOLLOW)).isTrue(); | |
| assertThat(linkPath.isDirectory()).isFalse(); | |
| assertThat(linkPath.isDirectory(Symlinks.NOFOLLOW)).isFalse(); | |
| assertThat(linkPath.isDirectory(Symlinks.FOLLOW)).isFalse(); | |
| if (testFS.supportsSymbolicLinksNatively(linkPath.asFragment())) { | |
| assertThat(linkPath.getFileSize(Symlinks.NOFOLLOW)).isEqualTo(newPath.toString().length()); | |
| assertThat(linkPath.getFileSize()).isEqualTo(newPath.getFileSize(Symlinks.NOFOLLOW)); | |
| } | |
| assertThat(linkPath.getParentDirectory().getDirectoryEntries()).hasSize(2); | |
| assertThat(linkPath.getParentDirectory().getDirectoryEntries()).containsExactly(newPath, | |
| linkPath); | |
| } | |
| @Test | |
| public void testCreateLinkToDirectory() throws IOException { | |
| Path newPath = xEmptyDirectory.getChild("new-file"); | |
| newPath.createDirectory(); | |
| Path linkPath = xEmptyDirectory.getChild("some-link"); | |
| createSymbolicLink(linkPath, newPath); | |
| assertThat(linkPath.isSymbolicLink()).isTrue(); | |
| assertThat(linkPath.isFile()).isFalse(); | |
| assertThat(linkPath.isDirectory()).isTrue(); | |
| assertThat(linkPath.getParentDirectory().getDirectoryEntries()).hasSize(2); | |
| assertThat(linkPath.getParentDirectory(). | |
| getDirectoryEntries()).containsExactly(newPath, linkPath); | |
| } | |
| @Test | |
| public void testFileCanonicalPath() throws IOException { | |
| Path newPath = absolutize("new-file"); | |
| FileSystemUtils.createEmptyFile(newPath); | |
| newPath = newPath.resolveSymbolicLinks(); | |
| Path link1 = absolutize("some-link"); | |
| Path link2 = absolutize("some-link2"); | |
| createSymbolicLink(link1, newPath); | |
| createSymbolicLink(link2, link1); | |
| assertCanonicalPathsMatch(newPath, link1, link2); | |
| } | |
| @Test | |
| public void testDirectoryCanonicalPath() throws IOException { | |
| Path newPath = absolutize("new-folder"); | |
| newPath.createDirectory(); | |
| newPath = newPath.resolveSymbolicLinks(); | |
| Path newFile = newPath.getChild("file"); | |
| FileSystemUtils.createEmptyFile(newFile); | |
| Path link1 = absolutize("some-link"); | |
| Path link2 = absolutize("some-link2"); | |
| createSymbolicLink(link1, newPath); | |
| createSymbolicLink(link2, link1); | |
| Path linkFile1 = link1.getChild("file"); | |
| Path linkFile2 = link2.getChild("file"); | |
| assertCanonicalPathsMatch(newFile, linkFile1, linkFile2); | |
| } | |
| private void assertCanonicalPathsMatch(Path newPath, Path link1, Path link2) | |
| throws IOException { | |
| assertThat(link1.resolveSymbolicLinks()).isEqualTo(newPath); | |
| assertThat(link2.resolveSymbolicLinks()).isEqualTo(newPath); | |
| } | |
| // | |
| // createDirectory | |
| // | |
| @Test | |
| public void testCreateDirectoryWhereDanglingSymlinkAlreadyExists() throws Exception { | |
| IOException e = assertThrows(IOException.class, () -> xDanglingLink.createDirectory()); | |
| assertThat(e).hasMessageThat().isEqualTo(xDanglingLink + " (File exists)"); | |
| assertThat(xDanglingLink.isSymbolicLink()).isTrue(); // still a symbolic link | |
| assertThat(xDanglingLink.isDirectory(Symlinks.FOLLOW)).isFalse(); // link still dangles | |
| } | |
| @Test | |
| public void testCreateDirectoryWhereSymlinkAlreadyExists() throws Exception { | |
| IOException e = assertThrows(IOException.class, () -> xLinkToDirectory.createDirectory()); | |
| assertThat(e).hasMessageThat().isEqualTo(xLinkToDirectory + " (File exists)"); | |
| assertThat(xLinkToDirectory.isSymbolicLink()).isTrue(); // still a symbolic link | |
| assertThat(xLinkToDirectory.isDirectory(Symlinks.FOLLOW)).isTrue(); // link still points to dir | |
| } | |
| // createSymbolicLink(PathFragment) | |
| @Test | |
| public void testCreateSymbolicLinkFromFragment() throws IOException { | |
| String[] linkTargets = { | |
| "foo", | |
| "foo/bar", | |
| ".", | |
| "..", | |
| "../foo", | |
| "../../foo", | |
| "../../../../../../../../../../../../../../../../../../../../../foo", | |
| "/foo", | |
| "/foo/bar", | |
| "/..", | |
| "/foo/../bar", | |
| }; | |
| Path linkPath = absolutize("link"); | |
| for (String linkTarget : linkTargets) { | |
| PathFragment relative = PathFragment.create(linkTarget); | |
| linkPath.delete(); | |
| createSymbolicLink(linkPath, relative); | |
| if (testFS.supportsSymbolicLinksNatively(linkPath.asFragment())) { | |
| assertThat(linkPath.getFileSize(Symlinks.NOFOLLOW)) | |
| .isEqualTo(relative.getSafePathString().length()); | |
| assertThat(linkPath.readSymbolicLink()).isEqualTo(relative); | |
| } | |
| } | |
| } | |
| @Test | |
| public void testLinkToRootResolvesCorrectly() throws IOException { | |
| if (OS.getCurrent() == OS.WINDOWS) { | |
| // This test cannot be run on Windows, it mixes "/" paths with "C:/" paths | |
| return; | |
| } | |
| Path rootPath = testFS.getPath("/"); | |
| try { | |
| rootPath.getChild("testDir").createDirectory(); | |
| } catch (IOException e) { | |
| // Do nothing. This is a real FS, and we don't have permission. | |
| } | |
| Path linkPath = absolutize("link"); | |
| createSymbolicLink(linkPath, rootPath); | |
| // resolveSymbolicLinks requires an existing path: | |
| assertThrows( | |
| FileNotFoundException.class, () -> linkPath.getRelative("test").resolveSymbolicLinks()); | |
| // The path may not be a symlink, neither on Darwin nor on Linux. | |
| String nonLinkEntry = null; | |
| for (String child : testFS.getDirectoryEntries(rootPath.asFragment())) { | |
| Path p = rootPath.getChild(child); | |
| if (!p.isSymbolicLink() && p.isDirectory()) { | |
| nonLinkEntry = p.getBaseName(); | |
| break; | |
| } | |
| } | |
| assertThat(nonLinkEntry).isNotNull(); | |
| Path rootChild = testFS.getPath("/" + nonLinkEntry); | |
| assertThat(linkPath.getRelative(nonLinkEntry).resolveSymbolicLinks()).isEqualTo(rootChild); | |
| } | |
| @Test | |
| public void testLinkToFragmentContainingLinkResolvesCorrectly() throws IOException { | |
| Path link1 = absolutize("link1"); | |
| PathFragment link1target = PathFragment.create("link2/foo"); | |
| Path link2 = absolutize("link2"); | |
| Path link2target = xNonEmptyDirectory; | |
| createSymbolicLink(link1, link1target); // ln -s link2/foo link1 | |
| createSymbolicLink(link2, link2target); // ln -s xNonEmptyDirectory link2 | |
| // link1 --> xNonEmptyDirectory/foo | |
| assertThat(link2target.getRelative("foo")).isEqualTo(link1.resolveSymbolicLinks()); | |
| } | |
| // | |
| // readSymbolicLink / resolveSymbolicLinks | |
| // | |
| @Test | |
| public void testRecursiveSymbolicLink() throws IOException { | |
| Path link = absolutize("recursive-link"); | |
| createSymbolicLink(link, link); | |
| if (testFS.supportsSymbolicLinksNatively(link.asFragment())) { | |
| IOException e = assertThrows(IOException.class, () -> link.resolveSymbolicLinks()); | |
| assertThat(e).hasMessageThat().isEqualTo(link + " (Too many levels of symbolic links)"); | |
| } | |
| } | |
| @Test | |
| public void testMutuallyRecursiveSymbolicLinks() throws IOException { | |
| Path link1 = absolutize("link1"); | |
| Path link2 = absolutize("link2"); | |
| createSymbolicLink(link2, link1); | |
| createSymbolicLink(link1, link2); | |
| if (testFS.supportsSymbolicLinksNatively(link1.asFragment())) { | |
| IOException e = assertThrows(IOException.class, () -> link1.resolveSymbolicLinks()); | |
| assertThat(e).hasMessageThat().isEqualTo(link1 + " (Too many levels of symbolic links)"); | |
| } | |
| } | |
| @Test | |
| public void testResolveSymbolicLinksENOENT() { | |
| if (testFS.supportsSymbolicLinksNatively(xDanglingLink.asFragment())) { | |
| IOException e = assertThrows(IOException.class, () -> xDanglingLink.resolveSymbolicLinks()); | |
| assertThat(e).hasMessageThat().endsWith(xNothing + " (No such file or directory)"); | |
| } | |
| } | |
| @Test | |
| public void testResolveSymbolicLinksENOTDIR() throws IOException { | |
| Path badLinkTarget = xFile.getChild("bad"); // parent is not a directory! | |
| Path badLink = absolutize("badLink"); | |
| if (testFS.supportsSymbolicLinksNatively(badLink.asFragment())) { | |
| createSymbolicLink(badLink, badLinkTarget); | |
| assertThrows(IOException.class, badLink::resolveSymbolicLinks); | |
| } | |
| } | |
| @Test | |
| public void testResolveSymbolicLinksWithUplevelRefs() throws IOException { | |
| if (testFS.supportsSymbolicLinksNatively(xLinkToFile.asFragment())) { | |
| // Create a series of links that refer to xFile as ./xFile, | |
| // ./../foo/xFile, ./../../bar/foo/xFile, etc. They should all resolve | |
| // to xFile. | |
| Path ancestor = xFile; | |
| String prefix = "./"; | |
| while ((ancestor = ancestor.getParentDirectory()) != null) { | |
| xLinkToFile.delete(); | |
| createSymbolicLink(xLinkToFile, PathFragment.create(prefix + xFile.relativeTo(ancestor))); | |
| assertThat(xLinkToFile.resolveSymbolicLinks()).isEqualTo(xFile); | |
| prefix += "../"; | |
| } | |
| } | |
| } | |
| @Test | |
| public void testReadSymbolicLink() throws IOException { | |
| if (testFS.supportsSymbolicLinksNatively(xDanglingLink.asFragment())) { | |
| assertThat(xDanglingLink.readSymbolicLink().toString()).isEqualTo(xNothing.toString()); | |
| } | |
| assertThat(xLinkToFile.readSymbolicLink().toString()).isEqualTo(xFile.toString()); | |
| assertThat(xLinkToDirectory.readSymbolicLink().toString()) | |
| .isEqualTo(xEmptyDirectory.toString()); | |
| NotASymlinkException nase = | |
| assertThrows(NotASymlinkException.class, () -> xFile.readSymbolicLink()); | |
| assertThat(nase).hasMessageThat().isEqualTo(xFile.toString() + " is not a symlink"); | |
| FileNotFoundException fnfe = | |
| assertThrows(FileNotFoundException.class, () -> xNothing.readSymbolicLink()); | |
| assertThat(fnfe).hasMessageThat().endsWith(xNothing + " (No such file or directory)"); | |
| } | |
| @Test | |
| public void testCannotCreateSymbolicLinkWithReadOnlyParent() | |
| throws IOException { | |
| xEmptyDirectory.setWritable(false); | |
| Path xChildOfReadonlyDir = xEmptyDirectory.getChild("x"); | |
| if (testFS.supportsSymbolicLinksNatively(xChildOfReadonlyDir.asFragment())) { | |
| IOException e = | |
| assertThrows(IOException.class, () -> xChildOfReadonlyDir.createSymbolicLink(xNothing)); | |
| assertThat(e).hasMessageThat().endsWith(xChildOfReadonlyDir + " (Permission denied)"); | |
| } | |
| } | |
| // | |
| // createSymbolicLink | |
| // | |
| @Test | |
| public void testCanCreateDanglingLink() throws IOException { | |
| Path newPath = absolutize("non-existing-dir/new-file"); | |
| Path someLink = absolutize("dangling-link"); | |
| createSymbolicLink(someLink, newPath); | |
| assertThat(someLink.isSymbolicLink()).isTrue(); | |
| assertThat(someLink.exists(Symlinks.NOFOLLOW)).isTrue(); // the link itself exists | |
| assertThat(someLink.exists()).isFalse(); // ...but the referent doesn't | |
| if (testFS.supportsSymbolicLinksNatively(someLink.asFragment())) { | |
| FileNotFoundException e = | |
| assertThrows(FileNotFoundException.class, someLink::resolveSymbolicLinks); | |
| assertThat(e) | |
| .hasMessageThat() | |
| .endsWith(newPath.getParentDirectory() + " (No such file or directory)"); | |
| } | |
| } | |
| @Test | |
| public void testCannotCreateSymbolicLinkWithoutParent() throws IOException { | |
| Path xChildOfMissingDir = xNothing.getChild("x"); | |
| if (testFS.supportsSymbolicLinksNatively(xChildOfMissingDir.asFragment())) { | |
| FileNotFoundException e = | |
| assertThrows( | |
| FileNotFoundException.class, () -> xChildOfMissingDir.createSymbolicLink(xFile)); | |
| assertThat(e).hasMessageThat().endsWith(" (No such file or directory)"); | |
| } | |
| } | |
| @Test | |
| public void testCreateSymbolicLinkWhereNothingExists() throws IOException { | |
| createSymbolicLink(xNothing, xFile); | |
| assertThat(xNothing.isSymbolicLink()).isTrue(); | |
| } | |
| @Test | |
| public void testCreateSymbolicLinkWhereDirectoryAlreadyExists() throws Exception { | |
| IOException e = | |
| assertThrows(IOException.class, () -> createSymbolicLink(xEmptyDirectory, xFile)); | |
| assertThat(e).hasMessageThat().endsWith(xEmptyDirectory + " (File exists)"); | |
| assertThat(xEmptyDirectory.isDirectory(Symlinks.NOFOLLOW)).isTrue(); | |
| } | |
| @Test | |
| public void testCreateSymbolicLinkWhereFileAlreadyExists() throws Exception { | |
| IOException e = | |
| assertThrows(IOException.class, () -> createSymbolicLink(xFile, xEmptyDirectory)); | |
| assertThat(e).hasMessageThat().endsWith(xFile + " (File exists)"); | |
| assertThat(xFile.isFile(Symlinks.NOFOLLOW)).isTrue(); | |
| } | |
| @Test | |
| public void testCreateSymbolicLinkWhereDanglingSymlinkAlreadyExists() throws Exception { | |
| IOException e = assertThrows(IOException.class, () -> createSymbolicLink(xDanglingLink, xFile)); | |
| assertThat(e).hasMessageThat().endsWith(xDanglingLink + " (File exists)"); | |
| assertThat(xDanglingLink.isSymbolicLink()).isTrue(); // still a symbolic link | |
| assertThat(xDanglingLink.isDirectory()).isFalse(); // link still dangles | |
| } | |
| @Test | |
| public void testCreateSymbolicLinkWhereSymlinkAlreadyExists() throws Exception { | |
| IOException e = | |
| assertThrows(IOException.class, () -> createSymbolicLink(xLinkToDirectory, xNothing)); | |
| assertThat(e).hasMessageThat().endsWith(xLinkToDirectory + " (File exists)"); | |
| assertThat(xLinkToDirectory.isSymbolicLink()).isTrue(); // still a symbolic link | |
| assertThat(xLinkToDirectory.isDirectory()).isTrue(); // link still points to dir | |
| } | |
| @Test | |
| public void testDeleteLink() throws IOException { | |
| Path newPath = xEmptyDirectory.getChild("new-file"); | |
| Path someLink = xEmptyDirectory.getChild("a-link"); | |
| FileSystemUtils.createEmptyFile(newPath); | |
| createSymbolicLink(someLink, newPath); | |
| assertThat(xEmptyDirectory.getDirectoryEntries()).hasSize(2); | |
| assertThat(someLink.delete()).isTrue(); | |
| assertThat(xEmptyDirectory.getDirectoryEntries()).hasSize(1); | |
| assertThat(xEmptyDirectory.getDirectoryEntries()).containsExactly(newPath); | |
| } | |
| // Testing the links | |
| @Test | |
| public void testLinkFollowedToDirectory() throws IOException { | |
| Path theDirectory = absolutize("foo/"); | |
| assertThat(theDirectory.createDirectory()).isTrue(); | |
| Path newPath1 = absolutize("foo/new-file-1"); | |
| Path newPath2 = absolutize("foo/new-file-2"); | |
| Path newPath3 = absolutize("foo/new-file-3"); | |
| FileSystemUtils.createEmptyFile(newPath1); | |
| FileSystemUtils.createEmptyFile(newPath2); | |
| FileSystemUtils.createEmptyFile(newPath3); | |
| Path linkPath = absolutize("link"); | |
| createSymbolicLink(linkPath, theDirectory); | |
| Path resultPath1 = absolutize("link/new-file-1"); | |
| Path resultPath2 = absolutize("link/new-file-2"); | |
| Path resultPath3 = absolutize("link/new-file-3"); | |
| assertThat(linkPath.getDirectoryEntries()).containsExactly(resultPath1, resultPath2, | |
| resultPath3); | |
| } | |
| @Test | |
| public void testDanglingLinkIsNoFile() throws IOException { | |
| Path newPath1 = absolutize("new-file-1"); | |
| Path newPath2 = absolutize("new-file-2"); | |
| FileSystemUtils.createEmptyFile(newPath1); | |
| assertThat(newPath2.createDirectory()).isTrue(); | |
| Path linkPath1 = absolutize("link1"); | |
| Path linkPath2 = absolutize("link2"); | |
| createSymbolicLink(linkPath1, newPath1); | |
| createSymbolicLink(linkPath2, newPath2); | |
| newPath1.delete(); | |
| newPath2.delete(); | |
| assertThat(linkPath1.isFile()).isFalse(); | |
| assertThat(linkPath2.isDirectory()).isFalse(); | |
| } | |
| @Test | |
| public void testWriteOnLinkChangesFile() throws IOException { | |
| Path testFile = absolutize("test-file"); | |
| FileSystemUtils.createEmptyFile(testFile); | |
| String testData = "abc19"; | |
| Path testLink = absolutize("a-link"); | |
| createSymbolicLink(testLink, testFile); | |
| FileSystemUtils.writeContentAsLatin1(testLink, testData); | |
| String resultData = | |
| new String(FileSystemUtils.readContentAsLatin1(testFile)); | |
| assertThat(resultData).isEqualTo(testData); | |
| } | |
| // | |
| // Symlink tests: | |
| // | |
| @Test | |
| public void testExistsWithSymlinks() throws IOException { | |
| Path a = absolutize("a"); | |
| Path b = absolutize("b"); | |
| FileSystemUtils.createEmptyFile(b); | |
| createSymbolicLink(a, b); // ln -sf "b" "a" | |
| assertThat(a.exists()).isTrue(); // = exists(FOLLOW) | |
| assertThat(b.exists()).isTrue(); // = exists(FOLLOW) | |
| assertThat(a.exists(Symlinks.FOLLOW)).isTrue(); | |
| assertThat(b.exists(Symlinks.FOLLOW)).isTrue(); | |
| assertThat(a.exists(Symlinks.NOFOLLOW)).isTrue(); | |
| assertThat(b.exists(Symlinks.NOFOLLOW)).isTrue(); | |
| b.delete(); // "a" is now a dangling link | |
| assertThat(a.exists()).isFalse(); // = exists(FOLLOW) | |
| assertThat(b.exists()).isFalse(); // = exists(FOLLOW) | |
| assertThat(a.exists(Symlinks.FOLLOW)).isFalse(); | |
| assertThat(b.exists(Symlinks.FOLLOW)).isFalse(); | |
| assertThat(a.exists(Symlinks.NOFOLLOW)).isTrue(); // symlink still exists | |
| assertThat(b.exists(Symlinks.NOFOLLOW)).isFalse(); | |
| } | |
| @Test | |
| public void testIsDirectoryWithSymlinks() throws IOException { | |
| Path a = absolutize("a"); | |
| Path b = absolutize("b"); | |
| b.createDirectory(); | |
| createSymbolicLink(a, b); // ln -sf "b" "a" | |
| assertThat(a.isDirectory()).isTrue(); // = isDirectory(FOLLOW) | |
| assertThat(b.isDirectory()).isTrue(); // = isDirectory(FOLLOW) | |
| assertThat(a.isDirectory(Symlinks.FOLLOW)).isTrue(); | |
| assertThat(b.isDirectory(Symlinks.FOLLOW)).isTrue(); | |
| assertThat(a.isDirectory(Symlinks.NOFOLLOW)).isFalse(); // it's a link! | |
| assertThat(b.isDirectory(Symlinks.NOFOLLOW)).isTrue(); | |
| b.delete(); // "a" is now a dangling link | |
| assertThat(a.isDirectory()).isFalse(); // = isDirectory(FOLLOW) | |
| assertThat(b.isDirectory()).isFalse(); // = isDirectory(FOLLOW) | |
| assertThat(a.isDirectory(Symlinks.FOLLOW)).isFalse(); | |
| assertThat(b.isDirectory(Symlinks.FOLLOW)).isFalse(); | |
| assertThat(a.isDirectory(Symlinks.NOFOLLOW)).isFalse(); | |
| assertThat(b.isDirectory(Symlinks.NOFOLLOW)).isFalse(); | |
| } | |
| @Test | |
| public void testIsFileWithSymlinks() throws IOException { | |
| Path a = absolutize("a"); | |
| Path b = absolutize("b"); | |
| FileSystemUtils.createEmptyFile(b); | |
| createSymbolicLink(a, b); // ln -sf "b" "a" | |
| assertThat(a.isFile()).isTrue(); // = isFile(FOLLOW) | |
| assertThat(b.isFile()).isTrue(); // = isFile(FOLLOW) | |
| assertThat(a.isFile(Symlinks.FOLLOW)).isTrue(); | |
| assertThat(b.isFile(Symlinks.FOLLOW)).isTrue(); | |
| assertThat(a.isFile(Symlinks.NOFOLLOW)).isFalse(); // it's a link! | |
| assertThat(b.isFile(Symlinks.NOFOLLOW)).isTrue(); | |
| b.delete(); // "a" is now a dangling link | |
| assertThat(a.isFile()).isFalse(); // = isFile() | |
| assertThat(b.isFile()).isFalse(); // = isFile() | |
| assertThat(a.isFile()).isFalse(); | |
| assertThat(b.isFile()).isFalse(); | |
| assertThat(a.isFile(Symlinks.NOFOLLOW)).isFalse(); | |
| assertThat(b.isFile(Symlinks.NOFOLLOW)).isFalse(); | |
| } | |
| @Test | |
| public void testGetDirectoryEntriesOnLinkToDirectory() throws Exception { | |
| Path fooAlias = xNothing.getChild("foo"); | |
| createSymbolicLink(xNothing, xNonEmptyDirectory); | |
| Collection<Path> dirents = xNothing.getDirectoryEntries(); | |
| assertThat(dirents).containsExactly(fooAlias); | |
| } | |
| @Test | |
| public void testFilesOfLinkedDirectories() throws Exception { | |
| Path child = xEmptyDirectory.getChild("child"); | |
| Path aliasToChild = xLinkToDirectory.getChild("child"); | |
| assertThat(aliasToChild.exists()).isFalse(); | |
| FileSystemUtils.createEmptyFile(child); | |
| assertThat(aliasToChild.exists()).isTrue(); | |
| assertThat(aliasToChild.isFile()).isTrue(); | |
| assertThat(aliasToChild.isDirectory()).isFalse(); | |
| validateLinkedReferenceObeysReadOnly(child, aliasToChild); | |
| validateLinkedReferenceObeysExecutable(child, aliasToChild); | |
| } | |
| @Test | |
| public void testDirectoriesOfLinkedDirectories() throws Exception { | |
| Path childDir = xEmptyDirectory.getChild("childDir"); | |
| Path linkToChildDir = xLinkToDirectory.getChild("childDir"); | |
| assertThat(linkToChildDir.exists()).isFalse(); | |
| childDir.createDirectory(); | |
| assertThat(linkToChildDir.exists()).isTrue(); | |
| assertThat(linkToChildDir.isDirectory()).isTrue(); | |
| assertThat(linkToChildDir.isFile()).isFalse(); | |
| validateLinkedReferenceObeysReadOnly(childDir, linkToChildDir); | |
| validateLinkedReferenceObeysExecutable(childDir, linkToChildDir); | |
| } | |
| @Test | |
| public void testDirectoriesOfLinkedDirectoriesOfLinkedDirectories() throws Exception { | |
| Path childDir = xEmptyDirectory.getChild("childDir"); | |
| Path linkToLinkToDirectory = absolutize("xLinkToLinkToDirectory"); | |
| createSymbolicLink(linkToLinkToDirectory, xLinkToDirectory); | |
| Path linkToChildDir = linkToLinkToDirectory.getChild("childDir"); | |
| assertThat(linkToChildDir.exists()).isFalse(); | |
| childDir.createDirectory(); | |
| assertThat(linkToChildDir.exists()).isTrue(); | |
| assertThat(linkToChildDir.isDirectory()).isTrue(); | |
| assertThat(linkToChildDir.isFile()).isFalse(); | |
| validateLinkedReferenceObeysReadOnly(childDir, linkToChildDir); | |
| validateLinkedReferenceObeysExecutable(childDir, linkToChildDir); | |
| } | |
| private void validateLinkedReferenceObeysReadOnly(Path path, Path link) throws IOException { | |
| path.setWritable(false); | |
| assertThat(path.isWritable()).isFalse(); | |
| assertThat(link.isWritable()).isFalse(); | |
| path.setWritable(true); | |
| assertThat(path.isWritable()).isTrue(); | |
| assertThat(link.isWritable()).isTrue(); | |
| path.setWritable(false); | |
| assertThat(path.isWritable()).isFalse(); | |
| assertThat(link.isWritable()).isFalse(); | |
| } | |
| private void validateLinkedReferenceObeysExecutable(Path path, Path link) throws IOException { | |
| path.setExecutable(true); | |
| assertThat(path.isExecutable()).isTrue(); | |
| assertThat(link.isExecutable()).isTrue(); | |
| path.setExecutable(false); | |
| assertThat(path.isExecutable()).isFalse(); | |
| assertThat(link.isExecutable()).isFalse(); | |
| path.setExecutable(true); | |
| assertThat(path.isExecutable()).isTrue(); | |
| assertThat(link.isExecutable()).isTrue(); | |
| } | |
| @Test | |
| public void testReadingFileFromLinkedDirectory() throws Exception { | |
| Path linkedTo = absolutize("linkedTo"); | |
| linkedTo.createDirectory(); | |
| Path child = linkedTo.getChild("child"); | |
| FileSystemUtils.createEmptyFile(child); | |
| byte[] outputData = "This is a test".getBytes(); | |
| FileSystemUtils.writeContent(child, outputData); | |
| Path link = absolutize("link"); | |
| createSymbolicLink(link, linkedTo); | |
| Path linkedChild = link.getChild("child"); | |
| byte[] inputData = FileSystemUtils.readContent(linkedChild); | |
| assertThat(inputData).isEqualTo(outputData); | |
| } | |
| @Test | |
| public void testCreatingFileInLinkedDirectory() throws Exception { | |
| Path linkedTo = absolutize("linkedTo"); | |
| linkedTo.createDirectory(); | |
| Path child = linkedTo.getChild("child"); | |
| Path link = absolutize("link"); | |
| createSymbolicLink(link, linkedTo); | |
| Path linkedChild = link.getChild("child"); | |
| byte[] outputData = "This is a test".getBytes(); | |
| FileSystemUtils.writeContent(linkedChild, outputData); | |
| byte[] inputData = FileSystemUtils.readContent(child); | |
| assertThat(inputData).isEqualTo(outputData); | |
| } | |
| @Test | |
| public void testUtf8Symlink() throws Exception { | |
| assumeUtf8CompatibleEncoding(); | |
| String target = StringEncoding.unicodeToInternal("入力_A_🌱.target"); | |
| Path link = absolutize(StringEncoding.unicodeToInternal("入力_A_🌱.txt")); | |
| createSymbolicLink(link, PathFragment.create(target)); | |
| assertThat(link.readSymbolicLink().toString()).isEqualTo(target); | |
| java.nio.file.Path javaPath = getJavaPathOrSkipIfUnsupported(link); | |
| assertThat(platformToUnicode(Files.readSymbolicLink(javaPath).toString())) | |
| .isEqualTo("入力_A_🌱.target"); | |
| } | |
| } |