diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/local/build.gradle.kts | 2 | ||||
| -rw-r--r-- | libs/local/src/test/java/org/the_jk/cleversync/local/LocalTreeTest.kt | 309 | ||||
| -rw-r--r-- | libs/samba/build.gradle.kts | 1 | ||||
| -rw-r--r-- | libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt | 36 | ||||
| -rw-r--r-- | libs/samba/src/test/java/org/the_jk/cleversync/samba/SambaTreeTest.kt | 176 | ||||
| -rw-r--r-- | libs/test-utils/build.gradle.kts | 15 | ||||
| -rw-r--r-- | libs/test-utils/src/main/java/org/the_jk/cleversync/TreeAbstractTest.kt | 342 | ||||
| -rw-r--r-- | libs/utils/build.gradle.kts | 1 |
8 files changed, 454 insertions, 428 deletions
diff --git a/libs/local/build.gradle.kts b/libs/local/build.gradle.kts index 46d8128..6a126d0 100644 --- a/libs/local/build.gradle.kts +++ b/libs/local/build.gradle.kts @@ -8,5 +8,5 @@ android { dependencies { implementation(project(":libs:io")) - testImplementation(project(":libs:utils")) + testImplementation(project(":libs:test-utils")) } diff --git a/libs/local/src/test/java/org/the_jk/cleversync/local/LocalTreeTest.kt b/libs/local/src/test/java/org/the_jk/cleversync/local/LocalTreeTest.kt index a7d0afa..e2d4264 100644 --- a/libs/local/src/test/java/org/the_jk/cleversync/local/LocalTreeTest.kt +++ b/libs/local/src/test/java/org/the_jk/cleversync/local/LocalTreeTest.kt @@ -1,326 +1,23 @@ package org.the_jk.cleversync.local -import com.google.common.truth.Truth.assertThat -import org.junit.Assert import org.junit.Before import org.junit.Rule -import org.junit.Test import org.junit.rules.TemporaryFolder import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import org.robolectric.shadows.ShadowLooper -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 org.the_jk.cleversync.safeValue +import org.the_jk.cleversync.TreeAbstractTest @Config(manifest=Config.NONE) @RunWith(RobolectricTestRunner::class) -class LocalTreeTest { +class LocalTreeTest : TreeAbstractTest() { @get:Rule val folder = TemporaryFolder() - private lateinit var tree: ModifiableTree - @Before fun setUp() { tree = LocalTreeFactory.modifiableTree(folder.root.toPath()) } - @Test - fun empty() { - val content = tree.list() - assertThat(content.directories).isEmpty() - assertThat(content.files).isEmpty() - assertThat(content.links).isEmpty() - } - - @Test - fun emptyLive() { - val content = tree.liveList().safeValue() - assertThat(content?.directories).isEmpty() - assertThat(content?.files).isEmpty() - assertThat(content?.links).isEmpty() - } - - @Test - 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 - fun observeCreateDirectory() { - val content = tree.liveList() - var dir: Directory? = null - content.observeForever { - if (it.directories.size == 1) dir = it.directories[0] - } - tree.createDirectory("foo") - while (dir == null) { - ShadowLooper.idleMainLooper() - } - assertThat(dir?.name).isEqualTo("foo") - } - - @Test - 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 - 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 - fun removeDir() { - tree.createDirectory("foo") - tree.removeDirectory("foo") - assertThat(tree.list().directories).isEmpty() - } - - @Test - fun removeDirLive() { - tree.createDirectory("foo") - val content = tree.liveList() - var done = false - content.observeForever { - if (it.directories.isEmpty()) done = true - } - tree.removeDirectory("foo") - while (!done) { - ShadowLooper.idleMainLooper() - } - } - - @Test - fun createLink() { - 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 - ) - is Link.FileTarget -> Assert.fail() - is Link.NoTarget -> Assert.fail() - } - assertThat(tree.openDir("link")).isEqualTo(dir) - - link.target(file) - target = link.resolve() - when (target) { - is Link.DirectoryTarget -> Assert.fail() - is Link.FileTarget -> Assert.fail() - is Link.NoTarget -> Unit - } - file.write().use { it.write(1) } - target = link.resolve() - when (target) { - is Link.DirectoryTarget -> Assert.fail() - is Link.FileTarget -> assertThat(target.file).isEqualTo(file) - is Link.NoTarget -> Assert.fail() - } - - assertThat(tree.openFile("link")).isEqualTo(file) - } - - @Test - fun createLinkSubdir() { - 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()) - assertThat((link1.resolve() as Link.DirectoryTarget).directory).isEqualTo(bar) - val link3 = foo.createLink("link3", "../link1") - assertThat((link3.resolve() as Link.DirectoryTarget).directory).isEqualTo(bar) - } - - @Test - fun createLiveLink() { - val content = tree.liveList() - var link: Link? = null - content.observeForever { - if (it.links.size == 1) link = it.links[0] - } - val dir = tree.createDirectory("dir") - tree.createLink("link", "dir") - while (link == null) { - ShadowLooper.idleMainLooper() - } - assertThat((link?.resolve() as Link.DirectoryTarget).directory).isEqualTo(dir) - } - - @Test - 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 - 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 - fun sameLink() { - 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 - fun removeDirWithContent() { - val foo = tree.createDirectory("foo") - foo.createDirectory("dir") - foo.createFile("file").write().use { it.write(byteArrayOf(1, 2, 3, 4)) } - foo.createLink("link", "file") - assertThat(tree.list().directories).hasSize(1) - assertThat(tree.removeDirectory("foo")).isTrue() - assertThat(tree.list().directories).isEmpty() - } - - @Test - 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() - 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) - assertThat(content.links).hasSize(1) - } - - @Test - fun removeFile() { - tree.createFile("file").write().use { it.write(byteArrayOf(1, 2, 3, 4)) } - assertThat(tree.list().files).hasSize(1) - tree.removeFile("file") - assertThat(tree.list().files).isEmpty() - } - - @Test - fun removeLink() { - 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) - tree.removeLink("link1") - tree.removeLink("link2") - assertThat(tree.list().links).isEmpty() - } - - @Test - fun changeLink() { - 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 - fun changeModifiableLink() { - 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 - fun recursiveLink() { - val link = tree.createLink("link", "link") - assertThat(link.resolve() is Link.NoTarget).isTrue() - } - - @Test - fun names() { - assertThat(tree.createDirectory("dir").name).isEqualTo("dir") - assertThat(tree.createFile("file").name).isEqualTo("file") - assertThat(tree.createLink("link", "file").name).isEqualTo("link") - } - - @Test - fun openNonExistent() { - assertThat(tree.openDir("dir")).isNull() - assertThat(tree.openFile("file")).isNull() - assertThat(tree.openLink("link")).isNull() - } - - @Test - 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() - } + override fun supportSymlinks() = true } diff --git a/libs/samba/build.gradle.kts b/libs/samba/build.gradle.kts index 7fab391..bae1ec9 100644 --- a/libs/samba/build.gradle.kts +++ b/libs/samba/build.gradle.kts @@ -47,6 +47,7 @@ android { dependencies { implementation(project(":libs:io")) testImplementation(project(":libs:utils")) + testImplementation(project(":libs:test-utils")) } listOf("Debug", "Release").forEach { buildType -> diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt index f5230b2..fc5b290 100644 --- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt +++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt @@ -126,15 +126,45 @@ internal open class SambaDirectory( } override fun removeDirectory(name: String): Boolean { - return conn.removeDir(SambaConnection.join(path, name)) + val removePath = SambaConnection.join(path, name) + val entry = conn.entry(removePath) ?: return false + if (entry.type != NativeSamba.DirEntryType.DIR) return false + return removeRecursive(removePath) + } + + private fun removeRecursive(removePath: String): Boolean { + val dir = conn.openDir(removePath) ?: return false + try { + dir.list().forEach { entry -> + val entryPath = SambaConnection.join(removePath, entry.name) + if (!when (entry.type) { + NativeSamba.DirEntryType.FILE, + NativeSamba.DirEntryType.LINK, + -> conn.unlink(entryPath) + NativeSamba.DirEntryType.DIR + -> removeRecursive(entryPath) + }) { + return false + } + } + return conn.removeDir(removePath) + } finally { + dir.destroy() + } } override fun removeFile(name: String): Boolean { - return conn.unlink(SambaConnection.join(path, name)) + val removePath = SambaConnection.join(path, name) + val entry = conn.entry(removePath) ?: return false + if (entry.type != NativeSamba.DirEntryType.FILE) return false + return conn.unlink(removePath) } override fun removeLink(name: String): Boolean { - return conn.unlink(SambaConnection.join(path, name)) + val removePath = SambaConnection.join(path, name) + val entry = conn.entry(removePath) ?: return false + if (entry.type != NativeSamba.DirEntryType.LINK) return false + return conn.unlink(removePath) } override fun openDir(name: String) = modifiableOpenDir(name) diff --git a/libs/samba/src/test/java/org/the_jk/cleversync/samba/SambaTreeTest.kt b/libs/samba/src/test/java/org/the_jk/cleversync/samba/SambaTreeTest.kt index 61c8da7..fecd746 100644 --- a/libs/samba/src/test/java/org/the_jk/cleversync/samba/SambaTreeTest.kt +++ b/libs/samba/src/test/java/org/the_jk/cleversync/samba/SambaTreeTest.kt @@ -12,9 +12,8 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.shadows.ShadowLooper -import org.the_jk.cleversync.io.Directory +import org.the_jk.cleversync.TreeAbstractTest import org.the_jk.cleversync.io.samba.SambaCredentials -import org.the_jk.cleversync.safeValue import java.io.File import java.nio.charset.StandardCharsets import java.nio.file.Files @@ -22,14 +21,18 @@ import java.util.concurrent.TimeUnit @Config(manifest=Config.NONE) @RunWith(RobolectricTestRunner::class) -class SambaTreeTest { +class SambaTreeTest : TreeAbstractTest() { @Before fun setUpTest() { assertThat(shareDir.listFiles()).isEmpty() + + tree = SambaTreeFactory.modifiableTree(uri, credentials).getOrThrow() } @After fun tearDownTest() { + tree.close() + for (file in shareDir.listFiles()!!) { if (file.isDirectory) { file.deleteRecursively() @@ -48,156 +51,93 @@ class SambaTreeTest { } @Test - fun listEmptyRoot() { - SambaTreeFactory.tree(uri, credentials).getOrThrow().use { root -> - val content = root.list() - assertThat(content.directories).isEmpty() - assertThat(content.files).isEmpty() - assertThat(content.links).isEmpty() - } - } - - @Test - fun listEmptyRootLive() { - SambaTreeFactory.tree(uri, credentials).getOrThrow().use { root -> - val content = root.liveList().safeValue() - assertThat(content?.directories).isEmpty() - assertThat(content?.files).isEmpty() - assertThat(content?.links).isEmpty() - } - } - - @Test fun listRootWithSymlink() { File(shareDir, "dir").mkdir() File(shareDir, "file").writeText("foo") Files.createSymbolicLink(File(shareDir, "link").toPath(), File("file").toPath()) - SambaTreeFactory.tree(uri, credentials).getOrThrow().use { root -> - val content = root.list() + val content = tree.list() - assertThat(content.directories).hasSize(1) - assertThat(content.directories[0].name).isEqualTo("dir") - assertThat(content.files).hasSize(2) - if (content.files[0].name == "file") { - assertThat(content.files[0].name).isEqualTo("file") - assertThat(content.files[0].size).isEqualTo(3UL) - assertThat(content.files[1].name).isEqualTo("link") - assertThat(content.files[1].size).isEqualTo(3UL) - } else { - assertThat(content.files[0].name).isEqualTo("link") - assertThat(content.files[0].size).isEqualTo(3UL) - assertThat(content.files[1].name).isEqualTo("file") - assertThat(content.files[1].size).isEqualTo(3UL) - } - // libsmb uses SMB2/SMB3 and unix extensions are SMB1, so no symlinks for now - assertThat(content.links).isEmpty() + assertThat(content.directories).hasSize(1) + assertThat(content.directories[0].name).isEqualTo("dir") + assertThat(content.files).hasSize(2) + if (content.files[0].name == "file") { + assertThat(content.files[0].name).isEqualTo("file") + assertThat(content.files[0].size).isEqualTo(3UL) + assertThat(content.files[1].name).isEqualTo("link") + assertThat(content.files[1].size).isEqualTo(3UL) + } else { + assertThat(content.files[0].name).isEqualTo("link") + assertThat(content.files[0].size).isEqualTo(3UL) + assertThat(content.files[1].name).isEqualTo("file") + assertThat(content.files[1].size).isEqualTo(3UL) } + // libsmb uses SMB2/SMB3 and unix extensions are SMB1, so no symlinks for now + assertThat(content.links).isEmpty() } @Test - fun readFile() { + fun readExistingFile() { File(shareDir, "file").writeText("hello world") - SambaTreeFactory.tree(uri, credentials).getOrThrow().use { root -> - val file = root.openFile("file") - assertThat(file?.name).isEqualTo("file") - assertThat(file?.size).isEqualTo(11UL) + val file = tree.openFile("file") + assertThat(file?.name).isEqualTo("file") + assertThat(file?.size).isEqualTo(11UL) - file?.read().use { input -> - assertThat(input?.readAllBytes()?.toString(StandardCharsets.UTF_8)).isEqualTo("hello world") - } + file?.read().use { input -> + assertThat(input?.readAllBytes()?.toString(StandardCharsets.UTF_8)).isEqualTo("hello world") + } - file?.read().use { input -> - assertThat(input?.available()).isEqualTo(11) - assertThat(input?.markSupported()).isTrue() - val buffer = ByteArray(10) - assertThat(input?.read(buffer, 5, 5)).isEqualTo(5) - input?.mark(100) - assertThat(buffer.sliceArray(5..<10).toString(StandardCharsets.UTF_8)).isEqualTo("hello") - assertThat(input?.read(buffer)).isEqualTo(6) - assertThat(buffer.sliceArray(0..<6).toString(StandardCharsets.UTF_8)).isEqualTo(" world") - input?.reset() - assertThat(input?.read(buffer, 3, 5)).isEqualTo(5) - assertThat(buffer.sliceArray(3..<8).toString(StandardCharsets.UTF_8)).isEqualTo(" worl") - } + file?.read().use { input -> + assertThat(input?.available()).isEqualTo(11) + assertThat(input?.markSupported()).isTrue() + val buffer = ByteArray(10) + assertThat(input?.read(buffer, 5, 5)).isEqualTo(5) + input?.mark(100) + assertThat(buffer.sliceArray(5..<10).toString(StandardCharsets.UTF_8)).isEqualTo("hello") + assertThat(input?.read(buffer)).isEqualTo(6) + assertThat(buffer.sliceArray(0..<6).toString(StandardCharsets.UTF_8)).isEqualTo(" world") + input?.reset() + assertThat(input?.read(buffer, 3, 5)).isEqualTo(5) + assertThat(buffer.sliceArray(3..<8).toString(StandardCharsets.UTF_8)).isEqualTo(" worl") } } @Test - fun writeFile() { - SambaTreeFactory.modifiableTree(uri, credentials).getOrThrow().use { root -> - val file = root.createFile("file") - assertThat(file.name).isEqualTo("file") - - file.write().writer().use { output -> - output.write("hello world") - } - - assertThat(file.size).isEqualTo(11UL) - } + override fun createFile() { + super.createFile() - assertThat(File(shareDir, "file").readText()).isEqualTo("hello world") + assertThat(File(shareDir, "foo").readBytes()).isEqualTo(byteArrayOf(1, 2, 3, 4)) } @Test - fun overwriteFile() { - File(shareDir, "file").writeText("hello world") - - SambaTreeFactory.modifiableTree(uri, credentials).getOrThrow().use { root -> - val file = root.modifiableOpenFile("file") - assertThat(file?.name).isEqualTo("file") - assertThat(file?.size).isEqualTo(11UL) - - file?.write().use { output -> - val buffer = "foobar".toByteArray(StandardCharsets.UTF_8) - output?.write(buffer, 0, 1) - output?.write(buffer, 1, 2) - output?.write(buffer, 3, 3) - } - - assertThat(file?.size).isEqualTo(6UL) - } + override fun overwriteFile() { + super.overwriteFile() - assertThat(File(shareDir, "file").readText()).isEqualTo("foobar") + assertThat(File(shareDir, "foo").readBytes()).isEqualTo(byteArrayOf(127, 1)) } @Test - fun createDirectory() { - SambaTreeFactory.modifiableTree(uri, credentials).getOrThrow().use { root -> - val foo = root.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 = root.list() - assertThat(content.directories).contains(foo) - assertThat(content.files).isEmpty() - assertThat(content.links).isEmpty() - } + override fun createDirectory() { + super.createDirectory() assertThat(File(shareDir, "foo").isDirectory).isTrue() } @Test(timeout = 10000) - fun observeCreateDirectory() { - SambaTreeFactory.modifiableTree(uri, credentials).getOrThrow().use { root -> - val content = root.liveList() - var dir: Directory? = null - content.observeForever { - if (it.directories.size == 1) dir = it.directories[0] - } - root.createDirectory("foo") - while (dir == null) { - ShadowLooper.idleMainLooper(10, TimeUnit.SECONDS) - } - assertThat(dir?.name).isEqualTo("foo") - } + override fun observeCreateDirectory() { + super.observeCreateDirectory() assertThat(File(shareDir, "foo").isDirectory).isTrue() } + // libsmb uses SMB2/SMB3 and unix extensions are SMB1, so no symlinks for now + override fun supportSymlinks() = false + + override fun idle() { + ShadowLooper.idleMainLooper(10, TimeUnit.SECONDS) + } + companion object { private lateinit var uri: String private lateinit var credentials: SambaCredentials diff --git a/libs/test-utils/build.gradle.kts b/libs/test-utils/build.gradle.kts new file mode 100644 index 0000000..1b796fa --- /dev/null +++ b/libs/test-utils/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + alias(libs.plugins.android.library) +} + +android { + namespace = "org.the_jk.cleversync.testutils" +} + +dependencies { + implementation(project(":libs:io")) + implementation(libs.junit) + implementation(libs.robolectric) + implementation(libs.truth) + implementation(project(":libs:utils")) +} diff --git a/libs/test-utils/src/main/java/org/the_jk/cleversync/TreeAbstractTest.kt b/libs/test-utils/src/main/java/org/the_jk/cleversync/TreeAbstractTest.kt new file mode 100644 index 0000000..b513503 --- /dev/null +++ b/libs/test-utils/src/main/java/org/the_jk/cleversync/TreeAbstractTest.kt @@ -0,0 +1,342 @@ +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.robolectric.shadows.ShadowLooper +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 + +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 content = tree.liveList().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(timeout = 10000) + open fun observeCreateDirectory() { + val content = tree.liveList() + var dir: Directory? = null + content.observeForever { + if (it.directories.size == 1) dir = it.directories[0] + } + tree.createDirectory("foo") + 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 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") + tree.removeDirectory("foo") + assertThat(tree.list().directories).isEmpty() + } + + @Test(timeout = 10000) + open fun removeDirLive() { + tree.createDirectory("foo") + val content = tree.liveList() + var done = false + content.observeForever { + if (it.directories.isEmpty()) done = true + } + tree.removeDirectory("foo") + 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 + ) + is Link.FileTarget -> Assert.fail() + is Link.NoTarget -> Assert.fail() + } + assertThat(tree.openDir("link")).isEqualTo(dir) + + link.target(file) + target = link.resolve() + when (target) { + is Link.DirectoryTarget -> Assert.fail() + is Link.FileTarget -> Assert.fail() + is Link.NoTarget -> Unit + } + file.write().use { it.write(1) } + target = link.resolve() + when (target) { + is Link.DirectoryTarget -> Assert.fail() + is Link.FileTarget -> assertThat(target.file).isEqualTo(file) + is Link.NoTarget -> Assert.fail() + } + + assertThat(tree.openFile("link")).isEqualTo(file) + } + + @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()) + assertThat((link1.resolve() as Link.DirectoryTarget).directory) + .isEqualTo(bar) + val link3 = foo.createLink("link3", "../link1") + assertThat((link3.resolve() as Link.DirectoryTarget).directory) + .isEqualTo(bar) + } + + @Test(timeout = 10000) + open fun createLiveLink() { + Assume.assumeTrue(supportSymlinks()) + + val content = tree.liveList() + var link: Link? = null + content.observeForever { + if (it.links.size == 1) link = it.links[0] + } + val dir = tree.createDirectory("dir") + tree.createLink("link", "dir") + 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) + tree.removeFile("file") + 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) + tree.removeLink("link1") + tree.removeLink("link2") + 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() + } + + protected abstract fun supportSymlinks(): Boolean + + protected open fun idle() { + ShadowLooper.idleMainLooper() + } +} diff --git a/libs/utils/build.gradle.kts b/libs/utils/build.gradle.kts index b4f0ae5..693a327 100644 --- a/libs/utils/build.gradle.kts +++ b/libs/utils/build.gradle.kts @@ -9,4 +9,5 @@ android { dependencies { api(libs.androidx.livedata) api(libs.androidx.livedata.ktx) + testImplementation(project(":libs:io")) } |
