summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/local/build.gradle.kts2
-rw-r--r--libs/local/src/test/java/org/the_jk/cleversync/local/LocalTreeTest.kt309
-rw-r--r--libs/samba/build.gradle.kts1
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt36
-rw-r--r--libs/samba/src/test/java/org/the_jk/cleversync/samba/SambaTreeTest.kt176
-rw-r--r--libs/test-utils/build.gradle.kts15
-rw-r--r--libs/test-utils/src/main/java/org/the_jk/cleversync/TreeAbstractTest.kt342
-rw-r--r--libs/utils/build.gradle.kts1
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"))
}