Преглед на файлове

Additional filesystem-invariants-tests

* added tests
* fixed issue in CryptoFileSystem when deleting a file
** FileContentEncryptorImpl now does not submit an empty cleartext
buffer when receiving EOF
** CryptoWritableFile now only writesHeader on close if the file is
still open
Markus Kreusch преди 9 години
родител
ревизия
1a9ac16256

+ 2 - 2
main/commons-test/src/main/java/org/cryptomator/common/test/matcher/OptionalMatcher.java

@@ -13,7 +13,7 @@ public class OptionalMatcher {
 			@Override
 			public void describeTo(Description description) {
 				description //
-						.appendText("a present Optional with a value that") //
+						.appendText("a present Optional with a value that ") //
 						.appendDescriptionOf(valueMatcher);
 			}
 
@@ -23,7 +23,7 @@ public class OptionalMatcher {
 					if (valueMatcher.matches(item.get())) {
 						return true;
 					} else {
-						mismatchDescription.appendText("a present Optional with value that");
+						mismatchDescription.appendText("a present Optional with value that ");
 						valueMatcher.describeMismatch(item, mismatchDescription);
 						return false;
 					}

+ 4 - 2
main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImpl.java

@@ -81,8 +81,10 @@ class FileContentEncryptorImpl implements FileContentEncryptor {
 
 	private void submitCleartextBuffer() throws InterruptedException {
 		cleartextBuffer.flip();
-		Callable<ByteBuffer> encryptionJob = new EncryptionJob(cleartextBuffer, chunkNumber++);
-		dataProcessor.submit(encryptionJob);
+		if (cleartextBuffer.hasRemaining()) {
+			Callable<ByteBuffer> encryptionJob = new EncryptionJob(cleartextBuffer, chunkNumber++);
+			dataProcessor.submit(encryptionJob);
+		}
 	}
 
 	private void submitEof() throws InterruptedException {

+ 4 - 2
main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java

@@ -109,8 +109,10 @@ class CryptoWritableFile implements WritableFile {
 		try {
 			encryptor.append(FileContentCryptor.EOF);
 			writeTask.get();
-			writeHeader();
-			// TODO append padding
+			if (file.isOpen()) {
+				writeHeader();
+				// TODO append padding
+			}
 		} catch (ExecutionException e) {
 			if (e.getCause() instanceof UncheckedIOException) {
 				throw new UncheckedIOException(new IOException(e));

+ 90 - 0
main/filesystem-invariants-tests/src/test/java/org/cryptomator/filesystem/invariants/FileBiFunctions.java

@@ -0,0 +1,90 @@
+package org.cryptomator.filesystem.invariants;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.cryptomator.filesystem.File;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.WritableFile;
+import org.cryptomator.filesystem.invariants.FileBiFunctions.FileBiFunction;
+
+class FileBiFunctions implements Iterable<FileBiFunction> {
+
+	private final List<FileBiFunction> factories = new ArrayList<>();
+
+	public FileBiFunctions() {
+		addNonExisting("invoke file", this::invokeFile);
+
+		addExisting("create file by writing to it", this::createFileUsingTouch);
+	}
+
+	private File invokeFile(Folder parent, String name) {
+		return parent.file(name);
+	}
+
+	private File createFileUsingTouch(Folder parent, String name) {
+		File result = parent.file(name);
+		try (WritableFile writable = result.openWritable()) {
+			writable.write(ByteBuffer.wrap(new byte[] {1}));
+		}
+		return result;
+	}
+
+	private void addExisting(String name, ExistingFileBiFunction factory) {
+		factories.add(new ExistingFileBiFunction() {
+			@Override
+			public File fileWithName(Folder parent, String name) {
+				return factory.fileWithName(parent, name);
+			}
+
+			@Override
+			public String toString() {
+				return name;
+			}
+		});
+	}
+
+	private void addNonExisting(String name, NonExistingFileBiFunction factory) {
+		factories.add(new NonExistingFileBiFunction() {
+			@Override
+			public File fileWithName(Folder parent, String name) {
+				return factory.fileWithName(parent, name);
+			}
+
+			@Override
+			public String toString() {
+				return name;
+			}
+		});
+	}
+
+	public interface FileBiFunction {
+
+		File fileWithName(Folder parent, String name);
+
+		boolean returnedFilesExist();
+
+	}
+
+	public interface ExistingFileBiFunction extends FileBiFunction {
+		@Override
+		default boolean returnedFilesExist() {
+			return true;
+		}
+	}
+
+	public interface NonExistingFileBiFunction extends FileBiFunction {
+		@Override
+		default boolean returnedFilesExist() {
+			return false;
+		}
+	}
+
+	@Override
+	public Iterator<FileBiFunction> iterator() {
+		return factories.iterator();
+	}
+
+}

+ 20 - 10
main/filesystem-invariants-tests/src/test/java/org/cryptomator/filesystem/invariants/SubfolderBiFunctions.java

@@ -5,19 +5,20 @@ import java.util.Iterator;
 import java.util.List;
 
 import org.cryptomator.filesystem.Folder;
-import org.cryptomator.filesystem.invariants.SubfolderBiFunctions.SubfolderBiFunction;
+import org.cryptomator.filesystem.invariants.FolderBiFunctions.FolderBiFunction;
 
-class SubfolderBiFunctions implements Iterable<SubfolderBiFunction> {
+class FolderBiFunctions implements Iterable<FolderBiFunction> {
 
-	private final List<SubfolderBiFunction> factories = new ArrayList<>();
+	private final List<FolderBiFunction> factories = new ArrayList<>();
 
-	public SubfolderBiFunctions() {
+	public FolderBiFunctions() {
 		addNonExisting("invoke folder", this::invokeFolder);
 		addNonExisting("create and delete", this::createAndDeleteFolder);
-		addNonExisting("delete by moving", this::moveFolderAway);
+		addNonExisting("delete by moving", this::deleteFolderByMoving);
 
 		addExisting("invoke folder and create", this::invokeFolderAndCreate);
 		addExisting("create by moving", this::createByMoving);
+		addExisting("create by copying", this::createByCopying);
 	}
 
 	private Folder invokeFolder(Folder parent, String name) {
@@ -37,7 +38,7 @@ class SubfolderBiFunctions implements Iterable<SubfolderBiFunction> {
 		return result;
 	}
 
-	private Folder moveFolderAway(Folder parent, String name) {
+	private Folder deleteFolderByMoving(Folder parent, String name) {
 		Folder result = parent.folder(name);
 		result.create();
 		Folder target = parent.folder("subfolderFactoryMoveFolderAway");
@@ -54,6 +55,15 @@ class SubfolderBiFunctions implements Iterable<SubfolderBiFunction> {
 		return target;
 	}
 
+	private Folder createByCopying(Folder parent, String name) {
+		Folder temporary = parent.folder("subfolderFactoryCreateByCopying");
+		temporary.create();
+		Folder target = parent.folder(name);
+		temporary.copyTo(target);
+		temporary.delete();
+		return target;
+	}
+
 	private void addExisting(String name, ExistingSubfolderBiFunction factory) {
 		factories.add(new ExistingSubfolderBiFunction() {
 			@Override
@@ -82,7 +92,7 @@ class SubfolderBiFunctions implements Iterable<SubfolderBiFunction> {
 		});
 	}
 
-	public interface SubfolderBiFunction {
+	public interface FolderBiFunction {
 
 		Folder subfolderWithName(Folder parent, String name);
 
@@ -90,14 +100,14 @@ class SubfolderBiFunctions implements Iterable<SubfolderBiFunction> {
 
 	}
 
-	public interface ExistingSubfolderBiFunction extends SubfolderBiFunction {
+	public interface ExistingSubfolderBiFunction extends FolderBiFunction {
 		@Override
 		default boolean returnedFoldersExist() {
 			return true;
 		}
 	}
 
-	public interface NonExistingSubfolderSubfolderBiFunction extends SubfolderBiFunction {
+	public interface NonExistingSubfolderSubfolderBiFunction extends FolderBiFunction {
 		@Override
 		default boolean returnedFoldersExist() {
 			return false;
@@ -105,7 +115,7 @@ class SubfolderBiFunctions implements Iterable<SubfolderBiFunction> {
 	}
 
 	@Override
-	public Iterator<SubfolderBiFunction> iterator() {
+	public Iterator<FolderBiFunction> iterator() {
 		return factories.iterator();
 	}
 

+ 89 - 24
main/filesystem-invariants-tests/src/test/java/org/cryptomator/filesystem/invariants/FolderChildrenTests.java

@@ -10,10 +10,12 @@ import static org.junit.Assume.assumeThat;
 
 import java.io.UncheckedIOException;
 
+import org.cryptomator.filesystem.File;
 import org.cryptomator.filesystem.FileSystem;
 import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.invariants.FileBiFunctions.FileBiFunction;
 import org.cryptomator.filesystem.invariants.FileSystemFactories.FileSystemFactory;
-import org.cryptomator.filesystem.invariants.SubfolderBiFunctions.SubfolderBiFunction;
+import org.cryptomator.filesystem.invariants.FolderBiFunctions.FolderBiFunction;
 import org.junit.Rule;
 import org.junit.experimental.theories.DataPoints;
 import org.junit.experimental.theories.Theories;
@@ -30,17 +32,20 @@ public class FolderChildrenTests {
 	public static final Iterable<FileSystemFactory> FILE_SYSTEM_FACTORIES = new FileSystemFactories();
 
 	@DataPoints
-	public static final Iterable<SubfolderBiFunction> SUBFOLDER_BI_FUNCTIONS = new SubfolderBiFunctions();
+	public static final Iterable<FolderBiFunction> SUBFOLDER_BI_FUNCTIONS = new FolderBiFunctions();
+
+	@DataPoints
+	public static final Iterable<FileBiFunction> SUBFILE_BI_FUNCTIONS = new FileBiFunctions();
 
 	@Rule
 	public final ExpectedException thrown = ExpectedException.none();
 
 	@Theory
-	public void testChildrenThrowsExceptionIfFolderDoesNotExist(FileSystemFactory fileSystemFactory, SubfolderBiFunction subfolderFunction) {
-		assumeThat(subfolderFunction.returnedFoldersExist(), is(false));
+	public void testChildrenThrowsExceptionIfFolderDoesNotExist(FileSystemFactory fileSystemFactory, FolderBiFunction folderFunction) {
+		assumeThat(folderFunction.returnedFoldersExist(), is(false));
 
 		FileSystem fileSystem = fileSystemFactory.create();
-		Folder nonExistingFolder = subfolderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder nonExistingFolder = folderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
 
 		thrown.expect(UncheckedIOException.class);
 
@@ -48,11 +53,11 @@ public class FolderChildrenTests {
 	}
 
 	@Theory
-	public void testFilesThrowsExceptionIfFolderDoesNotExist(FileSystemFactory fileSystemFactory, SubfolderBiFunction subfolderFunction) {
-		assumeThat(subfolderFunction.returnedFoldersExist(), is(false));
+	public void testFilesThrowsExceptionIfFolderDoesNotExist(FileSystemFactory fileSystemFactory, FolderBiFunction folderFunction) {
+		assumeThat(folderFunction.returnedFoldersExist(), is(false));
 
 		FileSystem fileSystem = fileSystemFactory.create();
-		Folder nonExistingFolder = subfolderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder nonExistingFolder = folderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
 
 		thrown.expect(UncheckedIOException.class);
 
@@ -60,11 +65,11 @@ public class FolderChildrenTests {
 	}
 
 	@Theory
-	public void testFoldersThrowsExceptionIfFolderDoesNotExist(FileSystemFactory fileSystemFactory, SubfolderBiFunction subfolderFunction) {
-		assumeThat(subfolderFunction.returnedFoldersExist(), is(false));
+	public void testFoldersThrowsExceptionIfFolderDoesNotExist(FileSystemFactory fileSystemFactory, FolderBiFunction folderFunction) {
+		assumeThat(folderFunction.returnedFoldersExist(), is(false));
 
 		FileSystem fileSystem = fileSystemFactory.create();
-		Folder nonExistingFolder = subfolderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder nonExistingFolder = folderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
 
 		thrown.expect(UncheckedIOException.class);
 
@@ -72,37 +77,37 @@ public class FolderChildrenTests {
 	}
 
 	@Theory
-	public void testChildrenIsEmptyForEmptyFolder(FileSystemFactory fileSystemFactory, SubfolderBiFunction subfolderFunction) {
-		assumeThat(subfolderFunction.returnedFoldersExist(), is(true));
+	public void testChildrenIsEmptyForEmptyFolder(FileSystemFactory fileSystemFactory, FolderBiFunction folderFunction) {
+		assumeThat(folderFunction.returnedFoldersExist(), is(true));
 
 		FileSystem fileSystem = fileSystemFactory.create();
-		Folder existingFolder = subfolderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder existingFolder = folderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
 
 		assertThat(existingFolder.children().count(), is(0L));
 	}
 
 	@Theory
-	public void testFilesIsEmptyForEmptyFolder(FileSystemFactory fileSystemFactory, SubfolderBiFunction subfolderFunction) {
-		assumeThat(subfolderFunction.returnedFoldersExist(), is(true));
+	public void testFilesIsEmptyForEmptyFolder(FileSystemFactory fileSystemFactory, FolderBiFunction folderFunction) {
+		assumeThat(folderFunction.returnedFoldersExist(), is(true));
 
 		FileSystem fileSystem = fileSystemFactory.create();
-		Folder existingFolder = subfolderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder existingFolder = folderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
 
 		assertThat(existingFolder.files().count(), is(0L));
 	}
 
 	@Theory
-	public void testFoldersIsEmptyForEmptyFolder(FileSystemFactory fileSystemFactory, SubfolderBiFunction subfolderFunction) {
-		assumeThat(subfolderFunction.returnedFoldersExist(), is(true));
+	public void testFoldersIsEmptyForEmptyFolder(FileSystemFactory fileSystemFactory, FolderBiFunction folderFunction) {
+		assumeThat(folderFunction.returnedFoldersExist(), is(true));
 
 		FileSystem fileSystem = fileSystemFactory.create();
-		Folder existingFolder = subfolderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder existingFolder = folderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
 
 		assertThat(existingFolder.folders().count(), is(0L));
 	}
 
 	@Theory
-	public void testChildrenContainsCreatedChildFolder(FileSystemFactory fileSystemFactory, SubfolderBiFunction existingFolderFunction, SubfolderBiFunction childExistingFolderFunction) {
+	public void testChildrenContainsCreatedChildFolder(FileSystemFactory fileSystemFactory, FolderBiFunction existingFolderFunction, FolderBiFunction childExistingFolderFunction) {
 		assumeThat(existingFolderFunction.returnedFoldersExist(), is(true));
 		assumeThat(childExistingFolderFunction.returnedFoldersExist(), is(true));
 
@@ -116,7 +121,7 @@ public class FolderChildrenTests {
 	}
 
 	@Theory
-	public void testChildrenDoesNotContainCreatedAndDeletedChildFolder(FileSystemFactory fileSystemFactory, SubfolderBiFunction existingFolderFunction, SubfolderBiFunction childExistingFolderFunction) {
+	public void testChildrenDoesNotContainCreatedAndDeletedChildFolder(FileSystemFactory fileSystemFactory, FolderBiFunction existingFolderFunction, FolderBiFunction childExistingFolderFunction) {
 		assumeThat(existingFolderFunction.returnedFoldersExist(), is(true));
 		assumeThat(childExistingFolderFunction.returnedFoldersExist(), is(true));
 
@@ -131,7 +136,67 @@ public class FolderChildrenTests {
 	}
 
 	@Theory
-	public void testFoldersContainsAndFilesDoesNotContainCreatedChildFolder(FileSystemFactory fileSystemFactory, SubfolderBiFunction existingFolderFunction, SubfolderBiFunction childExistingFolderFunction) {
+	public void testChildrenContainsCreatedFile(FileSystemFactory fileSystemFactory, FolderBiFunction existingFolderFunction, FileBiFunction existingFileFunction) {
+		assumeThat(existingFolderFunction.returnedFoldersExist(), is(true));
+		assumeThat(existingFileFunction.returnedFilesExist(), is(true));
+
+		String childName = "childFolderName";
+
+		FileSystem fileSystem = fileSystemFactory.create();
+		Folder existingFolder = existingFolderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		File file = existingFileFunction.fileWithName(existingFolder, childName);
+
+		assertThat(existingFolder.children().collect(toList()), containsInAnyOrder(equalTo(file)));
+	}
+
+	@Theory
+	public void testChildrenDoesNotContainCreatedAndDeletedFile(FileSystemFactory fileSystemFactory, FolderBiFunction existingFolderFunction, FileBiFunction existingFileFunction) {
+		assumeThat(existingFolderFunction.returnedFoldersExist(), is(true));
+		assumeThat(existingFileFunction.returnedFilesExist(), is(true));
+
+		String childName = "childFolderName";
+
+		FileSystem fileSystem = fileSystemFactory.create();
+		Folder existingFolder = existingFolderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		File file = existingFileFunction.fileWithName(existingFolder, childName);
+		file.delete();
+
+		assertThat(existingFolder.children().collect(toList()), is(empty()));
+	}
+
+	@Theory
+	public void testFoldersDoesNotContainAndFilesContainsCreatedFile(FileSystemFactory fileSystemFactory, FolderBiFunction existingFolderFunction, FileBiFunction existingFileFunction) {
+		assumeThat(existingFolderFunction.returnedFoldersExist(), is(true));
+		assumeThat(existingFileFunction.returnedFilesExist(), is(true));
+
+		String childName = "childFolderName";
+
+		FileSystem fileSystem = fileSystemFactory.create();
+		Folder existingFolder = existingFolderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		File file = existingFileFunction.fileWithName(existingFolder, childName);
+
+		assertThat(existingFolder.folders().collect(toList()), is(empty()));
+		assertThat(existingFolder.files().collect(toList()), containsInAnyOrder(equalTo(file)));
+	}
+
+	@Theory
+	public void testFoldersAndFilesDoesNotContainCreatedAndDeletedFile(FileSystemFactory fileSystemFactory, FolderBiFunction existingFolderFunction, FileBiFunction existingFileFunction) {
+		assumeThat(existingFolderFunction.returnedFoldersExist(), is(true));
+		assumeThat(existingFileFunction.returnedFilesExist(), is(true));
+
+		String childName = "childFolderName";
+
+		FileSystem fileSystem = fileSystemFactory.create();
+		Folder existingFolder = existingFolderFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		File file = existingFileFunction.fileWithName(existingFolder, childName);
+		file.delete();
+
+		assertThat(existingFolder.folders().collect(toList()), is(empty()));
+		assertThat(existingFolder.files().collect(toList()), is(empty()));
+	}
+
+	@Theory
+	public void testFoldersContainsAndFilesDoesNotContainCreatedChildFolder(FileSystemFactory fileSystemFactory, FolderBiFunction existingFolderFunction, FolderBiFunction childExistingFolderFunction) {
 		assumeThat(existingFolderFunction.returnedFoldersExist(), is(true));
 		assumeThat(childExistingFolderFunction.returnedFoldersExist(), is(true));
 
@@ -146,7 +211,7 @@ public class FolderChildrenTests {
 	}
 
 	@Theory
-	public void testFoldersAndFilesDoesNotContainCreatedAndDeletedChildFolder(FileSystemFactory fileSystemFactory, SubfolderBiFunction existingFolderFunction, SubfolderBiFunction childExistingFolderFunction) {
+	public void testFoldersAndFilesDoesNotContainCreatedAndDeletedChildFolder(FileSystemFactory fileSystemFactory, FolderBiFunction existingFolderFunction, FolderBiFunction childExistingFolderFunction) {
 		assumeThat(existingFolderFunction.returnedFoldersExist(), is(true));
 		assumeThat(childExistingFolderFunction.returnedFoldersExist(), is(true));
 

+ 159 - 8
main/filesystem-invariants-tests/src/test/java/org/cryptomator/filesystem/invariants/FolderTests.java

@@ -1,13 +1,18 @@
 package org.cryptomator.filesystem.invariants;
 
+import static org.cryptomator.common.test.matcher.OptionalMatcher.presentOptionalWithValueThat;
+import static org.cryptomator.filesystem.invariants.matchers.NodeMatchers.fileWithName;
+import static org.cryptomator.filesystem.invariants.matchers.NodeMatchers.folderWithName;
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assume.assumeThat;
 
+import org.cryptomator.filesystem.File;
 import org.cryptomator.filesystem.FileSystem;
 import org.cryptomator.filesystem.Folder;
 import org.cryptomator.filesystem.invariants.FileSystemFactories.FileSystemFactory;
-import org.cryptomator.filesystem.invariants.SubfolderBiFunctions.SubfolderBiFunction;
+import org.cryptomator.filesystem.invariants.FolderBiFunctions.FolderBiFunction;
 import org.junit.Rule;
 import org.junit.experimental.theories.DataPoints;
 import org.junit.experimental.theories.Theories;
@@ -19,34 +24,180 @@ import org.junit.runner.RunWith;
 public class FolderTests {
 
 	private static final String FOLDER_NAME = "folderName";
+	private static final String FOLDER_NAME_2 = "folderName2";
+
+	private static final String PATH_NAME_1 = "pathName1";
+	private static final String PATH_NAME_2 = "pathName2";
+	private static final String PATH = PATH_NAME_1 + '/' + PATH_NAME_2;
+
+	private static final String FILE_NAME = "fileName";
 
 	@DataPoints
 	public static final Iterable<FileSystemFactory> FILE_SYSTEM_FACTORIES = new FileSystemFactories();
 
 	@DataPoints
-	public static final Iterable<SubfolderBiFunction> SUBFOLDER_FACTORIES = new SubfolderBiFunctions();
+	public static final Iterable<FolderBiFunction> SUBFOLDER_FACTORIES = new FolderBiFunctions();
 
 	@Rule
 	public final ExpectedException thrown = ExpectedException.none();
 
 	@Theory
-	public void testExistingFolderExists(FileSystemFactory fileSystemFactory, SubfolderBiFunction subfolderFactory) {
-		assumeThat(subfolderFactory.returnedFoldersExist(), is(true));
+	public void testFolderReturnsFolder(FileSystemFactory fileSystemFactory) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		assertThat(fileSystem.folder(FOLDER_NAME), is(notNullValue()));
+	}
+
+	@Theory
+	public void testFolderOnSubfolderReturnsFolder(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		Folder folder = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+
+		assertThat(folder.folder(FOLDER_NAME), is(notNullValue()));
+	}
+
+	@Theory
+	public void testResolveFolderReturnsFolder(FileSystemFactory fileSystemFactory) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		Folder resolvedFolder = fileSystem.resolveFolder(PATH);
+
+		assertThat(resolvedFolder, is(folderWithName(PATH_NAME_2)));
+		assertThat(resolvedFolder.parent(), presentOptionalWithValueThat(is(folderWithName(PATH_NAME_1))));
+		assertThat(resolvedFolder.parent().get().parent(), presentOptionalWithValueThat(is(fileSystem)));
+	}
+
+	@Theory
+	public void testResolveFolderOnSubfolderReturnsFolder(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		Folder folder = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder resolvedFolder = folder.resolveFolder(PATH);
+
+		assertThat(resolvedFolder, is(folderWithName(PATH_NAME_2)));
+		assertThat(resolvedFolder.parent(), presentOptionalWithValueThat(is(folderWithName(PATH_NAME_1))));
+		assertThat(resolvedFolder.parent().get().parent(), presentOptionalWithValueThat(is(folder)));
+	}
+
+	@Theory
+	public void testFileReturnsFile(FileSystemFactory fileSystemFactory) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		assertThat(fileSystem.file(FILE_NAME), is(notNullValue()));
+	}
+
+	@Theory
+	public void testFileOnSubfolderReturnsFile(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		Folder folder = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+
+		assertThat(folder.file(FILE_NAME), is(notNullValue()));
+	}
+
+	@Theory
+	public void testResolveFileReturnsFile(FileSystemFactory fileSystemFactory) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		File resolvedFile = fileSystem.resolveFile(PATH);
+
+		assertThat(resolvedFile, is(fileWithName(PATH_NAME_2)));
+		assertThat(resolvedFile.parent(), presentOptionalWithValueThat(is(folderWithName(PATH_NAME_1))));
+		assertThat(resolvedFile.parent().get().parent(), presentOptionalWithValueThat(is(fileSystem)));
+	}
+
+	@Theory
+	public void testResolveFileOnSubfolderReturnsFile(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		Folder folder = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		File resolvedFile = folder.resolveFile(PATH);
+
+		assertThat(resolvedFile, is(fileWithName(PATH_NAME_2)));
+		assertThat(resolvedFile.parent(), presentOptionalWithValueThat(is(folderWithName(PATH_NAME_1))));
+		assertThat(resolvedFile.parent().get().parent(), presentOptionalWithValueThat(is(folder)));
+	}
+
+	@Theory
+	public void testExistingFolderExists(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		assumeThat(folderBiFunction.returnedFoldersExist(), is(true));
 
 		FileSystem fileSystem = fileSystemFactory.create();
-		Folder existingFolder = subfolderFactory.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder existingFolder = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
 
 		assertThat(existingFolder.exists(), is(true));
 	}
 
 	@Theory
-	public void testNonExistingFolderDoesntExists(FileSystemFactory fileSystemFactory, SubfolderBiFunction subfolderFactory) {
-		assumeThat(subfolderFactory.returnedFoldersExist(), is(false));
+	public void testNonExistingFolderDoesntExists(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		assumeThat(folderBiFunction.returnedFoldersExist(), is(false));
 
 		FileSystem fileSystem = fileSystemFactory.create();
-		Folder existingFolder = subfolderFactory.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder existingFolder = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
 
 		assertThat(existingFolder.exists(), is(false));
 	}
 
+	@Theory
+	public void testFolderIsNotAncecstorOfItself(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		Folder folder = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+
+		assertThat(folder.isAncestorOf(folder), is(false));
+	}
+
+	@Theory
+	public void testFolderIsNotAncecstorOfItsParent(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		Folder parent = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder child = folderBiFunction.subfolderWithName(parent, FOLDER_NAME);
+
+		assertThat(child.isAncestorOf(parent), is(false));
+	}
+
+	@Theory
+	public void testFolderIsNotAncecstorOfItsParentsParent(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		Folder parentsParent = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder parent = folderBiFunction.subfolderWithName(parentsParent, FOLDER_NAME);
+		Folder child = folderBiFunction.subfolderWithName(parent, FOLDER_NAME);
+
+		assertThat(child.isAncestorOf(parentsParent), is(false));
+	}
+
+	@Theory
+	public void testFolderIsNotAncecstorOfItsSibling(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		Folder folder = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder sibling = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME_2);
+
+		assertThat(folder.isAncestorOf(sibling), is(false));
+	}
+
+	@Theory
+	public void testFolderIsAncecstorOfItsChild(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		Folder folder = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder child = folderBiFunction.subfolderWithName(folder, FOLDER_NAME);
+
+		assertThat(folder.isAncestorOf(child), is(true));
+	}
+
+	@Theory
+	public void testFolderIsAncecstorOfItsChildsChild(FileSystemFactory fileSystemFactory, FolderBiFunction folderBiFunction) {
+		FileSystem fileSystem = fileSystemFactory.create();
+
+		Folder folder = folderBiFunction.subfolderWithName(fileSystem, FOLDER_NAME);
+		Folder child = folderBiFunction.subfolderWithName(folder, FOLDER_NAME);
+		Folder childsChild = folderBiFunction.subfolderWithName(child, FOLDER_NAME);
+
+		assertThat(folder.isAncestorOf(childsChild), is(true));
+	}
+
 }

+ 20 - 0
main/filesystem-invariants-tests/src/test/java/org/cryptomator/filesystem/invariants/matchers/NodeMatchers.java

@@ -0,0 +1,20 @@
+package org.cryptomator.filesystem.invariants.matchers;
+
+import static org.hamcrest.CoreMatchers.is;
+
+import org.cryptomator.common.test.matcher.PropertyMatcher;
+import org.cryptomator.filesystem.File;
+import org.cryptomator.filesystem.Folder;
+import org.hamcrest.Matcher;
+
+public class NodeMatchers {
+
+	public static Matcher<Folder> folderWithName(String name) {
+		return new PropertyMatcher<>(Folder.class, Folder::name, "name", is(name));
+	}
+
+	public static Matcher<File> fileWithName(String name) {
+		return new PropertyMatcher<>(File.class, File::name, "name", is(name));
+	}
+
+}