diff --git a/main/commons-test/src/main/java/org/cryptomator/common/test/matcher/OptionalMatcher.java b/main/commons-test/src/main/java/org/cryptomator/common/test/matcher/OptionalMatcher.java new file mode 100644 index 000000000..0df2f5f88 --- /dev/null +++ b/main/commons-test/src/main/java/org/cryptomator/common/test/matcher/OptionalMatcher.java @@ -0,0 +1,40 @@ +package org.cryptomator.common.test.matcher; + +import java.util.Optional; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +public class OptionalMatcher { + + public static Matcher> presentOptionalWithValueThat(Matcher valueMatcher) { + return new TypeSafeDiagnosingMatcher>(Optional.class) { + @Override + public void describeTo(Description description) { + description // + .appendText("a present Optional with a value that") // + .appendDescriptionOf(valueMatcher); + } + + @Override + protected boolean matchesSafely(Optional item, Description mismatchDescription) { + if (item.isPresent()) { + if (valueMatcher.matches(item.get())) { + return true; + } else { + mismatchDescription.appendText("a present Optional with value that"); + valueMatcher.describeMismatch(item, mismatchDescription); + return false; + } + } else { + mismatchDescription.appendText("an empty Optional"); + return false; + } + + } + + }; + } + +} diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java index 9b1a1d00c..5403dc8af 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java @@ -2,8 +2,10 @@ package org.cryptomator.filesystem.nio; import static java.lang.String.format; +import java.io.IOException; import java.io.UncheckedIOException; import java.nio.ByteBuffer; +import java.nio.file.Files; import java.nio.file.Path; import java.time.Instant; import java.util.Optional; @@ -91,6 +93,11 @@ class NioFile extends NioNode implements File { @Override public void delete() throws UncheckedIOException { + try { + Files.delete(path); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } @Override diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFolder.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFolder.java index bf87d7319..868c468e8 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFolder.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFolder.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Instant; import java.util.Optional; import java.util.stream.Stream; @@ -68,6 +69,19 @@ class NioFolder extends NioNode implements Folder { } } + @Override + public Instant lastModified() throws UncheckedIOException { + if (Files.exists(path) && !Files.isDirectory(path)) { + throw new UncheckedIOException(new IOException(format("%s is a file", path))); + } + return super.lastModified(); + } + + @Override + public boolean exists() throws UncheckedIOException { + return Files.isDirectory(path); + } + @Override public void moveTo(Folder target) { if (belongsToSameFilesystem(target)) { @@ -94,6 +108,9 @@ class NioFolder extends NioNode implements Folder { @Override public void delete() { + if (!exists()) { + return; + } fileSystemVisitor() // .forEachFile(NioFolder::deleteFile) // .afterFolder(NioFolder::deleteEmptyFolder) // diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/FilesystemSetupUtils.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/FilesystemSetupUtils.java index a580fa952..b8f8c27d5 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/FilesystemSetupUtils.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/FilesystemSetupUtils.java @@ -6,6 +6,8 @@ import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.attribute.FileTime; +import java.time.Instant; import org.apache.commons.io.IOUtils; @@ -75,14 +77,24 @@ class FilesystemSetupUtils { public static class FolderEntry implements Entry { private Path relativePath; + private Instant lastModified; public FolderEntry(Path relativePath) { this.relativePath = relativePath; } + public FolderEntry withLastModified(Instant lastModified) { + this.lastModified = lastModified; + return this; + } + @Override public void create(Path root) throws IOException { - Files.createDirectories(root.resolve(relativePath)); + Path path = root.resolve(relativePath); + Files.createDirectories(path); + if (lastModified != null) { + Files.setLastModifiedTime(path, FileTime.from(lastModified)); + } } } diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderTest.java index efd96969f..c83fd0939 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderTest.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderTest.java @@ -2,13 +2,18 @@ package org.cryptomator.filesystem.nio; import static java.util.stream.Collectors.toList; import static org.cryptomator.common.test.matcher.ContainsMatcher.containsInAnyOrder; +import static org.cryptomator.common.test.matcher.OptionalMatcher.presentOptionalWithValueThat; import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.emptyFilesystem; import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.file; import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.folder; import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.testFilesystem; import static org.cryptomator.filesystem.nio.NioNodeMatcher.fileWithName; import static org.cryptomator.filesystem.nio.NioNodeMatcher.folderWithName; +import static org.cryptomator.filesystem.nio.PathMatcher.doesNotExist; +import static org.cryptomator.filesystem.nio.PathMatcher.isDirectory; +import static org.cryptomator.filesystem.nio.PathMatcher.isFile; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.Matchers.empty; import static org.junit.Assert.assertThat; @@ -16,7 +21,10 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Instant; +import org.cryptomator.filesystem.File; +import org.cryptomator.filesystem.FileSystem; import org.cryptomator.filesystem.Folder; import org.junit.Rule; import org.junit.Test; @@ -28,22 +36,62 @@ public class NioFolderTest { @Test public void testNameIsNameOfFolder() throws IOException { - final String folderName = "folderNameABC"; + final String folderName = "nameOfFolder"; NioFileSystem fileSystem = NioFileSystem.rootedAt(emptyFilesystem()); Folder folder = fileSystem.folder(folderName); assertThat(folder, folderWithName(folderName)); } + @Test + public void testCreateSucceedsIfFolderExists() throws IOException { + String folderName = "nameOfFolder"; + Path testFilesystemPath = testFilesystem( // + folder(folderName)); + NioFileSystem fileSystem = NioFileSystem.rootedAt(testFilesystemPath); + Folder existingFolder = fileSystem.folder(folderName); + + existingFolder.create(); + + assertThat(Files.isDirectory(testFilesystemPath.resolve(folderName)), is(true)); + } + + @Test + public void testCreateSucceedsIfFolderDoesNotExist() throws IOException { + String folderName = "nameOfFolder"; + Path testFilesystemPath = emptyFilesystem(); + NioFileSystem fileSystem = NioFileSystem.rootedAt(testFilesystemPath); + Folder nonExistingFolder = fileSystem.folder(folderName); + + nonExistingFolder.create(); + + assertThat(Files.isDirectory(testFilesystemPath.resolve(folderName)), is(true)); + } + @Test public void testCreateSucceedsIfParentIsMissing() throws IOException { + String parentFolderName = "nameOfParentFolder"; + String folderName = "nameOfFolder"; Path emptyFilesystemPath = emptyFilesystem(); NioFileSystem fileSystem = NioFileSystem.rootedAt(emptyFilesystemPath); - Folder folderWithNonExistingParent = fileSystem.folder("a").folder("b"); + Folder folderWithNonExistingParent = fileSystem.folder(parentFolderName).folder(folderName); folderWithNonExistingParent.create(); - assertThat(Files.isDirectory(emptyFilesystemPath.resolve("a/b")), is(true)); + assertThat(Files.isDirectory(emptyFilesystemPath.resolve(parentFolderName).resolve(folderName)), is(true)); + } + + @Test + public void testCreateWithFolderWhichIsAFileThrowsUncheckedIOExceptionWithAbsolutePathOfFolderInMessage() throws IOException { + String folderName = "nameOfFolder"; + Path testFilesystemPath = testFilesystem(file(folderName)); + NioFileSystem fileSystem = NioFileSystem.rootedAt(testFilesystemPath); + Folder folderWhichIsAFile = fileSystem.folder(folderName); + + thrown.expect(UncheckedIOException.class); + thrown.expectMessage(testFilesystemPath.resolve(folderName).toString()); + + folderWhichIsAFile.create(); } @Test @@ -55,12 +103,24 @@ public class NioFolderTest { @Test public void testChildrenOfNonExistingFolderThrowsUncheckedIOExceptionWithAbolutePathOfFolderInMessage() throws IOException { - Path emptyFolderPath = emptyFilesystem(); - NioFolder folder = NioFileSystem.rootedAt(emptyFolderPath); - Files.delete(emptyFolderPath); + String nameOfNonExistingFolder = "nameOfFolder"; + Path filesystemPath = emptyFilesystem(); + Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(nameOfNonExistingFolder); thrown.expect(UncheckedIOException.class); - thrown.expectMessage(emptyFolderPath.toString()); + thrown.expectMessage(filesystemPath.resolve(nameOfNonExistingFolder).toString()); + + folder.children(); + } + + @Test + public void testChildrenOfFolderWhichIsAFileThrowsUncheckedIOExceptionWithAbsolutePathOfFolderInMessage() throws IOException { + String folderName = "nameOfFolder"; + Path filesystemPath = testFilesystem(file(folderName)); + Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName); + + thrown.expect(UncheckedIOException.class); + thrown.expectMessage(filesystemPath.resolve(folderName).toString()); folder.children(); } @@ -147,4 +207,222 @@ public class NioFolderTest { folder.folders(); } + + @Test + public void testDeleteOfEmptyFolderDeletesIt() throws IOException { + String folderName = "nameOfFolder"; + Path filesystemPath = testFilesystem( // + folder(folderName)); + Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName); + + folder.delete(); + + assertThat(filesystemPath.resolve(folderName), doesNotExist()); + } + + @Test + public void testDeleteOfFolderWithChildrenDeletesItAndAllChildrenRecursive() throws IOException { + String folderName = "nameOfFolder"; + Path filesystemPath = testFilesystem( // + folder(folderName), // + folder(folderName + "/subfolder1"), // + file(folderName + "/subfolder1/fileName1"), // + folder(folderName + "/subfolder2"), // + file(folderName + "/fileName1"), // + file(folderName + "/fileName2")); + Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName); + + folder.delete(); + + assertThat(filesystemPath.resolve(folderName), doesNotExist()); + } + + @Test + public void testDeleteOfNonExistingFolderDoesNothing() throws IOException { + String folderName = "nameOfFolder"; + Path filesystemPath = emptyFilesystem(); + Folder nonExistingFolder = NioFileSystem.rootedAt(filesystemPath).folder(folderName); + + nonExistingFolder.delete(); + + assertThat(filesystemPath.resolve(folderName), doesNotExist()); + } + + @Test + public void testDeleteOfFolderWhichIsAFileDoesNothing() throws IOException { + String folderName = "nameOfFolder"; + Path filesystemPath = testFilesystem( // + file(folderName)); + Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName); + + folder.delete(); + + assertThat(filesystemPath.resolve(folderName), isFile()); + } + + @Test + public void testExistsReturnsTrueForExistingDirectory() throws IOException { + String folderName = "nameOfFolder"; + Path filesystemPath = testFilesystem( // + folder(folderName)); + Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName); + + assertThat(folder.exists(), is(true)); + } + + @Test + public void testExistsReturnsFalseForNonExistingDirectory() throws IOException { + String folderName = "nameOfFolder"; + Path filesystemPath = emptyFilesystem(); + Folder nonExistingFolder = NioFileSystem.rootedAt(filesystemPath).folder(folderName); + + assertThat(nonExistingFolder.exists(), is(false)); + } + + @Test + public void testExistsReturnsFalseForDirectoryWhichIsAFile() throws IOException { + String folderName = "nameOfFolder"; + Path filesystemPath = testFilesystem( // + file(folderName)); + Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName); + + assertThat(folder.exists(), is(false)); + } + + @Test + public void testIsAncestorOfWithChildReturnsTrue() throws IOException { + Folder folder = NioFileSystem.rootedAt(emptyFilesystem()).folder("a"); + Folder child = folder.folder("b"); + + assertThat(folder.isAncestorOf(child), is(true)); + } + + @Test + public void testIsAncestorOfWithChildOfChildReturnsTrue() throws IOException { + Folder folder = NioFileSystem.rootedAt(emptyFilesystem()).folder("a"); + Folder child = folder.folder("b"); + File childOfChild = child.file("c"); + + assertThat(folder.isAncestorOf(childOfChild), is(true)); + } + + @Test + public void testIsAncestorOfWithSiblingReturnsFalse() throws IOException { + FileSystem fileSystem = NioFileSystem.rootedAt(emptyFilesystem()); + Folder folder = fileSystem.folder("a"); + Folder sibling = fileSystem.folder("b"); + + assertThat(folder.isAncestorOf(sibling), is(false)); + } + + @Test + public void testIsAncestorOfWithParentReturnsFalse() throws IOException { + Folder parent = NioFileSystem.rootedAt(emptyFilesystem()).folder("a"); + Folder folder = parent.folder("b"); + + assertThat(folder.isAncestorOf(parent), is(false)); + } + + @Test + public void testLastModifiedOfExistingFolderReturnsLastModifiedDate() throws IOException { + String folderName = "nameOfFolder"; + Instant lastModified = Instant.parse("2015-12-29T15:36:10.00Z"); + Folder folder = NioFileSystem + .rootedAt(testFilesystem( // + folder(folderName).withLastModified(lastModified))) // + .folder(folderName); + + assertThat(folder.lastModified(), is(lastModified)); + } + + @Test + public void testLastModifiedOfNonExistingFolderThrowsUncheckedIOExceptionWithAbsolutePathOfFolder() throws IOException { + String folderName = "nameOfFolder"; + Path filesystemPath = emptyFilesystem(); + Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName); + + thrown.expect(UncheckedIOException.class); + thrown.expectMessage(filesystemPath.resolve(folderName).toString()); + + folder.lastModified(); + } + + @Test + public void testLastModifiedOfFolderWhichIsAFileThrowsUncheckedIOExceptionWithAbsolutePathOfFolder() throws IOException { + String folderName = "nameOfFolder"; + Path filesystemPath = testFilesystem(file(folderName)); + Folder folder = NioFileSystem // + .rootedAt(filesystemPath) // + .folder(folderName); + + thrown.expect(UncheckedIOException.class); + thrown.expectMessage(filesystemPath.resolve(folderName).toString()); + + folder.lastModified(); + } + + @Test + public void testParentOfDirectChildOfFilesystemReturnsFilesystem() throws IOException { + FileSystem fileSystem = NioFileSystem.rootedAt(emptyFilesystem()); + Folder folder = fileSystem.folder("aName"); + + assertThat(folder.parent(), presentOptionalWithValueThat(is(sameInstance(fileSystem)))); + } + + @Test + public void testParentOfChildOfFolderReturnsFolder() throws IOException { + Folder folder = NioFileSystem.rootedAt(emptyFilesystem()).folder("aName"); + Folder child = folder.folder("anotherName"); + + assertThat(child.parent(), presentOptionalWithValueThat(is(sameInstance(folder)))); + } + + @Test + public void testMoveToOfFolderToNonExistingFolderMovesFolder() throws IOException { + Path filesystemPath = testFilesystem( // + folder("folderToMove")); + NioFileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath); + Folder folderToMove = fileSystem.folder("folderToMove"); + Folder folderToMoveTo = fileSystem.folder("folderToMoveTo"); + + folderToMove.moveTo(folderToMoveTo); + + assertThat(filesystemPath.resolve("folderToMove"), doesNotExist()); + assertThat(filesystemPath.resolve("folderToMoveTo"), isDirectory()); + } + + @Test + public void testMoveToOfFolderWithChildrenMovesFolderAndChildren() throws IOException { + Path filesystemPath = testFilesystem( // + folder("folderToMove"), // + folder("folderToMove/subfolder1"), // + folder("folderToMove/subfolder2"), // + file("folderToMove/subfolder1/file1"), // + file("folderToMove/file2"), // + file("folderToMove/file3")); + NioFileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath); + Folder folderToMove = fileSystem.folder("folderToMove"); + Folder folderToMoveTo = fileSystem.folder("folderToMoveTo"); + + folderToMove.moveTo(folderToMoveTo); + + assertThat(filesystemPath.resolve("folderToMove"), doesNotExist()); + assertThat(filesystemPath.resolve("folderToMoveTo"), isDirectory()); + assertThat(filesystemPath.resolve("folderToMoveTo/subfolder1"), isDirectory()); + assertThat(filesystemPath.resolve("folderToMoveTo/subfolder2"), isDirectory()); + assertThat(filesystemPath.resolve("folderToMoveTo/subfolder1/file1"), isFile()); + assertThat(filesystemPath.resolve("folderToMoveTo/file2"), isFile()); + assertThat(filesystemPath.resolve("folderToMoveTo/file3"), isFile()); + } + + @Test + public void testMoveToOfFolderToExistingFolderReplacesTargetFolder() throws IOException { + // TODO Markus Kreusch implement test + } + + @Test + public void testMoveToOfFolderToExistingFileThrowsUncheckedIOExceptionWithAbsolutePathOfTarget() throws IOException { + // TODO Markus Kreusch implement test + } + } diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/PathMatcher.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/PathMatcher.java index a7b53ac4b..7d117cd64 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/PathMatcher.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/PathMatcher.java @@ -19,4 +19,31 @@ class PathMatcher { }; } + public static Matcher isFile() { + return new FeatureMatcher(is(true), "a path for which Files.isRegularFile", "Files.isRegularFile") { + @Override + protected Boolean featureValueOf(Path actual) { + return Files.isRegularFile(actual); + } + }; + } + + public static Matcher doesNotExist() { + return new FeatureMatcher(is(false), "a path for which Files.exists", "Files.exists") { + @Override + protected Boolean featureValueOf(Path actual) { + return Files.exists(actual); + } + }; + } + + public static Matcher doesExist() { + return new FeatureMatcher(is(true), "a path for which Files.exists", "Files.exists") { + @Override + protected Boolean featureValueOf(Path actual) { + return Files.exists(actual); + } + }; + } + }