diff options
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java')
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java | 1356 |
1 files changed, 1356 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java new file mode 100644 index 0000000000..b6e88d8976 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java @@ -0,0 +1,1356 @@ +// Copyright 2014 Google Inc. 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.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.common.base.Preconditions; +import com.google.common.io.BaseEncoding; +import com.google.devtools.build.lib.testutil.MoreAsserts; +import com.google.devtools.build.lib.testutil.TestUtils; +import com.google.devtools.build.lib.util.Fingerprint; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 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 FileSystemTest { + + private long savedTime; + protected FileSystem testFS; + protected boolean supportsSymlinks; + protected Path workingDir; + + // Some useful examples of various kinds of files (mnemonic: "x" = "eXample") + protected Path xNothing; + protected Path xFile; + protected Path xNonEmptyDirectory; + protected Path xNonEmptyDirectoryFoo; + protected Path xEmptyDirectory; + + @Before + public void setUp() throws Exception { + testFS = getFreshFileSystem(); + workingDir = testFS.getPath(getTestTmpDir()); + cleanUpWorkingDirectory(workingDir); + supportsSymlinks = testFS.supportsSymbolicLinks(); + + // % ls -lR + // -rw-rw-r-- xFile + // drwxrwxr-x xNonEmptyDirectory + // -rw-rw-r-- xNonEmptyDirectory/foo + // drwxrwxr-x xEmptyDirectory + + xNothing = absolutize("xNothing"); + xFile = absolutize("xFile"); + xNonEmptyDirectory = absolutize("xNonEmptyDirectory"); + xNonEmptyDirectoryFoo = xNonEmptyDirectory.getChild("foo"); + xEmptyDirectory = absolutize("xEmptyDirectory"); + + FileSystemUtils.createEmptyFile(xFile); + xNonEmptyDirectory.createDirectory(); + FileSystemUtils.createEmptyFile(xNonEmptyDirectoryFoo); + xEmptyDirectory.createDirectory(); + } + + @After + public void tearDown() throws Exception { + destroyFileSystem(testFS); + } + + /** + * Returns an instance of the file system to test. + */ + protected abstract FileSystem getFreshFileSystem() throws IOException; + + protected boolean isSymbolicLink(File file) { + return com.google.devtools.build.lib.unix.FilesystemUtils.isSymbolicLink(file); + } + + protected void setWritable(File file) throws IOException { + com.google.devtools.build.lib.unix.FilesystemUtils.setWritable(file); + } + + protected void setExecutable(File file) throws IOException { + com.google.devtools.build.lib.unix.FilesystemUtils.setExecutable(file); + } + + private static final Pattern STAT_SUBDIR_ERROR = Pattern.compile("(.*) \\(Not a directory\\)"); + + // Test that file is not present, using statIfFound. Base implementation throws an exception, but + // subclasses may override statIfFound to return null, in which case their tests should override + // this method. + @SuppressWarnings("unused") // Subclasses may throw. + protected void expectNotFound(Path path) throws IOException { + try { + assertNull(path.statIfFound()); + } catch (IOException e) { + // May be because of a non-directory path component. Parse exception to check this. + Matcher matcher = STAT_SUBDIR_ERROR.matcher(e.getMessage()); + if (!matcher.matches() || !path.getPathString().startsWith(matcher.group(1))) { + // Throw if this doesn't match what an ENOTDIR error looks like. + throw e; + } + } + } + + /** + * Removes all stuff from the test filesystem. + */ + protected void destroyFileSystem(FileSystem fileSystem) throws IOException { + Preconditions.checkArgument(fileSystem.equals(workingDir.getFileSystem())); + cleanUpWorkingDirectory(workingDir); + } + + /** + * Cleans up the working directory by removing everything. + */ + protected void cleanUpWorkingDirectory(Path workingPath) + throws IOException { + if (workingPath.exists()) { + removeEntireDirectory(workingPath.getPathFile()); // uses java.io.File! + } + FileSystemUtils.createDirectoryAndParents(workingPath); + } + + /** + * This function removes an entire directory and all of its contents. + * Much like rm -rf directoryToRemove + */ + protected void removeEntireDirectory(File directoryToRemove) + throws IOException { + // make sure that we do not remove anything outside the test directory + Path testDirPath = testFS.getPath(getTestTmpDir()); + if (!testFS.getPath(directoryToRemove.getAbsolutePath()).startsWith(testDirPath)) { + throw new IOException("trying to remove files outside of the testdata directory"); + } + // Some tests set the directories read-only and/or non-executable, so + // override that: + setWritable(directoryToRemove); + setExecutable(directoryToRemove); + + File[] files = directoryToRemove.listFiles(); + if (files != null) { + for (File currentFile : files) { + boolean isSymbolicLink = isSymbolicLink(currentFile); + if (!isSymbolicLink && currentFile.isDirectory()) { + removeEntireDirectory(currentFile); + } else { + if (!isSymbolicLink) { + setWritable(currentFile); + } + if (!currentFile.delete()) { + throw new IOException("Failed to delete '" + currentFile + "'"); + } + } + } + } + if (!directoryToRemove.delete()) { + throw new IOException("Failed to delete '" + directoryToRemove + "'"); + } + } + + /** + * Returns the directory to use as the FileSystem's working directory. + * Canonicalized to make tests hermetic against symbolic links in TEST_TMPDIR. + */ + protected final String getTestTmpDir() throws IOException { + return new File(TestUtils.tmpDir()).getCanonicalPath() + "/testdir"; + } + + /** + * Indirection to create links so we can test FileSystems that do not support + * link creation. For example, JavaFileSystemTest overrides this method + * and creates the link with an alternate FileSystem. + */ + protected void createSymbolicLink(Path link, Path target) throws IOException { + createSymbolicLink(link, target.asFragment()); + } + + /** + * Indirection to create links so we can test FileSystems that do not support + * link creation. For example, JavaFileSystemTest overrides this method + * and creates the link with an alternate FileSystem. + */ + protected void createSymbolicLink(Path link, PathFragment target) throws IOException { + link.createSymbolicLink(target); + } + + /** + * Indirection to setReadOnly(false) on FileSystems that do not + * support setReadOnly(false). For example, JavaFileSystemTest overrides this + * method and makes the Path writable with an alternate FileSystem. + */ + protected void makeWritable(Path target) throws IOException { + target.setWritable(true); + } + + /** + * Indirection to {@link Path#setExecutable(boolean)} on FileSystems that do + * not support setExecutable. For example, JavaFileSystemTest overrides this + * method and makes the Path executable with an alternate FileSystem. + */ + protected void setExecutable(Path target, boolean mode) throws IOException { + target.setExecutable(mode); + } + + // TODO(bazel-team): (2011) Put in a setLastModifiedTime into the various objects + // and clobber the current time of the object we're currently handling. + // Otherwise testing the thing might get a little hard, depending on the clock. + void storeReferenceTime(long timeToMark) { + savedTime = timeToMark; + } + + boolean isLaterThanreferenceTime(long testTime) { + return (savedTime <= testTime); + } + + Path getTestFile() throws IOException { + Path tempPath = absolutize("test-file"); + FileSystemUtils.createEmptyFile(tempPath); + return tempPath; + } + + protected Path absolutize(String relativePathName) { + return workingDir.getRelative(relativePathName); + } + + // Here the tests begin. + + @Test + public void testIsFileForNonexistingPath() { + Path nonExistingPath = testFS.getPath("/something/strange"); + assertFalse(nonExistingPath.isFile()); + } + + @Test + public void testIsDirectoryForNonexistingPath() { + Path nonExistingPath = testFS.getPath("/something/strange"); + assertFalse(nonExistingPath.isDirectory()); + } + + @Test + public void testIsLinkForNonexistingPath() { + Path nonExistingPath = testFS.getPath("/something/strange"); + assertFalse(nonExistingPath.isSymbolicLink()); + } + + @Test + public void testExistsForNonexistingPath() throws Exception { + Path nonExistingPath = testFS.getPath("/something/strange"); + assertFalse(nonExistingPath.exists()); + expectNotFound(nonExistingPath); + } + + @Test + public void testBadPermissionsThrowsExceptionOnStatIfFound() throws Exception { + Path inaccessible = absolutize("inaccessible"); + inaccessible.createDirectory(); + Path child = inaccessible.getChild("child"); + FileSystemUtils.createEmptyFile(child); + inaccessible.setExecutable(false); + assertFalse(child.exists()); + try { + child.statIfFound(); + fail(); + } catch (IOException expected) { + // Expected. + } + } + + @Test + public void testStatIfFoundReturnsNullForChildOfNonDir() throws Exception { + Path foo = absolutize("foo"); + foo.createDirectory(); + Path nonDir = foo.getRelative("bar"); + FileSystemUtils.createEmptyFile(nonDir); + assertNull(nonDir.getRelative("file").statIfFound()); + } + + // The following tests check the handling of the current working directory. + @Test + public void testCreatePathRelativeToWorkingDirectory() { + Path relativeCreatedPath = absolutize("some-file"); + Path expectedResult = workingDir.getRelative(new PathFragment("some-file")); + + assertEquals(expectedResult, relativeCreatedPath); + } + + // The following tests check the handling of the root directory + @Test + public void testRootIsDirectory() { + Path rootPath = testFS.getPath("/"); + assertTrue(rootPath.isDirectory()); + } + + @Test + public void testRootHasNoParent() { + Path rootPath = testFS.getPath("/"); + assertNull(rootPath.getParentDirectory()); + } + + // The following functions test the creation of files/links/directories. + @Test + public void testFileExists() throws Exception { + Path someFile = absolutize("some-file"); + FileSystemUtils.createEmptyFile(someFile); + assertTrue(someFile.exists()); + assertNotNull(someFile.statIfFound()); + } + + @Test + public void testFileIsFile() throws Exception { + Path someFile = absolutize("some-file"); + FileSystemUtils.createEmptyFile(someFile); + assertTrue(someFile.isFile()); + } + + @Test + public void testFileIsNotDirectory() throws Exception { + Path someFile = absolutize("some-file"); + FileSystemUtils.createEmptyFile(someFile); + assertFalse(someFile.isDirectory()); + } + + @Test + public void testFileIsNotSymbolicLink() throws Exception { + Path someFile = absolutize("some-file"); + FileSystemUtils.createEmptyFile(someFile); + assertFalse(someFile.isSymbolicLink()); + } + + @Test + public void testDirectoryExists() throws Exception { + Path someDirectory = absolutize("some-dir"); + someDirectory.createDirectory(); + assertTrue(someDirectory.exists()); + assertNotNull(someDirectory.statIfFound()); + } + + @Test + public void testDirectoryIsDirectory() throws Exception { + Path someDirectory = absolutize("some-dir"); + someDirectory.createDirectory(); + assertTrue(someDirectory.isDirectory()); + } + + @Test + public void testDirectoryIsNotFile() throws Exception { + Path someDirectory = absolutize("some-dir"); + someDirectory.createDirectory(); + assertFalse(someDirectory.isFile()); + } + + @Test + public void testDirectoryIsNotSymbolicLink() throws Exception { + Path someDirectory = absolutize("some-dir"); + someDirectory.createDirectory(); + assertFalse(someDirectory.isSymbolicLink()); + } + + @Test + public void testSymbolicFileLinkExists() throws Exception { + if (supportsSymlinks) { + Path someLink = absolutize("some-link"); + someLink.createSymbolicLink(xFile); + assertTrue(someLink.exists()); + assertNotNull(someLink.statIfFound()); + } + } + + @Test + public void testSymbolicFileLinkIsSymbolicLink() throws Exception { + if (supportsSymlinks) { + Path someLink = absolutize("some-link"); + someLink.createSymbolicLink(xFile); + assertTrue(someLink.isSymbolicLink()); + } + } + + @Test + public void testSymbolicFileLinkIsFile() throws Exception { + if (supportsSymlinks) { + Path someLink = absolutize("some-link"); + someLink.createSymbolicLink(xFile); + assertTrue(someLink.isFile()); + } + } + + @Test + public void testSymbolicFileLinkIsNotDirectory() throws Exception { + if (supportsSymlinks) { + Path someLink = absolutize("some-link"); + someLink.createSymbolicLink(xFile); + assertFalse(someLink.isDirectory()); + } + } + + @Test + public void testSymbolicDirLinkExists() throws Exception { + if (supportsSymlinks) { + Path someLink = absolutize("some-link"); + someLink.createSymbolicLink(xEmptyDirectory); + assertTrue(someLink.exists()); + assertNotNull(someLink.statIfFound()); + } + } + + @Test + public void testSymbolicDirLinkIsSymbolicLink() throws Exception { + if (supportsSymlinks) { + Path someLink = absolutize("some-link"); + someLink.createSymbolicLink(xEmptyDirectory); + assertTrue(someLink.isSymbolicLink()); + } + } + + @Test + public void testSymbolicDirLinkIsDirectory() throws Exception { + if (supportsSymlinks) { + Path someLink = absolutize("some-link"); + someLink.createSymbolicLink(xEmptyDirectory); + assertTrue(someLink.isDirectory()); + } + } + + @Test + public void testSymbolicDirLinkIsNotFile() throws Exception { + if (supportsSymlinks) { + Path someLink = absolutize("some-link"); + someLink.createSymbolicLink(xEmptyDirectory); + assertFalse(someLink.isFile()); + } + } + + @Test + public void testChildOfNonDirectory() throws Exception { + Path somePath = absolutize("file-name"); + FileSystemUtils.createEmptyFile(somePath); + Path childOfNonDir = somePath.getChild("child"); + assertFalse(childOfNonDir.exists()); + expectNotFound(childOfNonDir); + } + + @Test + public void testCreateDirectoryIsEmpty() throws Exception { + Path newPath = xEmptyDirectory.getChild("new-dir"); + newPath.createDirectory(); + assertEquals(newPath.getDirectoryEntries().size(), 0); + } + + @Test + public void testCreateDirectoryIsOnlyChildInParent() throws Exception { + Path newPath = xEmptyDirectory.getChild("new-dir"); + newPath.createDirectory(); + assertEquals(1, newPath.getParentDirectory().getDirectoryEntries().size()); + assertThat(newPath.getParentDirectory().getDirectoryEntries()).containsExactly(newPath); + } + + @Test + public void testCreateDirectories() throws Exception { + Path newPath = absolutize("new-dir/sub/directory"); + assertTrue(FileSystemUtils.createDirectoryAndParents(newPath)); + } + + @Test + public void testCreateDirectoriesIsDirectory() throws Exception { + Path newPath = absolutize("new-dir/sub/directory"); + FileSystemUtils.createDirectoryAndParents(newPath); + assertTrue(newPath.isDirectory()); + } + + @Test + public void testCreateDirectoriesIsNotFile() throws Exception { + Path newPath = absolutize("new-dir/sub/directory"); + FileSystemUtils.createDirectoryAndParents(newPath); + assertFalse(newPath.isFile()); + } + + @Test + public void testCreateDirectoriesIsNotSymbolicLink() throws Exception { + Path newPath = absolutize("new-dir/sub/directory"); + FileSystemUtils.createDirectoryAndParents(newPath); + assertFalse(newPath.isSymbolicLink()); + } + + @Test + public void testCreateDirectoriesIsEmpty() throws Exception { + Path newPath = absolutize("new-dir/sub/directory"); + FileSystemUtils.createDirectoryAndParents(newPath); + assertEquals(newPath.getDirectoryEntries().size(), 0); + } + + @Test + public void testCreateDirectoriesIsOnlyChildInParent() throws Exception { + Path newPath = absolutize("new-dir/sub/directory"); + FileSystemUtils.createDirectoryAndParents(newPath); + assertEquals(1, newPath.getParentDirectory().getDirectoryEntries().size()); + assertThat(newPath.getParentDirectory().getDirectoryEntries()).containsExactly(newPath); + } + + @Test + public void testCreateEmptyFileIsEmpty() throws Exception { + Path newPath = xEmptyDirectory.getChild("new-file"); + FileSystemUtils.createEmptyFile(newPath); + + assertEquals(newPath.getFileSize(), 0); + } + + @Test + public void testCreateFileIsOnlyChildInParent() throws Exception { + Path newPath = xEmptyDirectory.getChild("new-file"); + FileSystemUtils.createEmptyFile(newPath); + assertEquals(1, newPath.getParentDirectory().getDirectoryEntries().size()); + assertThat(newPath.getParentDirectory().getDirectoryEntries()).containsExactly(newPath); + } + + // The following functions test the behavior if errors occur during the + // creation of files/links/directories. + @Test + public void testCreateDirectoryWhereDirectoryAlreadyExists() throws Exception { + assertFalse(xEmptyDirectory.createDirectory()); + } + + @Test + public void testCreateDirectoryWhereFileAlreadyExists() { + try { + xFile.createDirectory(); + fail(); + } catch (IOException e) { + assertEquals(xFile + " (File exists)", e.getMessage()); + } + } + + @Test + public void testCannotCreateDirectoryWithoutExistingParent() throws Exception { + Path newPath = testFS.getPath("/deep/new-dir"); + try { + newPath.createDirectory(); + fail(); + } catch (FileNotFoundException e) { + MoreAsserts.assertEndsWith(" (No such file or directory)", e.getMessage()); + } + } + + @Test + public void testCannotCreateDirectoryWithReadOnlyParent() throws Exception { + xEmptyDirectory.setWritable(false); + Path xChildOfReadonlyDir = xEmptyDirectory.getChild("x"); + try { + xChildOfReadonlyDir.createDirectory(); + fail(); + } catch (IOException e) { + assertEquals(xChildOfReadonlyDir + " (Permission denied)", e.getMessage()); + } + } + + @Test + public void testCannotCreateFileWithoutExistingParent() throws Exception { + Path newPath = testFS.getPath("/non-existing-dir/new-file"); + try { + FileSystemUtils.createEmptyFile(newPath); + fail(); + } catch (FileNotFoundException e) { + MoreAsserts.assertEndsWith(" (No such file or directory)", e.getMessage()); + } + } + + @Test + public void testCannotCreateFileWithReadOnlyParent() throws Exception { + xEmptyDirectory.setWritable(false); + Path xChildOfReadonlyDir = xEmptyDirectory.getChild("x"); + try { + FileSystemUtils.createEmptyFile(xChildOfReadonlyDir); + fail(); + } catch (IOException e) { + assertEquals(xChildOfReadonlyDir + " (Permission denied)", e.getMessage()); + } + } + + @Test + public void testCannotCreateFileWithinFile() throws Exception { + Path newFilePath = absolutize("some-file"); + FileSystemUtils.createEmptyFile(newFilePath); + Path wrongPath = absolutize("some-file/new-file"); + try { + FileSystemUtils.createEmptyFile(wrongPath); + fail(); + } catch (IOException e) { + MoreAsserts.assertEndsWith(" (Not a directory)", e.getMessage()); + } + } + + @Test + public void testCannotCreateDirectoryWithinFile() throws Exception { + Path newFilePath = absolutize("some-file"); + FileSystemUtils.createEmptyFile(newFilePath); + Path wrongPath = absolutize("some-file/new-file"); + try { + wrongPath.createDirectory(); + fail(); + } catch (IOException e) { + MoreAsserts.assertEndsWith(" (Not a directory)", e.getMessage()); + } + } + + // Test directory contents + @Test + public void testCreateMultipleChildren() throws Exception { + Path theDirectory = absolutize("foo/"); + theDirectory.createDirectory(); + 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); + + assertThat(theDirectory.getDirectoryEntries()).containsExactly(newPath1, newPath2, newPath3); + } + + @Test + public void testGetDirectoryEntriesThrowsExceptionWhenRunOnFile() throws Exception { + try { + xFile.getDirectoryEntries(); + fail("No Exception thrown."); + } catch (IOException ex) { + if (ex instanceof FileNotFoundException) { + fail("The method should throw an object of class IOException."); + } + assertEquals(xFile + " (Not a directory)", ex.getMessage()); + } + } + + @Test + public void testGetDirectoryEntriesThrowsExceptionForNonexistingPath() { + Path somePath = testFS.getPath("/non-existing-path"); + try { + somePath.getDirectoryEntries(); + fail("FileNotFoundException not thrown."); + } catch (Exception x) { + assertEquals(somePath + " (No such file or directory)", x.getMessage()); + } + } + + // Test the removal of items + @Test + public void testDeleteDirectory() throws Exception { + assertTrue(xEmptyDirectory.delete()); + } + + @Test + public void testDeleteDirectoryIsNotDirectory() throws Exception { + xEmptyDirectory.delete(); + assertFalse(xEmptyDirectory.isDirectory()); + } + + @Test + public void testDeleteDirectoryParentSize() throws Exception { + int parentSize = workingDir.getDirectoryEntries().size(); + xEmptyDirectory.delete(); + assertEquals(workingDir.getDirectoryEntries().size(), parentSize - 1); + } + + @Test + public void testDeleteFile() throws Exception { + assertTrue(xFile.delete()); + } + + @Test + public void testDeleteFileIsNotFile() throws Exception { + xFile.delete(); + assertFalse(xEmptyDirectory.isFile()); + } + + @Test + public void testDeleteFileParentSize() throws Exception { + int parentSize = workingDir.getDirectoryEntries().size(); + xFile.delete(); + assertEquals(workingDir.getDirectoryEntries().size(), parentSize - 1); + } + + @Test + public void testDeleteRemovesCorrectFile() throws Exception { + Path newPath1 = xEmptyDirectory.getChild("new-file-1"); + Path newPath2 = xEmptyDirectory.getChild("new-file-2"); + Path newPath3 = xEmptyDirectory.getChild("new-file-3"); + + FileSystemUtils.createEmptyFile(newPath1); + FileSystemUtils.createEmptyFile(newPath2); + FileSystemUtils.createEmptyFile(newPath3); + + assertTrue(newPath2.delete()); + assertThat(xEmptyDirectory.getDirectoryEntries()).containsExactly(newPath1, newPath3); + } + + @Test + public void testDeleteNonExistingDir() throws Exception { + Path path = xEmptyDirectory.getRelative("non-existing-dir"); + assertFalse(path.delete()); + } + + @Test + public void testDeleteNotADirectoryPath() throws Exception { + Path path = xFile.getChild("new-file"); + assertFalse(path.delete()); + } + + // Here we test the situations where delete should throw exceptions. + @Test + public void testDeleteNonEmptyDirectoryThrowsException() throws Exception { + try { + xNonEmptyDirectory.delete(); + fail(); + } catch (IOException e) { + assertEquals(xNonEmptyDirectory + " (Directory not empty)", e.getMessage()); + } + } + + @Test + public void testDeleteNonEmptyDirectoryNotDeletedDirectory() throws Exception { + try { + xNonEmptyDirectory.delete(); + fail(); + } catch (IOException e) { + // Expected + } + + assertTrue(xNonEmptyDirectory.isDirectory()); + } + + @Test + public void testDeleteNonEmptyDirectoryNotDeletedFile() throws Exception { + try { + xNonEmptyDirectory.delete(); + fail(); + } catch (IOException e) { + // Expected + } + + assertTrue(xNonEmptyDirectoryFoo.isFile()); + } + + @Test + public void testCannotRemoveRoot() { + Path rootDirectory = testFS.getRootDirectory(); + try { + rootDirectory.delete(); + fail(); + } catch (IOException e) { + String msg = e.getMessage(); + assertTrue(String.format("got %s want EBUSY or ENOTEMPTY", msg), + msg.endsWith(" (Directory not empty)") + || msg.endsWith(" (Device or resource busy)") + || msg.endsWith(" (Is a directory)")); // Happens on OS X. + } + } + + // Test the date functions + @Test + public void testCreateFileChangesTimeOfDirectory() throws Exception { + storeReferenceTime(workingDir.getLastModifiedTime()); + Path newPath = absolutize("new-file"); + FileSystemUtils.createEmptyFile(newPath); + assertTrue(isLaterThanreferenceTime(workingDir.getLastModifiedTime())); + } + + @Test + public void testRemoveFileChangesTimeOfDirectory() throws Exception { + Path newPath = absolutize("new-file"); + FileSystemUtils.createEmptyFile(newPath); + storeReferenceTime(workingDir.getLastModifiedTime()); + newPath.delete(); + assertTrue(isLaterThanreferenceTime(workingDir.getLastModifiedTime())); + } + + // This test is a little bit strange, as we cannot test the progression + // of the time directly. As the Java time and the OS time are slightly different. + // Therefore, we first create an unrelated file to get a notion + // of the current OS time and use that as a baseline. + @Test + public void testCreateFileTimestamp() throws Exception { + Path syncFile = absolutize("sync-file"); + FileSystemUtils.createEmptyFile(syncFile); + + Path newFile = absolutize("new-file"); + storeReferenceTime(syncFile.getLastModifiedTime()); + FileSystemUtils.createEmptyFile(newFile); + assertTrue(isLaterThanreferenceTime(newFile.getLastModifiedTime())); + } + + @Test + public void testCreateDirectoryTimestamp() throws Exception { + Path syncFile = absolutize("sync-file"); + FileSystemUtils.createEmptyFile(syncFile); + + Path newPath = absolutize("new-dir"); + storeReferenceTime(syncFile.getLastModifiedTime()); + assertTrue(newPath.createDirectory()); + assertTrue(isLaterThanreferenceTime(newPath.getLastModifiedTime())); + } + + @Test + public void testWriteChangesModifiedTime() throws Exception { + storeReferenceTime(xFile.getLastModifiedTime()); + FileSystemUtils.writeContentAsLatin1(xFile, "abc19"); + assertTrue(isLaterThanreferenceTime(xFile.getLastModifiedTime())); + } + + @Test + public void testGetLastModifiedTimeThrowsExceptionForNonexistingPath() throws Exception { + Path newPath = testFS.getPath("/non-existing-dir"); + try { + newPath.getLastModifiedTime(); + fail("FileNotFoundException not thrown!"); + } catch (FileNotFoundException x) { + assertEquals(newPath + " (No such file or directory)", x.getMessage()); + } + } + + // Test file size + @Test + public void testFileSizeThrowsExceptionForNonexistingPath() throws Exception { + Path newPath = testFS.getPath("/non-existing-file"); + try { + newPath.getFileSize(); + fail("FileNotFoundException not thrown."); + } catch (FileNotFoundException e) { + assertEquals(newPath + " (No such file or directory)", e.getMessage()); + } + } + + @Test + public void testFileSizeAfterWrite() throws Exception { + String testData = "abc19"; + + FileSystemUtils.writeContentAsLatin1(xFile, testData); + assertEquals(testData.length(), xFile.getFileSize()); + } + + // Testing the input/output routines + @Test + public void testFileWriteAndReadAsLatin1() throws Exception { + String testData = "abc19"; + + FileSystemUtils.writeContentAsLatin1(xFile, testData); + String resultData = new String(FileSystemUtils.readContentAsLatin1(xFile)); + + assertEquals(testData,resultData); + } + + @Test + public void testInputAndOutputStreamEOF() throws Exception { + OutputStream outStream = xFile.getOutputStream(); + outStream.write(1); + outStream.close(); + + InputStream inStream = xFile.getInputStream(); + inStream.read(); + assertEquals(-1, inStream.read()); + inStream.close(); + } + + @Test + public void testInputAndOutputStream() throws Exception { + OutputStream outStream = xFile.getOutputStream(); + for (int i = 33; i < 126; i++) { + outStream.write(i); + } + outStream.close(); + + InputStream inStream = xFile.getInputStream(); + for (int i = 33; i < 126; i++) { + int readValue = inStream.read(); + assertEquals(i,readValue); + } + inStream.close(); + } + + @Test + public void testInputAndOutputStreamAppend() throws Exception { + OutputStream outStream = xFile.getOutputStream(); + for (int i = 33; i < 126; i++) { + outStream.write(i); + } + outStream.close(); + + OutputStream appendOut = xFile.getOutputStream(true); + for (int i = 126; i < 155; i++) { + appendOut.write(i); + } + appendOut.close(); + + InputStream inStream = xFile.getInputStream(); + for (int i = 33; i < 155; i++) { + int readValue = inStream.read(); + assertEquals(i,readValue); + } + inStream.close(); + } + + @Test + public void testInputAndOutputStreamNoAppend() throws Exception { + OutputStream outStream = xFile.getOutputStream(); + outStream.write(1); + outStream.close(); + + OutputStream noAppendOut = xFile.getOutputStream(false); + noAppendOut.close(); + + InputStream inStream = xFile.getInputStream(); + assertEquals(-1, inStream.read()); + inStream.close(); + } + + @Test + public void testGetOutputStreamCreatesFile() throws Exception { + Path newFile = absolutize("does_not_exist_yet.txt"); + + OutputStream out = newFile.getOutputStream(); + out.write(42); + out.close(); + + assertTrue(newFile.isFile()); + } + + @Test + public void testInpuStreamThrowExceptionOnDirectory() throws Exception { + try { + xEmptyDirectory.getOutputStream(); + fail("The Exception was not thrown!"); + } catch (IOException ex) { + assertEquals(xEmptyDirectory + " (Is a directory)", ex.getMessage()); + } + } + + @Test + public void testOutputStreamThrowExceptionOnDirectory() throws Exception { + try { + xEmptyDirectory.getInputStream(); + fail("The Exception was not thrown!"); + } catch (IOException ex) { + assertEquals(xEmptyDirectory + " (Is a directory)", ex.getMessage()); + } + } + + // Test renaming + @Test + public void testCanRenameToUnusedName() throws Exception { + xFile.renameTo(xNothing); + assertFalse(xFile.exists()); + assertTrue(xNothing.isFile()); + } + + @Test + public void testCanRenameFileToExistingFile() throws Exception { + Path otherFile = absolutize("otherFile"); + FileSystemUtils.createEmptyFile(otherFile); + xFile.renameTo(otherFile); // succeeds + assertFalse(xFile.exists()); + assertTrue(otherFile.isFile()); + } + + @Test + public void testCanRenameDirToExistingEmptyDir() throws Exception { + xNonEmptyDirectory.renameTo(xEmptyDirectory); // succeeds + assertFalse(xNonEmptyDirectory.exists()); + assertTrue(xEmptyDirectory.isDirectory()); + assertFalse(xEmptyDirectory.getDirectoryEntries().isEmpty()); + } + + @Test + public void testCantRenameDirToExistingNonEmptyDir() throws Exception { + try { + xEmptyDirectory.renameTo(xNonEmptyDirectory); + fail(); + } catch (IOException e) { + MoreAsserts.assertEndsWith(" (Directory not empty)", e.getMessage()); + } + } + + @Test + public void testCantRenameDirToExistingNonEmptyDirNothingChanged() throws Exception { + try { + xEmptyDirectory.renameTo(xNonEmptyDirectory); + fail(); + } catch (IOException e) { + // Expected + } + + assertTrue(xNonEmptyDirectory.isDirectory()); + assertTrue(xEmptyDirectory.isDirectory()); + assertTrue(xEmptyDirectory.getDirectoryEntries().isEmpty()); + assertFalse(xNonEmptyDirectory.getDirectoryEntries().isEmpty()); + } + + @Test + public void testCantRenameDirToExistingFile() { + try { + xEmptyDirectory.renameTo(xFile); + fail(); + } catch (IOException e) { + assertEquals(xEmptyDirectory + " -> " + xFile + " (Not a directory)", e.getMessage()); + } + } + + @Test + public void testCantRenameDirToExistingFileNothingChanged() { + try { + xEmptyDirectory.renameTo(xFile); + fail(); + } catch (IOException e) { + // Expected + } + + assertTrue(xEmptyDirectory.isDirectory()); + assertTrue(xFile.isFile()); + } + + @Test + public void testCantRenameFileToExistingDir() { + try { + xFile.renameTo(xEmptyDirectory); + fail(); + } catch (IOException e) { + assertEquals(xFile + " -> " + xEmptyDirectory + " (Is a directory)", + e.getMessage()); + } + } + + @Test + public void testCantRenameFileToExistingDirNothingChanged() { + try { + xFile.renameTo(xEmptyDirectory); + fail(); + } catch (IOException e) { + // Expected + } + + assertTrue(xEmptyDirectory.isDirectory()); + assertTrue(xFile.isFile()); + } + + @Test + public void testMoveOnNonExistingFileThrowsException() throws Exception { + Path nonExistingPath = absolutize("non-existing"); + Path targetPath = absolutize("does-not-matter"); + try { + nonExistingPath.renameTo(targetPath); + fail(); + } catch (FileNotFoundException e) { + MoreAsserts.assertEndsWith(" (No such file or directory)", e.getMessage()); + } + } + + // Test the Paths + @Test + public void testGetPathOnlyAcceptsAbsolutePath() { + try { + testFS.getPath("not-absolute"); + fail("The expected Exception was not thrown."); + } catch (IllegalArgumentException ex) { + assertEquals("not-absolute (not an absolute path)", ex.getMessage()); + } + } + + @Test + public void testGetPathOnlyAcceptsAbsolutePathFragment() { + try { + testFS.getPath(new PathFragment("not-absolute")); + fail("The expected Exception was not thrown."); + } catch (IllegalArgumentException ex) { + assertEquals("not-absolute (not an absolute path)", ex.getMessage()); + } + } + + // Test the access permissions + @Test + public void testNewFilesAreWritable() throws Exception { + assertTrue(xFile.isWritable()); + } + + @Test + public void testNewFilesAreReadable() throws Exception { + assertTrue(xFile.isReadable()); + } + + @Test + public void testNewDirsAreWritable() throws Exception { + assertTrue(xEmptyDirectory.isWritable()); + } + + @Test + public void testNewDirsAreReadable() throws Exception { + assertTrue(xEmptyDirectory.isReadable()); + } + + @Test + public void testNewDirsAreExecutable() throws Exception { + assertTrue(xEmptyDirectory.isExecutable()); + } + + @Test + public void testCannotGetExecutableOnNonexistingFile() throws Exception { + try { + xNothing.isExecutable(); + fail("No exception thrown."); + } catch (FileNotFoundException ex) { + assertEquals(xNothing + " (No such file or directory)", ex.getMessage()); + } + } + + @Test + public void testCannotSetExecutableOnNonexistingFile() throws Exception { + try { + xNothing.setExecutable(true); + fail("No exception thrown."); + } catch (FileNotFoundException ex) { + assertEquals(xNothing + " (No such file or directory)", ex.getMessage()); + } + } + + @Test + public void testCannotGetWritableOnNonexistingFile() throws Exception { + try { + xNothing.isWritable(); + fail("No exception thrown."); + } catch (FileNotFoundException ex) { + assertEquals(xNothing + " (No such file or directory)", ex.getMessage()); + } + } + + @Test + public void testCannotSetWritableOnNonexistingFile() throws Exception { + try { + xNothing.setWritable(false); + fail("No exception thrown."); + } catch (FileNotFoundException ex) { + assertEquals(xNothing + " (No such file or directory)", ex.getMessage()); + } + } + + @Test + public void testSetReadableOnFile() throws Exception { + xFile.setReadable(false); + assertFalse(xFile.isReadable()); + xFile.setReadable(true); + assertTrue(xFile.isReadable()); + } + + @Test + public void testSetWritableOnFile() throws Exception { + xFile.setWritable(false); + assertFalse(xFile.isWritable()); + xFile.setWritable(true); + assertTrue(xFile.isWritable()); + } + + @Test + public void testSetExecutableOnFile() throws Exception { + xFile.setExecutable(true); + assertTrue(xFile.isExecutable()); + xFile.setExecutable(false); + assertFalse(xFile.isExecutable()); + } + + @Test + public void testSetExecutableOnDirectory() throws Exception { + setExecutable(xNonEmptyDirectory, false); + + try { + // We can't map names->inodes in a non-executable directory: + xNonEmptyDirectoryFoo.isWritable(); // i.e. stat + fail(); + } catch (IOException e) { + MoreAsserts.assertEndsWith(" (Permission denied)", e.getMessage()); + } + } + + @Test + public void testWritingToReadOnlyFileThrowsException() throws Exception { + xFile.setWritable(false); + try { + FileSystemUtils.writeContent(xFile, "hello, world!".getBytes()); + fail("No exception thrown."); + } catch (IOException e) { + assertEquals(xFile + " (Permission denied)", e.getMessage()); + } + } + + @Test + public void testReadingFromUnreadableFileThrowsException() throws Exception { + FileSystemUtils.writeContent(xFile, "hello, world!".getBytes()); + xFile.setReadable(false); + try { + FileSystemUtils.readContent(xFile); + fail("No exception thrown."); + } catch (IOException e) { + assertEquals(xFile + " (Permission denied)", e.getMessage()); + } + } + + @Test + public void testCannotCreateFileInReadOnlyDirectory() throws Exception { + Path xNonEmptyDirectoryBar = xNonEmptyDirectory.getChild("bar"); + xNonEmptyDirectory.setWritable(false); + + try { + FileSystemUtils.createEmptyFile(xNonEmptyDirectoryBar); + fail("No exception thrown."); + } catch (IOException e) { + assertEquals(xNonEmptyDirectoryBar + " (Permission denied)", e.getMessage()); + } + } + + @Test + public void testCannotCreateDirectoryInReadOnlyDirectory() throws Exception { + Path xNonEmptyDirectoryBar = xNonEmptyDirectory.getChild("bar"); + xNonEmptyDirectory.setWritable(false); + + try { + xNonEmptyDirectoryBar.createDirectory(); + fail("No exception thrown."); + } catch (IOException e) { + assertEquals(xNonEmptyDirectoryBar + " (Permission denied)", e.getMessage()); + } + } + + @Test + public void testCannotMoveIntoReadOnlyDirectory() throws Exception { + Path xNonEmptyDirectoryBar = xNonEmptyDirectory.getChild("bar"); + xNonEmptyDirectory.setWritable(false); + + try { + xFile.renameTo(xNonEmptyDirectoryBar); + fail("No exception thrown."); + } catch (IOException e) { + MoreAsserts.assertEndsWith(" (Permission denied)", e.getMessage()); + } + } + + @Test + public void testCannotMoveFromReadOnlyDirectory() throws Exception { + xNonEmptyDirectory.setWritable(false); + + try { + xNonEmptyDirectoryFoo.renameTo(xNothing); + fail("No exception thrown."); + } catch (IOException e) { + MoreAsserts.assertEndsWith(" (Permission denied)", e.getMessage()); + } + } + + @Test + public void testCannotDeleteInReadOnlyDirectory() throws Exception { + xNonEmptyDirectory.setWritable(false); + + try { + xNonEmptyDirectoryFoo.delete(); + fail("No exception thrown."); + } catch (IOException e) { + assertEquals(xNonEmptyDirectoryFoo + " (Permission denied)", e.getMessage()); + } + } + + @Test + public void testCannotCreatSymbolicLinkInReadOnlyDirectory() throws Exception { + Path xNonEmptyDirectoryBar = xNonEmptyDirectory.getChild("bar"); + xNonEmptyDirectory.setWritable(false); + + if (supportsSymlinks) { + try { + createSymbolicLink(xNonEmptyDirectoryBar, xNonEmptyDirectoryFoo); + fail("No exception thrown."); + } catch (IOException e) { + assertEquals(xNonEmptyDirectoryBar + " (Permission denied)", e.getMessage()); + } + } + } + + @Test + public void testGetMD5DigestForEmptyFile() throws Exception { + Fingerprint fp = new Fingerprint(); + fp.addBytes(new byte[0]); + assertEquals(BaseEncoding.base16().lowerCase().encode(xFile.getMD5Digest()), + fp.hexDigestAndReset()); + } + + @Test + public void testGetMD5Digest() throws Exception { + byte[] buffer = new byte[500000]; + for (int i = 0; i < buffer.length; ++i) { + buffer[i] = 1; + } + FileSystemUtils.writeContent(xFile, buffer); + Fingerprint fp = new Fingerprint(); + fp.addBytes(buffer); + assertEquals(BaseEncoding.base16().lowerCase().encode(xFile.getMD5Digest()), + fp.hexDigestAndReset()); + } + + @Test + public void testStatFailsFastOnNonExistingFiles() throws Exception { + try { + xNothing.stat(); + fail("Expected IOException"); + } catch(IOException e) { + // Do nothing. + } + } + + @Test + public void testStatNullableFailsFastOnNonExistingFiles() throws Exception { + assertNull(xNothing.statNullable()); + } + + @Test + public void testResolveSymlinks() throws Exception { + if (supportsSymlinks) { + createSymbolicLink(xNothing, xFile); + FileSystemUtils.createEmptyFile(xFile); + assertEquals(xFile.asFragment(), testFS.resolveOneLink(xNothing)); + assertEquals(xFile, xNothing.resolveSymbolicLinks()); + } + } + + @Test + public void testResolveNonSymlinks() throws Exception { + if (supportsSymlinks) { + assertEquals(null, testFS.resolveOneLink(xFile)); + assertEquals(xFile, xFile.resolveSymbolicLinks()); + } + } + +} |