summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2024-08-25 02:30:36 +0200
committerJoel Klinghed <the_jk@spawned.biz>2024-08-25 02:30:36 +0200
commita15cfde0fcbdb29dafdb9ebe39fe53c8da4073be (patch)
treeae7894796099d34381990d5230ebb22be16e897c
parent855a23136973313a656bfaf60afd8b98833a05c0 (diff)
Combine tests from both local and samba
Most the tests test the Tree implementation and thus should work on all such implementations. Current exception is symlinks which Samba backend doesn't (currently?) support. Improve the Samba remove methods to better match the expected behavior.
-rw-r--r--.idea/gradle.xml1
-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
-rw-r--r--settings.gradle.kts1
10 files changed, 456 insertions, 428 deletions
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 965fd30..d80a4e3 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -26,6 +26,7 @@
<option value="$PROJECT_DIR$/libs/io" />
<option value="$PROJECT_DIR$/libs/local" />
<option value="$PROJECT_DIR$/libs/samba" />
+ <option value="$PROJECT_DIR$/libs/test-utils" />
<option value="$PROJECT_DIR$/libs/utils" />
</set>
</option>
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"))
}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index e357a62..10a406b 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -24,4 +24,5 @@ include(":app")
include(":libs:local")
include(":libs:io")
include(":libs:samba")
+include(":libs:test-utils")
include(":libs:utils")