diff --git a/src/main/java/org/cryptomator/common/mount/MountWithinParentUtil.java b/src/main/java/org/cryptomator/common/mount/MountWithinParentUtil.java index 11671e4af..467ffa9cf 100644 --- a/src/main/java/org/cryptomator/common/mount/MountWithinParentUtil.java +++ b/src/main/java/org/cryptomator/common/mount/MountWithinParentUtil.java @@ -70,7 +70,8 @@ public final class MountWithinParentUtil { } } - private static boolean removeResidualJunction(Path path) { + //visible for testing + static boolean removeResidualJunction(Path path) { try { if (Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).isOther()) { LOG.info("Mountpoint \"{}\" is still a junction. Deleting it.", path); @@ -85,7 +86,8 @@ public final class MountWithinParentUtil { } } - private static void removeResidualHideaway(Path mountPoint, Path hideaway) throws IOException { + //visible for testing + static void removeResidualHideaway(Path mountPoint, Path hideaway) throws IOException { checkIsHideawayDirectory(mountPoint, hideaway); Files.delete(hideaway); //Fails if not empty } diff --git a/src/test/java/org/cryptomator/common/mount/MountWithinParentUtilTest.java b/src/test/java/org/cryptomator/common/mount/MountWithinParentUtilTest.java new file mode 100644 index 000000000..715c47be1 --- /dev/null +++ b/src/test/java/org/cryptomator/common/mount/MountWithinParentUtilTest.java @@ -0,0 +1,208 @@ +package org.cryptomator.common.mount; + +import org.apache.commons.lang3.SystemUtils; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static java.nio.file.LinkOption.NOFOLLOW_LINKS; +import static org.cryptomator.common.mount.MountWithinParentUtil.cleanup; +import static org.cryptomator.common.mount.MountWithinParentUtil.getHideaway; +import static org.cryptomator.common.mount.MountWithinParentUtil.prepareParentNoMountPoint; +import static org.cryptomator.common.mount.MountWithinParentUtil.removeResidualHideaway; +import static org.cryptomator.common.mount.MountWithinParentUtil.removeResidualJunction; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class MountWithinParentUtilTest { + + @Test + void testPrepareNeitherExist(@TempDir Path parentDir) { + assertThrows(MountPointNotExistingException.class, () -> { + prepareParentNoMountPoint(parentDir.resolve("mount")); + }); + } + + @Test + void testPrepareOnlyHideawayFileExists(@TempDir Path parentDir) throws IOException { + var mount = parentDir.resolve("mount"); + var hideaway = getHideaway(mount); + Files.createFile(hideaway); + + assertThrows(HideawayNotDirectoryException.class, () -> { + prepareParentNoMountPoint(mount); + }); + } + + @Test + void testPrepareOnlyHideawayDirExists(@TempDir Path parentDir) throws IOException { + var mount = parentDir.resolve("mount"); + var hideaway = getHideaway(mount); + Files.createDirectory(hideaway); + assertFalse(isHidden(hideaway)); + + prepareParentNoMountPoint(mount); + + assertTrue(!SystemUtils.IS_OS_WINDOWS || isHidden(hideaway)); + } + + @Test + void testPrepareBothExistHideawayFile(@TempDir Path parentDir) throws IOException { + var mount = parentDir.resolve("mount"); + var hideaway = getHideaway(mount); + Files.createFile(hideaway); + Files.createDirectory(mount); + + assertThrows(HideawayNotDirectoryException.class, () -> { + prepareParentNoMountPoint(mount); + }); + } + + @Test + void testPrepareBothExistHideawayNotEmpty(@TempDir Path parentDir) throws IOException { + var mount = parentDir.resolve("mount"); + var hideaway = getHideaway(mount); + Files.createDirectory(hideaway); + Files.createFile(hideaway.resolve("dummy")); + Files.createDirectory(mount); + + var exc = assertThrows(UncheckedIOException.class, () -> { + prepareParentNoMountPoint(mount); + }); + assertInstanceOf(DirectoryNotEmptyException.class, exc.getCause()); + } + + @Test + void testPrepareBothExistMountNotDir(@TempDir Path parentDir) throws IOException { + var mount = parentDir.resolve("mount"); + var hideaway = getHideaway(mount); + Files.createDirectory(hideaway); + Files.createFile(mount); + + assertThrows(MountPointNotEmptyDirectoryException.class, () -> { + prepareParentNoMountPoint(mount); + }); + assertTrue(Files.notExists(hideaway, NOFOLLOW_LINKS)); + } + + @Test + void testPrepareBothExistMountNotEmpty(@TempDir Path parentDir) throws IOException { + var mount = parentDir.resolve("mount"); + var hideaway = getHideaway(mount); + Files.createDirectory(hideaway); + Files.createDirectory(mount); + Files.createFile(mount.resolve("dummy")); + + assertThrows(MountPointNotEmptyDirectoryException.class, () -> { + prepareParentNoMountPoint(mount); + }); + assertTrue(Files.notExists(hideaway, NOFOLLOW_LINKS)); + } + + @Test + void testPrepareBothExist(@TempDir Path parentDir) throws IOException { + var mount = parentDir.resolve("mount"); + var hideaway = getHideaway(mount); + Files.createDirectory(hideaway); + Files.createDirectory(mount); + + prepareParentNoMountPoint(mount); + assertTrue(Files.notExists(mount, NOFOLLOW_LINKS)); + assertTrue(isHidden(hideaway)); + } + + @Test + void testRemoveResidualJunction(@TempDir Path parentDir) throws IOException { + //Sadly can't easily create files with "Other" attribute + var regularFile = parentDir.resolve("regularFile"); + Files.createFile(regularFile); + + assertTrue(removeResidualJunction(regularFile)); + assertFalse(removeResidualJunction(parentDir.resolve("notExisting"))); + } + + @Test + void testRemoveResidualHideawayFile(@TempDir Path parentDir) throws IOException { + var hideaway = parentDir.resolve("hideaway"); + Files.createFile(hideaway); + + assertThrows(HideawayNotDirectoryException.class, () -> removeResidualHideaway(parentDir.resolve("mount"), hideaway)); + } + + @Test + void testRemoveResidualHideawayNotEmpty(@TempDir Path parentDir) throws IOException { + var hideaway = parentDir.resolve("hideaway"); + Files.createDirectory(hideaway); + Files.createFile(hideaway.resolve("dummy")); + + assertThrows(DirectoryNotEmptyException.class, () -> removeResidualHideaway(parentDir.resolve("mount"), hideaway)); + } + + @Test + void testCleanupNoHideaway(@TempDir Path parentDir) { + assertDoesNotThrow(() -> cleanup(parentDir.resolve("mount"))); + } + + @Test + void testCleanup(@TempDir Path parentDir) throws IOException { + var mount = parentDir.resolve("mount"); + var hideaway = getHideaway(mount); + Files.createDirectory(hideaway); + + cleanup(mount); + assertTrue(Files.exists(mount, NOFOLLOW_LINKS)); + assertTrue(Files.notExists(hideaway, NOFOLLOW_LINKS)); + assertFalse(isHidden(mount)); + } + + @Test + @EnabledOnOs(OS.WINDOWS) + void testGetHideawayRootDirWin() { + var mount = Path.of("C:\\mount"); + var hideaway = getHideaway(mount); + + assertEquals(mount.getParent().toAbsolutePath(), Path.of("C:\\").toAbsolutePath()); + assertEquals(mount.getParent(), hideaway.getParent()); + assertEquals(mount.getParent().resolve(".~$mount.tmp"), hideaway); + assertEquals(mount.getParent().toAbsolutePath() + ".~$mount.tmp", hideaway.toAbsolutePath().toString()); + } + + @Test + @DisabledOnOs(OS.WINDOWS) + void testGetHideawayRootDirUnix() { + var mount = Path.of("/mount"); + var hideaway = getHideaway(mount); + + assertEquals(mount.getParent().toAbsolutePath(), Path.of("/").toAbsolutePath()); + assertEquals(mount.getParent(), hideaway.getParent()); + assertEquals(mount.getParent().resolve(".~$mount.tmp"), hideaway); + assertEquals(mount.getParent().toAbsolutePath() + ".~$mount.tmp", hideaway.toAbsolutePath().toString()); + } + + @Test + void testGetHideaway(@TempDir Path parentDir) { + var mount = parentDir.resolve("mount"); + var hideaway = getHideaway(mount); + + assertEquals(mount.getParent(), hideaway.getParent()); + assertEquals(mount.getParent().resolve(".~$mount.tmp"), hideaway); + assertEquals(mount.getParent().toAbsolutePath() + File.separator + ".~$mount.tmp", hideaway.toAbsolutePath().toString()); + } + + private static boolean isHidden(Path path) throws IOException { + return (boolean) Files.getAttribute(path, "dos:hidden", NOFOLLOW_LINKS); + } +} \ No newline at end of file