| // 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 static com.google.devtools.build.lib.vfs.FileSystemUtils.appendWithoutExtension; |
| import static com.google.devtools.build.lib.vfs.FileSystemUtils.commonAncestor; |
| import static com.google.devtools.build.lib.vfs.FileSystemUtils.copyFile; |
| import static com.google.devtools.build.lib.vfs.FileSystemUtils.copyTool; |
| import static com.google.devtools.build.lib.vfs.FileSystemUtils.moveFile; |
| import static com.google.devtools.build.lib.vfs.FileSystemUtils.relativePath; |
| import static com.google.devtools.build.lib.vfs.FileSystemUtils.removeExtension; |
| import static com.google.devtools.build.lib.vfs.FileSystemUtils.touchFile; |
| import static com.google.devtools.build.lib.vfs.FileSystemUtils.traverseTree; |
| import static java.nio.charset.StandardCharsets.ISO_8859_1; |
| import static java.nio.charset.StandardCharsets.UTF_8; |
| |
| import com.google.common.collect.Lists; |
| import com.google.devtools.build.lib.testutil.BlazeTestUtils; |
| import com.google.devtools.build.lib.testutil.ManualClock; |
| import com.google.devtools.build.lib.vfs.FileSystemUtils.MoveResult; |
| import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.nio.charset.StandardCharsets; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** |
| * This class tests the file system utilities. |
| */ |
| @RunWith(JUnit4.class) |
| public class FileSystemUtilsTest { |
| private ManualClock clock; |
| private FileSystem fileSystem; |
| private Path workingDir; |
| |
| @Before |
| public final void initializeFileSystem() throws Exception { |
| clock = new ManualClock(); |
| fileSystem = new InMemoryFileSystem(clock); |
| workingDir = fileSystem.getPath("/workingDir"); |
| workingDir.createDirectory(); |
| } |
| |
| Path topDir; |
| Path file1; |
| Path file2; |
| Path aDir; |
| Path bDir; |
| Path file3; |
| Path innerDir; |
| Path link1; |
| Path dirLink; |
| Path file4; |
| Path file5; |
| |
| /* |
| * Build a directory tree that looks like: |
| * top-dir/ |
| * file-1 |
| * file-2 |
| * a-dir/ |
| * file-3 |
| * inner-dir/ |
| * link-1 => file-4 |
| * dir-link => b-dir |
| * file-4 |
| */ |
| private void createTestDirectoryTree() throws IOException { |
| topDir = fileSystem.getPath("/top-dir"); |
| file1 = fileSystem.getPath("/top-dir/file-1"); |
| file2 = fileSystem.getPath("/top-dir/file-2"); |
| aDir = fileSystem.getPath("/top-dir/a-dir"); |
| bDir = fileSystem.getPath("/top-dir/b-dir"); |
| file3 = fileSystem.getPath("/top-dir/a-dir/file-3"); |
| innerDir = fileSystem.getPath("/top-dir/a-dir/inner-dir"); |
| link1 = fileSystem.getPath("/top-dir/a-dir/inner-dir/link-1"); |
| dirLink = fileSystem.getPath("/top-dir/a-dir/inner-dir/dir-link"); |
| file4 = fileSystem.getPath("/file-4"); |
| file5 = fileSystem.getPath("/top-dir/b-dir/file-5"); |
| |
| topDir.createDirectory(); |
| FileSystemUtils.createEmptyFile(file1); |
| FileSystemUtils.createEmptyFile(file2); |
| aDir.createDirectory(); |
| bDir.createDirectory(); |
| FileSystemUtils.createEmptyFile(file3); |
| innerDir.createDirectory(); |
| link1.createSymbolicLink(file4); // simple symlink |
| dirLink.createSymbolicLink(bDir); |
| FileSystemUtils.createEmptyFile(file4); |
| FileSystemUtils.createEmptyFile(file5); |
| } |
| |
| private Path checkTestDirectoryTreesBelowExceptSymlinks(Path toPath) throws IOException { |
| Path copiedFile1 = toPath.getChild("file-1"); |
| assertThat(copiedFile1.exists()).isTrue(); |
| assertThat(copiedFile1.isFile()).isTrue(); |
| |
| Path copiedFile2 = toPath.getChild("file-2"); |
| assertThat(copiedFile2.exists()).isTrue(); |
| assertThat(copiedFile2.isFile()).isTrue(); |
| |
| Path copiedADir = toPath.getChild("a-dir"); |
| assertThat(copiedADir.exists()).isTrue(); |
| assertThat(copiedADir.isDirectory()).isTrue(); |
| Collection<Path> aDirEntries = copiedADir.getDirectoryEntries(); |
| assertThat(aDirEntries).hasSize(2); |
| |
| Path copiedFile3 = copiedADir.getChild("file-3"); |
| assertThat(copiedFile3.exists()).isTrue(); |
| assertThat(copiedFile3.isFile()).isTrue(); |
| |
| Path copiedInnerDir = copiedADir.getChild("inner-dir"); |
| assertThat(copiedInnerDir.exists()).isTrue(); |
| assertThat(copiedInnerDir.isDirectory()).isTrue(); |
| return copiedInnerDir; |
| } |
| |
| private void checkTestDirectoryTreesBelow(Path toPath) throws IOException { |
| Path copiedInnerDir = checkTestDirectoryTreesBelowExceptSymlinks(toPath); |
| Path copiedLink1 = copiedInnerDir.getChild("link-1"); |
| assertThat(copiedLink1.exists()).isTrue(); |
| assertThat(copiedLink1.isSymbolicLink()).isFalse(); |
| |
| Path copiedDirLink = copiedInnerDir.getChild("dir-link"); |
| assertThat(copiedDirLink.exists()).isTrue(); |
| assertThat(copiedDirLink.isDirectory()).isTrue(); |
| assertThat(copiedDirLink.getChild("file-5").exists()).isTrue(); |
| } |
| |
| // tests |
| |
| @Test |
| public void testChangeModtime() throws IOException { |
| Path file = fileSystem.getPath("/my-file"); |
| assertThrows(FileNotFoundException.class, () -> BlazeTestUtils.changeModtime(file)); |
| FileSystemUtils.createEmptyFile(file); |
| long prevMtime = file.getLastModifiedTime(); |
| BlazeTestUtils.changeModtime(file); |
| assertThat(prevMtime == file.getLastModifiedTime()).isFalse(); |
| } |
| |
| @Test |
| public void testCommonAncestor() { |
| assertThat(commonAncestor(topDir, topDir)).isEqualTo(topDir); |
| assertThat(commonAncestor(file1, file3)).isEqualTo(topDir); |
| assertThat(commonAncestor(file1, dirLink)).isEqualTo(topDir); |
| } |
| |
| @Test |
| public void testRelativePath() throws IOException { |
| createTestDirectoryTree(); |
| assertThat( |
| relativePath(PathFragment.create("/top-dir"), PathFragment.create("/top-dir/file-1")) |
| .getPathString()) |
| .isEqualTo("file-1"); |
| assertThat( |
| relativePath(PathFragment.create("/top-dir"), PathFragment.create("/top-dir")) |
| .getPathString()) |
| .isEqualTo(""); |
| assertThat( |
| relativePath( |
| PathFragment.create("/top-dir"), |
| PathFragment.create("/top-dir/a-dir/inner-dir/dir-link")) |
| .getPathString()) |
| .isEqualTo("a-dir/inner-dir/dir-link"); |
| assertThat( |
| relativePath(PathFragment.create("/top-dir"), PathFragment.create("/file-4")) |
| .getPathString()) |
| .isEqualTo("../file-4"); |
| assertThat( |
| relativePath( |
| PathFragment.create("/top-dir/a-dir/inner-dir"), PathFragment.create("/file-4")) |
| .getPathString()) |
| .isEqualTo("../../../file-4"); |
| } |
| |
| @Test |
| public void testRemoveExtension_Strings() throws Exception { |
| assertThat(removeExtension("foo.c")).isEqualTo("foo"); |
| assertThat(removeExtension("a/foo.c")).isEqualTo("a/foo"); |
| assertThat(removeExtension("a.b/foo")).isEqualTo("a.b/foo"); |
| assertThat(removeExtension("foo")).isEqualTo("foo"); |
| assertThat(removeExtension("foo.")).isEqualTo("foo"); |
| } |
| |
| @Test |
| public void testRemoveExtension_Paths() throws Exception { |
| assertPath("/foo", removeExtension(fileSystem.getPath("/foo.c"))); |
| assertPath("/a/foo", removeExtension(fileSystem.getPath("/a/foo.c"))); |
| assertPath("/a.b/foo", removeExtension(fileSystem.getPath("/a.b/foo"))); |
| assertPath("/foo", removeExtension(fileSystem.getPath("/foo"))); |
| assertPath("/foo", removeExtension(fileSystem.getPath("/foo."))); |
| } |
| |
| private static void assertPath(String expected, PathFragment actual) { |
| assertThat(actual.getPathString()).isEqualTo(expected); |
| } |
| |
| private static void assertPath(String expected, Path actual) { |
| assertThat(actual.getPathString()).isEqualTo(expected); |
| } |
| |
| @Test |
| public void testReplaceExtension_Path() throws Exception { |
| assertPath("/foo/bar.baz", |
| FileSystemUtils.replaceExtension(fileSystem.getPath("/foo/bar"), ".baz")); |
| assertPath("/foo/bar.baz", |
| FileSystemUtils.replaceExtension(fileSystem.getPath("/foo/bar.cc"), ".baz")); |
| assertPath("/foo.baz", FileSystemUtils.replaceExtension(fileSystem.getPath("/foo/"), ".baz")); |
| assertPath("/foo.baz", |
| FileSystemUtils.replaceExtension(fileSystem.getPath("/foo.cc/"), ".baz")); |
| assertPath("/foo.baz", FileSystemUtils.replaceExtension(fileSystem.getPath("/foo"), ".baz")); |
| assertPath("/foo.baz", FileSystemUtils.replaceExtension(fileSystem.getPath("/foo.cc"), ".baz")); |
| assertPath("/.baz", FileSystemUtils.replaceExtension(fileSystem.getPath("/.cc"), ".baz")); |
| assertThat(FileSystemUtils.replaceExtension(fileSystem.getPath("/"), ".baz")).isNull(); |
| } |
| |
| @Test |
| public void testReplaceExtension_PathFragment() throws Exception { |
| assertPath("foo/bar.baz", |
| FileSystemUtils.replaceExtension(PathFragment.create("foo/bar"), ".baz")); |
| assertPath("foo/bar.baz", |
| FileSystemUtils.replaceExtension(PathFragment.create("foo/bar.cc"), ".baz")); |
| assertPath("/foo/bar.baz", |
| FileSystemUtils.replaceExtension(PathFragment.create("/foo/bar"), ".baz")); |
| assertPath("/foo/bar.baz", |
| FileSystemUtils.replaceExtension(PathFragment.create("/foo/bar.cc"), ".baz")); |
| assertPath("foo.baz", FileSystemUtils.replaceExtension(PathFragment.create("foo/"), ".baz")); |
| assertPath("foo.baz", FileSystemUtils.replaceExtension(PathFragment.create("foo.cc/"), ".baz")); |
| assertPath("/foo.baz", FileSystemUtils.replaceExtension(PathFragment.create("/foo/"), ".baz")); |
| assertPath("/foo.baz", |
| FileSystemUtils.replaceExtension(PathFragment.create("/foo.cc/"), ".baz")); |
| assertPath("foo.baz", FileSystemUtils.replaceExtension(PathFragment.create("foo"), ".baz")); |
| assertPath("foo.baz", FileSystemUtils.replaceExtension(PathFragment.create("foo.cc"), ".baz")); |
| assertPath("/foo.baz", FileSystemUtils.replaceExtension(PathFragment.create("/foo"), ".baz")); |
| assertPath("/foo.baz", |
| FileSystemUtils.replaceExtension(PathFragment.create("/foo.cc"), ".baz")); |
| assertPath(".baz", FileSystemUtils.replaceExtension(PathFragment.create(".cc"), ".baz")); |
| assertThat(FileSystemUtils.replaceExtension(PathFragment.create("/"), ".baz")).isNull(); |
| assertThat(FileSystemUtils.replaceExtension(PathFragment.create(""), ".baz")).isNull(); |
| assertPath("foo/bar.baz", |
| FileSystemUtils.replaceExtension(PathFragment.create("foo/bar.pony"), ".baz", ".pony")); |
| assertPath("foo/bar.baz", |
| FileSystemUtils.replaceExtension(PathFragment.create("foo/bar"), ".baz", "")); |
| assertThat(FileSystemUtils.replaceExtension(PathFragment.create(""), ".baz", ".pony")).isNull(); |
| assertThat( |
| FileSystemUtils.replaceExtension( |
| PathFragment.create("foo/bar.pony"), ".baz", ".unicorn")) |
| .isNull(); |
| } |
| |
| @Test |
| public void testAppendWithoutExtension() throws Exception { |
| assertPath("libfoo-src.jar", |
| appendWithoutExtension(PathFragment.create("libfoo.jar"), "-src")); |
| assertPath("foo/libfoo-src.jar", |
| appendWithoutExtension(PathFragment.create("foo/libfoo.jar"), "-src")); |
| assertPath("java/com/google/foo/libfoo-src.jar", |
| appendWithoutExtension(PathFragment.create("java/com/google/foo/libfoo.jar"), "-src")); |
| assertPath("libfoo.bar-src.jar", |
| appendWithoutExtension(PathFragment.create("libfoo.bar.jar"), "-src")); |
| assertPath("libfoo-src", |
| appendWithoutExtension(PathFragment.create("libfoo"), "-src")); |
| assertPath("libfoo-src.jar", |
| appendWithoutExtension(PathFragment.create("libfoo.jar/"), "-src")); |
| assertPath("libfoo.src.jar", |
| appendWithoutExtension(PathFragment.create("libfoo.jar"), ".src")); |
| assertThat(appendWithoutExtension(PathFragment.create("/"), "-src")).isNull(); |
| assertThat(appendWithoutExtension(PathFragment.create(""), "-src")).isNull(); |
| } |
| |
| @Test |
| public void testGetWorkingDirectory() { |
| String userDir = System.getProperty("user.dir"); |
| |
| assertThat(fileSystem.getPath(System.getProperty("user.dir", "/"))) |
| .isEqualTo(FileSystemUtils.getWorkingDirectory(fileSystem)); |
| |
| System.setProperty("user.dir", "/blah/blah/blah"); |
| assertThat(fileSystem.getPath("/blah/blah/blah")) |
| .isEqualTo(FileSystemUtils.getWorkingDirectory(fileSystem)); |
| |
| System.setProperty("user.dir", userDir); |
| } |
| |
| @Test |
| public void testResolveRelativeToFilesystemWorkingDir() { |
| PathFragment relativePath = PathFragment.create("relative/path"); |
| assertThat(workingDir.getRelative(relativePath)) |
| .isEqualTo(workingDir.getRelative(relativePath)); |
| |
| PathFragment absolutePath = PathFragment.create("/absolute/path"); |
| assertThat(workingDir.getRelative(absolutePath)).isEqualTo(fileSystem.getPath(absolutePath)); |
| } |
| |
| @Test |
| public void testTouchFileCreatesFile() throws IOException { |
| createTestDirectoryTree(); |
| Path nonExistingFile = fileSystem.getPath("/previously-non-existing"); |
| assertThat(nonExistingFile.exists()).isFalse(); |
| touchFile(nonExistingFile); |
| |
| assertThat(nonExistingFile.exists()).isTrue(); |
| } |
| |
| @Test |
| public void testTouchFileAdjustsFileTime() throws IOException { |
| createTestDirectoryTree(); |
| Path testFile = file4; |
| long oldTime = testFile.getLastModifiedTime(); |
| testFile.setLastModifiedTime(42); |
| touchFile(testFile); |
| |
| assertThat(testFile.getLastModifiedTime()).isAtLeast(oldTime); |
| } |
| |
| @Test |
| public void testCopyFile() throws IOException { |
| createTestDirectoryTree(); |
| Path originalFile = file1; |
| byte[] content = new byte[] { 'a', 'b', 'c', 23, 42 }; |
| FileSystemUtils.writeContent(originalFile, content); |
| |
| Path copyTarget = file2; |
| |
| copyFile(originalFile, copyTarget); |
| |
| assertThat(FileSystemUtils.readContent(copyTarget)).isEqualTo(content); |
| } |
| |
| @Test |
| public void testMoveFile() throws IOException { |
| createTestDirectoryTree(); |
| Path originalFile = file1; |
| byte[] content = new byte[] { 'a', 'b', 'c', 23, 42 }; |
| FileSystemUtils.writeContent(originalFile, content); |
| |
| Path moveTarget = file2; |
| |
| assertThat(moveFile(originalFile, moveTarget)).isEqualTo(MoveResult.FILE_MOVED); |
| |
| assertThat(FileSystemUtils.readContent(moveTarget)).isEqualTo(content); |
| assertThat(originalFile.exists()).isFalse(); |
| } |
| |
| @Test |
| public void testMoveFileAcrossDevices() throws Exception { |
| class MultipleDeviceFS extends InMemoryFileSystem { |
| @Override |
| public void renameTo(Path source, Path target) throws IOException { |
| if (!source.startsWith(target.asFragment().subFragment(0, 1))) { |
| throw new IOException("EXDEV"); |
| } |
| super.renameTo(source, target); |
| } |
| } |
| FileSystem fs = new MultipleDeviceFS(); |
| Path dev1 = fs.getPath("/fs1"); |
| dev1.createDirectory(); |
| Path dev2 = fs.getPath("/fs2"); |
| dev2.createDirectory(); |
| Path source = dev1.getChild("source"); |
| Path target = dev2.getChild("target"); |
| |
| FileSystemUtils.writeContent(source, UTF_8, "hello, world"); |
| source.setLastModifiedTime(142); |
| assertThat(FileSystemUtils.moveFile(source, target)).isEqualTo(MoveResult.FILE_COPIED); |
| assertThat(source.exists(Symlinks.NOFOLLOW)).isFalse(); |
| assertThat(target.isFile(Symlinks.NOFOLLOW)).isTrue(); |
| assertThat(FileSystemUtils.readContent(target, UTF_8)).isEqualTo("hello, world"); |
| assertThat(target.getLastModifiedTime()).isEqualTo(142); |
| |
| source.createSymbolicLink(PathFragment.create("link-target")); |
| assertThat(FileSystemUtils.moveFile(source, target)).isEqualTo(MoveResult.FILE_COPIED); |
| assertThat(source.exists(Symlinks.NOFOLLOW)).isFalse(); |
| assertThat(target.isSymbolicLink()).isTrue(); |
| assertThat(target.readSymbolicLink()).isEqualTo(PathFragment.create("link-target")); |
| } |
| |
| @Test |
| public void testReadContentWithLimit() throws IOException { |
| createTestDirectoryTree(); |
| String str = "this is a test of readContentWithLimit method"; |
| FileSystemUtils.writeContent(file1, StandardCharsets.ISO_8859_1, str); |
| assertThat(readStringFromFile(file1, 0)).isEmpty(); |
| assertThat(str.substring(0, 10)).isEqualTo(readStringFromFile(file1, 10)); |
| assertThat(str).isEqualTo(readStringFromFile(file1, 1000000)); |
| } |
| |
| private String readStringFromFile(Path file, int limit) throws IOException { |
| byte[] bytes = FileSystemUtils.readContentWithLimit(file, limit); |
| return new String(bytes, StandardCharsets.ISO_8859_1); |
| } |
| |
| @Test |
| public void testAppend() throws IOException { |
| createTestDirectoryTree(); |
| FileSystemUtils.writeIsoLatin1(file1, "nobody says "); |
| FileSystemUtils.writeIsoLatin1(file1, "mary had"); |
| FileSystemUtils.appendIsoLatin1(file1, "a little lamb"); |
| assertThat(new String(FileSystemUtils.readContentAsLatin1(file1))) |
| .isEqualTo("mary had\na little lamb\n"); |
| } |
| |
| @Test |
| public void testCopyFileAttributes() throws IOException { |
| createTestDirectoryTree(); |
| Path originalFile = file1; |
| byte[] content = new byte[] { 'a', 'b', 'c', 23, 42 }; |
| FileSystemUtils.writeContent(originalFile, content); |
| file1.setLastModifiedTime(12345L); |
| file1.setWritable(false); |
| file1.setExecutable(false); |
| |
| Path copyTarget = file2; |
| copyFile(originalFile, copyTarget); |
| |
| assertThat(file2.getLastModifiedTime()).isEqualTo(12345L); |
| assertThat(file2.isExecutable()).isFalse(); |
| assertThat(file2.isWritable()).isFalse(); |
| |
| file1.setWritable(true); |
| file1.setExecutable(true); |
| |
| copyFile(originalFile, copyTarget); |
| |
| assertThat(file2.getLastModifiedTime()).isEqualTo(12345L); |
| assertThat(file2.isExecutable()).isTrue(); |
| assertThat(file2.isWritable()).isTrue(); |
| } |
| |
| @Test |
| public void testCopyFileThrowsExceptionIfTargetCantBeDeleted() throws IOException { |
| createTestDirectoryTree(); |
| Path originalFile = file1; |
| byte[] content = new byte[] { 'a', 'b', 'c', 23, 42 }; |
| FileSystemUtils.writeContent(originalFile, content); |
| |
| IOException ex = assertThrows(IOException.class, () -> copyFile(originalFile, aDir)); |
| assertThat(ex) |
| .hasMessageThat() |
| .isEqualTo( |
| "error copying file: couldn't delete destination: " + aDir + " (Directory not empty)"); |
| } |
| |
| @Test |
| public void testCopyTool() throws IOException { |
| createTestDirectoryTree(); |
| Path originalFile = file1; |
| byte[] content = new byte[] { 'a', 'b', 'c', 23, 42 }; |
| FileSystemUtils.writeContent(originalFile, content); |
| |
| Path copyTarget = copyTool(topDir.getRelative("file-1"), aDir.getRelative("file-1")); |
| |
| assertThat(FileSystemUtils.readContent(copyTarget)).isEqualTo(content); |
| assertThat(copyTarget.isWritable()).isEqualTo(file1.isWritable()); |
| assertThat(copyTarget.isExecutable()).isEqualTo(file1.isExecutable()); |
| assertThat(copyTarget.getLastModifiedTime()).isEqualTo(file1.getLastModifiedTime()); |
| } |
| |
| @Test |
| public void testCopyTreesBelow() throws IOException { |
| createTestDirectoryTree(); |
| Path toPath = fileSystem.getPath("/copy-here"); |
| toPath.createDirectory(); |
| |
| FileSystemUtils.copyTreesBelow(topDir, toPath, Symlinks.FOLLOW); |
| checkTestDirectoryTreesBelow(toPath); |
| } |
| |
| @Test |
| public void testCopyTreesBelowWithOverriding() throws IOException { |
| createTestDirectoryTree(); |
| Path toPath = fileSystem.getPath("/copy-here"); |
| toPath.createDirectory(); |
| toPath.getChild("file-2"); |
| |
| FileSystemUtils.copyTreesBelow(topDir, toPath, Symlinks.FOLLOW); |
| checkTestDirectoryTreesBelow(toPath); |
| } |
| |
| @Test |
| public void testCopyTreesBelowToSubtree() throws IOException { |
| createTestDirectoryTree(); |
| IllegalArgumentException expected = |
| assertThrows( |
| IllegalArgumentException.class, |
| () -> FileSystemUtils.copyTreesBelow(topDir, aDir, Symlinks.FOLLOW)); |
| assertThat(expected).hasMessageThat().isEqualTo("/top-dir/a-dir is a subdirectory of /top-dir"); |
| } |
| |
| @Test |
| public void testCopyFileAsDirectoryTree() throws IOException { |
| createTestDirectoryTree(); |
| IOException expected = |
| assertThrows( |
| IOException.class, () -> FileSystemUtils.copyTreesBelow(file1, aDir, Symlinks.FOLLOW)); |
| assertThat(expected).hasMessageThat().isEqualTo("/top-dir/file-1 (Not a directory)"); |
| } |
| |
| @Test |
| public void testCopyTreesBelowToFile() throws IOException { |
| createTestDirectoryTree(); |
| Path copyDir = fileSystem.getPath("/my-dir"); |
| Path copySubDir = fileSystem.getPath("/my-dir/subdir"); |
| FileSystemUtils.createDirectoryAndParents(copySubDir); |
| IOException expected = |
| assertThrows( |
| IOException.class, |
| () -> FileSystemUtils.copyTreesBelow(copyDir, file4, Symlinks.FOLLOW)); |
| assertThat(expected).hasMessageThat().isEqualTo("/file-4 (Not a directory)"); |
| } |
| |
| @Test |
| public void testCopyTreesBelowFromUnexistingDir() throws IOException { |
| createTestDirectoryTree(); |
| |
| Path unexistingDir = fileSystem.getPath("/unexisting-dir"); |
| FileNotFoundException expected = |
| assertThrows( |
| FileNotFoundException.class, |
| () -> FileSystemUtils.copyTreesBelow(unexistingDir, aDir, Symlinks.FOLLOW)); |
| assertThat(expected).hasMessageThat().isEqualTo("/unexisting-dir (No such file or directory)"); |
| } |
| |
| @Test |
| public void testCopyTreesBelowNoFollowSymlinks() throws IOException { |
| createTestDirectoryTree(); |
| PathFragment relative1 = file1.relativeTo(topDir); |
| topDir.getRelative("relative-link").createSymbolicLink(relative1); |
| PathFragment relativeInner = PathFragment.create("..").getRelative(relative1); |
| bDir.getRelative("relative-inner-link").createSymbolicLink(relativeInner); |
| PathFragment rootDirectory = PathFragment.create("/"); |
| topDir.getRelative("absolute-link").createSymbolicLink(rootDirectory); |
| Path toPath = fileSystem.getPath("/copy-here"); |
| toPath.createDirectory(); |
| |
| FileSystemUtils.copyTreesBelow(topDir, toPath, Symlinks.NOFOLLOW); |
| Path copiedInnerDir = checkTestDirectoryTreesBelowExceptSymlinks(toPath); |
| Path copiedLink1 = copiedInnerDir.getChild("link-1"); |
| assertThat(copiedLink1.exists()).isTrue(); |
| assertThat(copiedLink1.isSymbolicLink()).isTrue(); |
| assertThat(copiedLink1.readSymbolicLink()).isEqualTo(file4.asFragment()); |
| |
| Path copiedDirLink = copiedInnerDir.getChild("dir-link"); |
| assertThat(copiedDirLink.exists()).isTrue(); |
| assertThat(copiedDirLink.isDirectory()).isTrue(); |
| assertThat(copiedDirLink.readSymbolicLink()).isEqualTo(bDir.asFragment()); |
| |
| assertThat(toPath.getRelative("relative-link").readSymbolicLink()).isEqualTo(relative1); |
| assertThat( |
| toPath |
| .getRelative(bDir.relativeTo(topDir)) |
| .getRelative("relative-inner-link") |
| .readSymbolicLink()) |
| .isEqualTo(relativeInner); |
| assertThat(toPath.getRelative("absolute-link").readSymbolicLink()).isEqualTo(rootDirectory); |
| } |
| |
| @Test |
| public void testTraverseTree() throws IOException { |
| createTestDirectoryTree(); |
| |
| Collection<Path> paths = traverseTree(topDir, p -> !p.getPathString().contains("a-dir")); |
| assertThat(paths).containsExactly(file1, file2, bDir, file5); |
| } |
| |
| @Test |
| public void testTraverseTreeDeep() throws IOException { |
| createTestDirectoryTree(); |
| |
| Collection<Path> paths = traverseTree(topDir, ignored -> true); |
| assertThat(paths).containsExactly(aDir, |
| file3, |
| innerDir, |
| link1, |
| file1, |
| file2, |
| dirLink, |
| bDir, |
| file5); |
| } |
| |
| @Test |
| public void testTraverseTreeLinkDir() throws IOException { |
| // Use a new little tree for this test: |
| // top-dir/ |
| // dir-link2 => linked-dir |
| // linked-dir/ |
| // file |
| topDir = fileSystem.getPath("/top-dir"); |
| Path dirLink2 = fileSystem.getPath("/top-dir/dir-link2"); |
| Path linkedDir = fileSystem.getPath("/linked-dir"); |
| Path linkedDirFile = fileSystem.getPath("/top-dir/dir-link2/file"); |
| |
| topDir.createDirectory(); |
| linkedDir.createDirectory(); |
| dirLink2.createSymbolicLink(linkedDir); // simple symlink |
| FileSystemUtils.createEmptyFile(linkedDirFile); // created through the link |
| |
| // traverseTree doesn't follow links: |
| Collection<Path> paths = traverseTree(topDir, ignored -> true); |
| assertThat(paths).containsExactly(dirLink2); |
| |
| paths = traverseTree(linkedDir, ignored -> true); |
| assertThat(paths).containsExactly(fileSystem.getPath("/linked-dir/file")); |
| } |
| |
| @Test |
| public void testWriteIsoLatin1() throws Exception { |
| Path file = fileSystem.getPath("/does/not/exist/yet.txt"); |
| FileSystemUtils.writeIsoLatin1(file, "Line 1", "Line 2", "Line 3"); |
| String expected = "Line 1\nLine 2\nLine 3\n"; |
| String actual = new String(FileSystemUtils.readContentAsLatin1(file)); |
| assertThat(actual).isEqualTo(expected); |
| } |
| |
| @Test |
| public void testWriteLinesAs() throws Exception { |
| Path file = fileSystem.getPath("/does/not/exist/yet.txt"); |
| FileSystemUtils.writeLinesAs(file, UTF_8, "\u00F6"); // an oe umlaut |
| byte[] expected = new byte[] {(byte) 0xC3, (byte) 0xB6, 0x0A};//"\u00F6\n"; |
| byte[] actual = FileSystemUtils.readContent(file); |
| assertThat(actual).isEqualTo(expected); |
| } |
| |
| @Test |
| public void testUpdateContent() throws Exception { |
| Path file = fileSystem.getPath("/test.txt"); |
| |
| clock.advanceMillis(1000); |
| |
| byte[] content = new byte[] { 'a', 'b', 'c', 23, 42 }; |
| FileSystemUtils.maybeUpdateContent(file, content); |
| byte[] actual = FileSystemUtils.readContent(file); |
| assertThat(actual).isEqualTo(content); |
| FileStatus stat = file.stat(); |
| assertThat(stat.getLastChangeTime()).isEqualTo(1000); |
| assertThat(stat.getLastModifiedTime()).isEqualTo(1000); |
| |
| clock.advanceMillis(1000); |
| |
| // Update with same contents; should not write anything. |
| FileSystemUtils.maybeUpdateContent(file, content); |
| assertThat(actual).isEqualTo(content); |
| stat = file.stat(); |
| assertThat(stat.getLastChangeTime()).isEqualTo(1000); |
| assertThat(stat.getLastModifiedTime()).isEqualTo(1000); |
| |
| clock.advanceMillis(1000); |
| |
| // Update with different contents; file should be rewritten. |
| content[0] = 'b'; |
| file.chmod(0400); // Protect the file to ensure we can rewrite it. |
| FileSystemUtils.maybeUpdateContent(file, content); |
| actual = FileSystemUtils.readContent(file); |
| assertThat(actual).isEqualTo(content); |
| stat = file.stat(); |
| assertThat(stat.getLastChangeTime()).isEqualTo(3000); |
| assertThat(stat.getLastModifiedTime()).isEqualTo(3000); |
| } |
| |
| @Test |
| public void testGetFileSystem() throws Exception { |
| Path mountTable = fileSystem.getPath("/proc/mounts"); |
| FileSystemUtils.writeIsoLatin1(mountTable, |
| "/dev/sda1 / ext2 blah 0 0", |
| "/dev/mapper/_dev_sda6 /usr/local/google ext3 blah 0 0", |
| "devshm /dev/shm tmpfs blah 0 0", |
| "/dev/fuse /fuse/mnt fuse blah 0 0", |
| "mtvhome22.nfs:/vol/mtvhome22/johndoe /home/johndoe nfs blah 0 0", |
| "/dev/foo /foo dummy_foo blah 0 0", |
| "/dev/foobar /foobar dummy_foobar blah 0 0", |
| "proc proc proc rw,noexec,nosuid,nodev 0 0"); |
| Path path = fileSystem.getPath("/usr/local/google/_blaze"); |
| FileSystemUtils.createDirectoryAndParents(path); |
| assertThat(FileSystemUtils.getFileSystem(path)).isEqualTo("ext3"); |
| |
| // Should match the root "/" |
| path = fileSystem.getPath("/usr/local/tmp"); |
| FileSystemUtils.createDirectoryAndParents(path); |
| assertThat(FileSystemUtils.getFileSystem(path)).isEqualTo("ext2"); |
| |
| // Make sure we don't consider /foobar matches /foo |
| path = fileSystem.getPath("/foo"); |
| FileSystemUtils.createDirectoryAndParents(path); |
| assertThat(FileSystemUtils.getFileSystem(path)).isEqualTo("dummy_foo"); |
| path = fileSystem.getPath("/foobar"); |
| FileSystemUtils.createDirectoryAndParents(path); |
| assertThat(FileSystemUtils.getFileSystem(path)).isEqualTo("dummy_foobar"); |
| |
| path = fileSystem.getPath("/dev/shm/blaze"); |
| FileSystemUtils.createDirectoryAndParents(path); |
| assertThat(FileSystemUtils.getFileSystem(path)).isEqualTo("tmpfs"); |
| |
| Path fusePath = fileSystem.getPath("/fuse/mnt/tmp"); |
| FileSystemUtils.createDirectoryAndParents(fusePath); |
| assertThat(FileSystemUtils.getFileSystem(fusePath)).isEqualTo("fuse"); |
| |
| // Create a symlink and make sure it gives the file system of the symlink target. |
| path = fileSystem.getPath("/usr/local/google/_blaze/out"); |
| path.createSymbolicLink(fusePath); |
| assertThat(FileSystemUtils.getFileSystem(path)).isEqualTo("fuse"); |
| |
| // Non existent path should return "unknown" |
| path = fileSystem.getPath("/does/not/exist"); |
| assertThat(FileSystemUtils.getFileSystem(path)).isEqualTo("unknown"); |
| } |
| |
| @Test |
| public void testStartsWithAnySuccess() throws Exception { |
| PathFragment a = PathFragment.create("a"); |
| assertThat( |
| FileSystemUtils.startsWithAny( |
| a, Arrays.asList(PathFragment.create("b"), PathFragment.create("a")))) |
| .isTrue(); |
| } |
| |
| @Test |
| public void testStartsWithAnyNotFound() throws Exception { |
| PathFragment a = PathFragment.create("a"); |
| assertThat( |
| FileSystemUtils.startsWithAny( |
| a, Arrays.asList(PathFragment.create("b"), PathFragment.create("c")))) |
| .isFalse(); |
| } |
| |
| @Test |
| public void testIterateLines() throws Exception { |
| Path file = fileSystem.getPath("/test.txt"); |
| FileSystemUtils.writeContent(file, ISO_8859_1, "a\nb"); |
| assertThat(Lists.newArrayList(FileSystemUtils.iterateLinesAsLatin1(file))) |
| .isEqualTo(Arrays.asList("a", "b")); |
| |
| FileSystemUtils.writeContent(file, ISO_8859_1, "a\rb"); |
| assertThat(Lists.newArrayList(FileSystemUtils.iterateLinesAsLatin1(file))) |
| .isEqualTo(Arrays.asList("a", "b")); |
| |
| FileSystemUtils.writeContent(file, ISO_8859_1, "a\r\nb"); |
| assertThat(Lists.newArrayList(FileSystemUtils.iterateLinesAsLatin1(file))) |
| .isEqualTo(Arrays.asList("a", "b")); |
| } |
| |
| @Test |
| public void testEnsureSymbolicLinkDoesNotMakeUnnecessaryChanges() throws Exception { |
| PathFragment target = PathFragment.create("/b"); |
| Path file = fileSystem.getPath("/a"); |
| file.createSymbolicLink(target); |
| long prevTimeMillis = clock.currentTimeMillis(); |
| clock.advanceMillis(1000); |
| FileSystemUtils.ensureSymbolicLink(file, target); |
| long timestamp = file.getLastModifiedTime(Symlinks.NOFOLLOW); |
| assertThat(timestamp).isEqualTo(prevTimeMillis); |
| } |
| |
| @Test |
| public void testCreateHardLinkForFile_Success() throws Exception { |
| |
| /* Original file exists and link file does not exist */ |
| Path originalPath = workingDir.getRelative("original"); |
| Path linkPath = workingDir.getRelative("link"); |
| FileSystemUtils.createEmptyFile(originalPath); |
| FileSystemUtils.createHardLink(linkPath, originalPath); |
| assertThat(originalPath.exists()).isTrue(); |
| assertThat(linkPath.exists()).isTrue(); |
| assertThat(fileSystem.stat(linkPath, false).getNodeId()) |
| .isEqualTo(fileSystem.stat(originalPath, false).getNodeId()); |
| } |
| |
| @Test |
| public void testCreateHardLinkForEmptyDirectory_Success() throws Exception { |
| |
| Path originalDir = workingDir.getRelative("originalDir"); |
| Path linkPath = workingDir.getRelative("link"); |
| |
| FileSystemUtils.createDirectoryAndParents(originalDir); |
| |
| /* Original directory is empty, no link to be created. */ |
| FileSystemUtils.createHardLink(linkPath, originalDir); |
| assertThat(linkPath.exists()).isFalse(); |
| } |
| |
| @Test |
| public void testCreateHardLinkForNonEmptyDirectory_Success() throws Exception { |
| |
| /* Test when original path is a directory */ |
| Path originalDir = workingDir.getRelative("originalDir"); |
| Path linkPath = workingDir.getRelative("link"); |
| Path originalPath1 = originalDir.getRelative("original1"); |
| Path originalPath2 = originalDir.getRelative("original2"); |
| Path originalPath3 = originalDir.getRelative("original3"); |
| Path linkPath1 = linkPath.getRelative("original1"); |
| Path linkPath2 = linkPath.getRelative("original2"); |
| Path linkPath3 = linkPath.getRelative("original3"); |
| |
| FileSystemUtils.createDirectoryAndParents(originalDir); |
| FileSystemUtils.createEmptyFile(originalPath1); |
| FileSystemUtils.createEmptyFile(originalPath2); |
| FileSystemUtils.createEmptyFile(originalPath3); |
| |
| /* Three link files created under linkPath */ |
| FileSystemUtils.createHardLink(linkPath, originalDir); |
| assertThat(linkPath.exists()).isTrue(); |
| assertThat(linkPath1.exists()).isTrue(); |
| assertThat(linkPath2.exists()).isTrue(); |
| assertThat(linkPath3.exists()).isTrue(); |
| assertThat(fileSystem.stat(linkPath1, false).getNodeId()) |
| .isEqualTo(fileSystem.stat(originalPath1, false).getNodeId()); |
| assertThat(fileSystem.stat(linkPath2, false).getNodeId()) |
| .isEqualTo(fileSystem.stat(originalPath2, false).getNodeId()); |
| assertThat(fileSystem.stat(linkPath3, false).getNodeId()) |
| .isEqualTo(fileSystem.stat(originalPath3, false).getNodeId()); |
| } |
| } |