package org.the_jk.cleversync import com.google.common.truth.Truth.assertThat import org.junit.Assert import org.junit.Assume import org.junit.Test import org.the_jk.cleversync.io.Directory import org.the_jk.cleversync.io.Link import org.the_jk.cleversync.io.ModifiableLink import org.the_jk.cleversync.io.ModifiableTree import java.io.IOException abstract class TreeAbstractTest { protected lateinit var tree: ModifiableTree @Test open fun empty() { val content = tree.list() assertThat(content.directories).isEmpty() assertThat(content.files).isEmpty() assertThat(content.links).isEmpty() } @Test open fun emptyLive() { val liveContent = tree.liveList() onMain { val content = liveContent.safeValue() assertThat(content?.directories).isEmpty() assertThat(content?.files).isEmpty() assertThat(content?.links).isEmpty() } } @Test open fun createDirectory() { val foo = tree.createDirectory("foo") assertThat(foo.name).isEqualTo("foo") val fooContent = foo.list() assertThat(fooContent.directories).isEmpty() assertThat(fooContent.files).isEmpty() assertThat(fooContent.links).isEmpty() val content = tree.list() assertThat(content.directories).contains(foo) assertThat(content.files).isEmpty() assertThat(content.links).isEmpty() } @Test open fun createDirectoryAlreadyExists() { tree.createDirectory("foo") tree.createFile("bar").write().use { it.write(0) } if (supportSymlinks()) { tree.createLink("fum", "does-not-exist") } Assert.assertThrows(IOException::class.java) { tree.createDirectory("foo") } Assert.assertThrows(IOException::class.java) { tree.createDirectory("bar") } if (supportSymlinks()) { Assert.assertThrows(IOException::class.java) { tree.createDirectory("fum") } } } @Test(timeout = 10000) open fun observeCreateDirectory() { val content = tree.liveList() // Only accessed on main thread var dir: Directory? = null onMain { content.observeForever { if (it.directories.size == 1) dir = it.directories[0] } } tree.createDirectory("foo") onMain { while (dir == null) { idle() } assertThat(dir?.name).isEqualTo("foo") } } @Test open fun createFile() { val foo = tree.createFile("foo") // Files are not created until you write to them. assertThat(tree.list().files).isEmpty() foo.write().use { os -> os.write(byteArrayOf(1, 2, 3, 4)) } assertThat(tree.list().files).contains(foo) assertThat(foo.size).isEqualTo(4.toULong()) foo.read().use { assertThat(it.readBytes()).isEqualTo(byteArrayOf(1, 2, 3, 4)) } } @Test open fun createFileAlreadyExists() { tree.createDirectory("foo") tree.createFile("bar").write().use { it.write(0) } if (supportSymlinks()) { tree.createLink("fum", "does-not-exist") } Assert.assertThrows(IOException::class.java) { tree.createFile("foo") } Assert.assertThrows(IOException::class.java) { tree.createFile("bar") } if (supportSymlinks()) { Assert.assertThrows(IOException::class.java) { tree.createFile("fum") } } } @Test open fun overwriteFile() { val foo = tree.createFile("foo") foo.write().use { os -> os.write(byteArrayOf(1, 2, 3, 4)) } foo.write().use { os -> os.write(127) os.write(byteArrayOf(1)) os.write(byteArrayOf(2), 0, 0) } assertThat(foo.size).isEqualTo(2.toULong()) assertThat(tree.list().files).hasSize(1) foo.read().use { assertThat(it.readBytes()).isEqualTo(byteArrayOf(127, 1)) } } @Test open fun removeDir() { tree.createDirectory("foo") assertThat(tree.removeDirectory("foo")).isTrue() assertThat(tree.list().directories).isEmpty() } @Test(timeout = 10000) open fun removeDirLive() { tree.createDirectory("foo") val content = tree.liveList() // Only accessed on main var done = false onMain { content.observeForever { if (it.directories.isEmpty()) done = true } } assertThat(tree.removeDirectory("foo")).isTrue() onMain { while (!done) { idle() } } } @Test open fun createLink() { Assume.assumeTrue(supportSymlinks()) val dir = tree.createDirectory("dir") val file = tree.createFile("file") val link = tree.createLink("link", dir.name) var target = link.resolve() when (target) { is Link.DirectoryTarget -> { assertThat(target.directory).isEqualTo(dir) assertThat(target.path).isEqualTo("dir") } is Link.FileTarget -> Assert.fail() is Link.NoTarget -> Assert.fail() } assertThat(tree.openDir("link")?.name).isAnyOf("dir", "link") link.target(file) target = link.resolve() when (target) { is Link.DirectoryTarget -> Assert.fail() is Link.FileTarget -> Assert.fail() is Link.NoTarget -> assertThat(target.path).isEqualTo("file") } file.write().use { it.write(1) } target = link.resolve() when (target) { is Link.DirectoryTarget -> Assert.fail() is Link.FileTarget -> { assertThat(target.file).isEqualTo(file) assertThat(target.path).isEqualTo("file") } is Link.NoTarget -> Assert.fail() } assertThat(tree.openFile("link")?.name).isAnyOf("file", "link") } @Test open fun createLinkAlreadyExists() { Assume.assumeTrue(supportSymlinks()) tree.createDirectory("foo") tree.createFile("bar").write().use { it.write(0) } tree.createLink("fum", "does-not-exist") Assert.assertThrows(IOException::class.java) { tree.createLink("foo", "test") } Assert.assertThrows(IOException::class.java) { tree.createLink("bar", "test") } Assert.assertThrows(IOException::class.java) { tree.createLink("fum", "test") } } @Test open fun createLinkSubdir() { Assume.assumeTrue(supportSymlinks()) val foo = tree.createDirectory("foo") val bar = foo.createDirectory("bar") val link1 = tree.createLink("link1", "foo/bar") val link2 = tree.createLink("link2", bar) assertThat(link1.resolve()).isEqualTo(link2.resolve()) when (val target = link1.resolve()) { is Link.DirectoryTarget -> { assertThat(target.directory).isEqualTo(bar) assertThat(target.path).isEqualTo("foo/bar") } else -> Assert.fail() } val link3 = foo.createLink("link3", "../link1") when (val target = link3.resolve()) { is Link.DirectoryTarget -> { assertThat(target.directory).isEqualTo(bar) assertThat(target.path).isEqualTo("../link1") } else -> Assert.fail() } } @Test(timeout = 10000) open fun createLiveLink() { Assume.assumeTrue(supportSymlinks()) val content = tree.liveList() // Only accessed on main var link: Link? = null onMain { content.observeForever { if (it.links.size == 1) link = it.links[0] } } val dir = tree.createDirectory("dir") tree.createLink("link", "dir") onMain { while (link == null) { idle() } assertThat((link?.resolve() as Link.DirectoryTarget).directory).isEqualTo( dir ) } } @Test open fun sameDir() { val dir1 = tree.createDirectory("dir") val dir2 = tree.openDir("dir") assertThat(dir1).isEqualTo(dir2) assertThat(dir1.hashCode()).isEqualTo(dir2.hashCode()) assertThat(dir1.toString()).isEqualTo(dir2.toString()) } @Test open fun sameFile() { val file1 = tree.createFile("file") file1.write().use { it.write(127) } val file2 = tree.openFile("file") assertThat(file1).isEqualTo(file2) assertThat(file1.hashCode()).isEqualTo(file2.hashCode()) assertThat(file1.toString()).isEqualTo(file2.toString()) } @Test open fun sameLink() { Assume.assumeTrue(supportSymlinks()) val link1 = tree.createLink("link", "foo") val link2 = tree.openLink("link") assertThat(link1).isEqualTo(link2) assertThat(link1.hashCode()).isEqualTo(link2.hashCode()) assertThat(link1.toString()).isEqualTo(link2.toString()) } @Test open fun removeDirWithContent() { val foo = tree.createDirectory("foo") foo.createDirectory("dir") foo.createFile("file").write().use { it.write(byteArrayOf(1, 2, 3, 4)) } if (supportSymlinks()) { foo.createLink("link", "file") } assertThat(tree.list().directories).hasSize(1) assertThat(tree.removeDirectory("foo")).isTrue() assertThat(tree.list().directories).isEmpty() } @Test open fun removeWrongType() { tree.createDirectory("dir") assertThat(tree.removeFile("dir")).isFalse() assertThat(tree.removeLink("dir")).isFalse() tree.createFile("file").write().use { it.write(byteArrayOf(1, 2, 3, 4)) } assertThat(tree.removeDirectory("file")).isFalse() assertThat(tree.removeLink("file")).isFalse() if (supportSymlinks()) { tree.createLink("link", "doesn't exist") assertThat(tree.removeDirectory("link")).isFalse() assertThat(tree.removeFile("link")).isFalse() } val content = tree.list() assertThat(content.directories).hasSize(1) assertThat(content.files).hasSize(1) if (supportSymlinks()) { assertThat(content.links).hasSize(1) } } @Test open fun removeFile() { tree.createFile("file").write().use { it.write(byteArrayOf(1, 2, 3, 4)) } assertThat(tree.list().files).hasSize(1) assertThat(tree.removeFile("file")).isTrue() assertThat(tree.list().files).isEmpty() } @Test open fun removeLink() { Assume.assumeTrue(supportSymlinks()) val dir = tree.createDirectory("dir") val file = tree.createFile("file") file.write().use { it.write(127) } tree.createLink("link1", dir) tree.createLink("link2", file) assertThat(tree.list().links).hasSize(2) assertThat(tree.removeLink("link1")).isTrue() assertThat(tree.removeLink("link2")).isTrue() assertThat(tree.list().links).isEmpty() } @Test open fun changeLink() { Assume.assumeTrue(supportSymlinks()) val dir = tree.createDirectory("dir") val file = tree.createFile("file") file.write().use { it.write(127) } val link = tree.createLink("link", "doesn't exist") assertThat(link.resolve() is Link.NoTarget).isTrue() link.target(file) assertThat((link.resolve() as Link.FileTarget).file).isEqualTo(file) link.target(dir) assertThat((link.resolve() as Link.DirectoryTarget).directory).isEqualTo(dir) link.target("bad") assertThat(link.resolve() is Link.NoTarget).isTrue() } @Test open fun changeModifiableLink() { Assume.assumeTrue(supportSymlinks()) val dir = tree.createDirectory("dir") val file = tree.createFile("file") file.write().use { it.write(127) } val link = tree.createLink("link", "doesn't exist") assertThat(link.modifiableResolve() is ModifiableLink.NoTarget).isTrue() link.target(file) assertThat((link.modifiableResolve() as ModifiableLink.ModifiableFileTarget).file).isEqualTo(file) link.target(dir) assertThat((link.modifiableResolve() as ModifiableLink.ModifiableDirectoryTarget).directory).isEqualTo(dir) link.target("bad") assertThat(link.modifiableResolve() is ModifiableLink.NoTarget).isTrue() } @Test open fun recursiveLink() { Assume.assumeTrue(supportSymlinks()) val link = tree.createLink("link", "link") assertThat(link.resolve() is Link.NoTarget).isTrue() } @Test open fun names() { assertThat(tree.createDirectory("dir").name).isEqualTo("dir") assertThat(tree.createFile("file").name).isEqualTo("file") if (supportSymlinks()) { assertThat(tree.createLink("link", "file").name).isEqualTo("link") } } @Test open fun openNonExistent() { assertThat(tree.openDir("dir")).isNull() assertThat(tree.openFile("file")).isNull() assertThat(tree.openLink("link")).isNull() } @Test open fun lastModified() { val file = tree.createFile("foo") file.write().use { it.write(1) } val old = file.lastModified file.write().use { it.write(2); it.flush() } val new = file.lastModified assertThat(old.isBefore(new) || old == new).isTrue() } @Test open fun unicodeFilename() { val file = tree.createFile("r\u00E4ksm\u00f6rg\u00E4s") file.write().use { it.write("Delicious".toByteArray()) } val dir = tree.createDirectory("\uD83D\uDCA9") val content = tree.list() assertThat(content.directories).hasSize(1) assertThat(content.directories[0].name).isEqualTo(dir.name) assertThat(content.files).hasSize(1) assertThat(content.files[0].name).isEqualTo(file.name) } @Test open fun openDirInDir() { val foo = tree.createDirectory("foo") val bar = foo.createDirectory("bar") val fooBar = tree.openDir("foo/bar") assertThat(fooBar).isEqualTo(bar) } @Test open fun openFileInDir() { val foo = tree.createDirectory("foo") val bar = foo.createFile("bar") bar.write().use { it.write(1) } val fooBar = tree.openFile("foo/bar") assertThat(fooBar).isEqualTo(bar) } @Test open fun createDirInDir() { val foo = tree.createDirectory("foo") val fooBar = tree.createDirectory("foo/bar") val bar = foo.openDir("bar") assertThat(bar).isEqualTo(fooBar) } @Test open fun createFileInDir() { val foo = tree.createDirectory("foo") val fooBar = tree.createFile("foo/bar") fooBar.write().use { it.write(1) } val bar = foo.openFile("bar") assertThat(bar).isEqualTo(fooBar) } @Test open fun removeDirInDir() { val foo = tree.createDirectory("foo") foo.createDirectory("bar") assertThat(tree.removeDirectory("foo/bar")).isTrue() assertThat(foo.list().directories).isEmpty() } @Test open fun removeFileInDir() { val foo = tree.createDirectory("foo") val bar = foo.createFile("bar") bar.write().use { it.write(1) } assertThat(tree.removeFile("foo/bar")).isTrue() assertThat(foo.list().files).isEmpty() } protected abstract fun supportSymlinks(): Boolean protected abstract fun idle() protected open fun onMain(block: () -> Unit) { block.invoke() } }