diff options
Diffstat (limited to 'app/src')
18 files changed, 5 insertions, 925 deletions
diff --git a/app/src/main/java/org/the_jk/cleversync/LiveDataUtils.kt b/app/src/main/java/org/the_jk/cleversync/LiveDataUtils.kt deleted file mode 100644 index 7f6ab1f..0000000 --- a/app/src/main/java/org/the_jk/cleversync/LiveDataUtils.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.the_jk.cleversync - -import androidx.lifecycle.LiveData -import androidx.lifecycle.Observer - -fun <T> LiveData<T>.safeValue(): T? { - if (this.hasActiveObservers()) - return value - var ret: T? = null - val observer = Observer<T> { value -> ret = value } - this.observeForever(observer) - this.removeObserver(observer) - return ret -} diff --git a/app/src/main/java/org/the_jk/cleversync/StringUtils.kt b/app/src/main/java/org/the_jk/cleversync/StringUtils.kt deleted file mode 100644 index 6adea24..0000000 --- a/app/src/main/java/org/the_jk/cleversync/StringUtils.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.the_jk.cleversync - -object StringUtils { - fun split(input: String, delimiter: Char, keepEmpty: Boolean = true, limit: Int = 0): List<String> { - return buildList { - var offset = 0 - var count = 0 - while (true) { - val next = input.indexOf(delimiter, offset) - if (next == -1) { - if (keepEmpty || offset < input.length) { - if (limit > 0 && count == limit) { - add("${removeLast()}${delimiter}${input.substring(offset)}") - break - } - add(input.substring(offset)) - } - break - } - if (keepEmpty || offset < next) { - if (limit > 0 && count == limit) { - add("${removeLast()}${delimiter}${input.substring(offset)}") - break - } - add(input.substring(offset, next)) - count++ - } - offset = next + 1 - } - } - } -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/Directory.kt b/app/src/main/java/org/the_jk/cleversync/io/Directory.kt deleted file mode 100644 index e653059..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/Directory.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.the_jk.cleversync.io - -import androidx.lifecycle.LiveData - -interface Directory { - val name: String - - fun openDir(name: String): Directory? - fun openFile(name: String): File? - fun openLink(name: String): Link? - - fun list(): Content - fun liveList(): LiveData<Content> - - data class Content( - val directories: List<Directory>, - val files: List<File>, - val links: List<Link>, - ) -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/File.kt b/app/src/main/java/org/the_jk/cleversync/io/File.kt deleted file mode 100644 index 17f142a..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/File.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.the_jk.cleversync.io - -import java.io.InputStream -import java.time.Instant - -interface File { - val name: String - val size: ULong - val lastModified: Instant - - fun read(): InputStream -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/Link.kt b/app/src/main/java/org/the_jk/cleversync/io/Link.kt deleted file mode 100644 index c05f29e..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/Link.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.the_jk.cleversync.io - -interface Link { - val name: String - - fun resolve(): LinkTarget - - sealed class LinkTarget - - data class DirectoryTarget(val directory: Directory): LinkTarget() - data class FileTarget(val file: File): LinkTarget() - data object NoTarget: LinkTarget() -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableDirectory.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableDirectory.kt deleted file mode 100644 index 8bddc2c..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/ModifiableDirectory.kt +++ /dev/null @@ -1,28 +0,0 @@ -package org.the_jk.cleversync.io - -import androidx.lifecycle.LiveData - -interface ModifiableDirectory : Directory { - fun modifiableOpenDir(name: String): ModifiableDirectory? - fun modifiableOpenFile(name: String): ModifiableFile? - fun modifiableOpenLink(name: String): ModifiableLink? - - fun modifiableList(): Content - fun modifiableLiveList(): LiveData<Content> - - fun createDirectory(name: String): ModifiableDirectory - fun createFile(name: String): ModifiableFile - fun createLink(name: String, target: Directory): ModifiableLink - fun createLink(name: String, target: File): ModifiableLink - fun createLink(name: String, target: String): ModifiableLink - - fun removeDirectory(name: String): Boolean - fun removeFile(name: String): Boolean - fun removeLink(name: String): Boolean - - data class Content( - val directories: List<ModifiableDirectory>, - val files: List<ModifiableFile>, - val links: List<ModifiableLink>, - ) -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableFile.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableFile.kt deleted file mode 100644 index 8675dae..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/ModifiableFile.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.the_jk.cleversync.io - -import java.io.OutputStream - -interface ModifiableFile : File { - fun write(): OutputStream -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableLink.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableLink.kt deleted file mode 100644 index 7dd565b..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/ModifiableLink.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.the_jk.cleversync.io - -interface ModifiableLink : Link { - fun modifiableResolve(): ModifiableLinkTarget - - fun target(directory: Directory) - fun target(file: File) - fun target(name: String) - - sealed class ModifiableLinkTarget - - data class ModifiableDirectoryTarget(val directory: ModifiableDirectory): ModifiableLinkTarget() - data class ModifiableFileTarget(val file: ModifiableFile): ModifiableLinkTarget() - data object NoTarget: ModifiableLinkTarget() -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableTree.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableTree.kt deleted file mode 100644 index 383360d..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/ModifiableTree.kt +++ /dev/null @@ -1,3 +0,0 @@ -package org.the_jk.cleversync.io - -interface ModifiableTree : Tree, ModifiableDirectory diff --git a/app/src/main/java/org/the_jk/cleversync/io/Tree.kt b/app/src/main/java/org/the_jk/cleversync/io/Tree.kt deleted file mode 100644 index b6f2d54..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/Tree.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.the_jk.cleversync.io - -import android.content.res.Resources - -interface Tree : Directory { - fun description(resources: Resources): CharSequence -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt b/app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt index d7c22f5..6df2c93 100644 --- a/app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt +++ b/app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt @@ -1,12 +1,14 @@ package org.the_jk.cleversync.io -import org.the_jk.cleversync.io.impl.PathTree +import org.the_jk.cleversync.local.LocalTreeFactory import java.nio.file.Path object TreeFactory { fun localModifiableTree(root: Path): ModifiableTree { - return PathTree(root) + return LocalTreeFactory.modifiableTree(root) } - fun localTree(root: Path): Tree = localModifiableTree(root) + fun localTree(root: Path): Tree { + return LocalTreeFactory.tree(root) + } } diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathDirectory.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathDirectory.kt deleted file mode 100644 index 55bed6a..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/impl/PathDirectory.kt +++ /dev/null @@ -1,188 +0,0 @@ -package org.the_jk.cleversync.io.impl - -import androidx.annotation.AnyThread -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.map -import org.the_jk.cleversync.io.Directory -import org.the_jk.cleversync.io.File -import org.the_jk.cleversync.io.ModifiableDirectory -import org.the_jk.cleversync.io.ModifiableFile -import org.the_jk.cleversync.io.ModifiableLink -import java.nio.file.LinkOption -import java.nio.file.NoSuchFileException -import java.nio.file.Path -import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.createDirectory -import kotlin.io.path.createSymbolicLinkPointingTo -import kotlin.io.path.deleteIfExists -import kotlin.io.path.deleteRecursively -import kotlin.io.path.isDirectory -import kotlin.io.path.isRegularFile -import kotlin.io.path.isSymbolicLink -import kotlin.io.path.listDirectoryEntries -import kotlin.io.path.name -import kotlin.io.path.readSymbolicLink - -@OptIn(ExperimentalPathApi::class) -internal open class PathDirectory( - internal val path: Path, - private val pathWatcher: PathWatcher, -) : ModifiableDirectory { - private val watcher: DirectoryWatcher by lazy { - DirectoryWatcher() - } - - private val modifiableLiveContent: LiveData<ModifiableDirectory.Content> by lazy { - watcher.content - } - - private val liveContent: LiveData<Directory.Content> by lazy { - modifiableLiveContent.map { - Directory.Content( - it.directories, - it.files, - it.links, - ) - } - } - - override fun modifiableOpenDir(name: String): ModifiableDirectory? { - val path = path.resolve(name) - if (path.isDirectory(LinkOption.NOFOLLOW_LINKS)) return PathDirectory(path, pathWatcher) - if (path.isSymbolicLink()) { - val target = path.readSymbolicLink() - if (target.isDirectory()) return PathDirectory(target.toRealPath(), pathWatcher) - } - return null - } - - override fun modifiableOpenFile(name: String): ModifiableFile? { - val path = path.resolve(name) - if (path.isRegularFile(LinkOption.NOFOLLOW_LINKS)) return PathFile(path) - if (path.isSymbolicLink()) { - val target = path.readSymbolicLink() - if (target.isRegularFile()) return PathFile(target.toRealPath()) - } - return null - } - - override fun modifiableOpenLink(name: String): ModifiableLink? { - val path = path.resolve(name) - return if (path.isSymbolicLink()) PathLink(path, pathWatcher) else null - } - - override fun modifiableList() = makeContent(path.listDirectoryEntries()) - override fun modifiableLiveList() = modifiableLiveContent - - override fun createDirectory(name: String): ModifiableDirectory { - val path = path.resolve(name) - return PathDirectory(path.createDirectory(), pathWatcher) - } - - override fun createFile(name: String): ModifiableFile { - val path = path.resolve(name) - return PathFile(path) - } - - override fun createLink(name: String, target: Directory): ModifiableLink { - val path = path.resolve(name) - return PathLink(path.createSymbolicLinkPointingTo((target as PathDirectory).path), pathWatcher) - } - - override fun createLink(name: String, target: File): ModifiableLink { - val path = path.resolve(name) - return PathLink(path.createSymbolicLinkPointingTo((target as PathFile).path), pathWatcher) - } - - override fun createLink(name: String, target: String): ModifiableLink { - val targetPath = path.resolve(target) - val path = path.resolve(name) - return PathLink(path.createSymbolicLinkPointingTo(targetPath), pathWatcher) - } - - override fun removeDirectory(name: String): Boolean { - val path = path.resolve(name) - return if (path.isDirectory(LinkOption.NOFOLLOW_LINKS)) { - path.deleteRecursively() - true - } else false - } - - override fun removeFile(name: String): Boolean { - val path = path.resolve(name) - return path.isRegularFile(LinkOption.NOFOLLOW_LINKS) && path.deleteIfExists() - } - - override fun removeLink(name: String): Boolean { - val path = path.resolve(name) - return path.isSymbolicLink() && path.deleteIfExists() - } - - override val name: String - get() = path.name - - override fun list(): Directory.Content { - val modifiable = modifiableList() - return Directory.Content(modifiable.directories, modifiable.files, modifiable.links) - } - - override fun liveList() = liveContent - - override fun openDir(name: String) = modifiableOpenDir(name) - override fun openFile(name: String) = modifiableOpenFile(name) - override fun openLink(name: String) = modifiableOpenLink(name) - - override fun equals(other: Any?) = other is PathDirectory && other.path == path - override fun hashCode() = path.hashCode() - override fun toString() = path.toString() - - private inner class DirectoryWatcher : PathWatcher.Delegate { - val content: LiveData<ModifiableDirectory.Content> - get() = _content - - @AnyThread - override fun update(added: List<Path>, removed: List<Path>) { - try { - _content.postValue(makeContent(path.listDirectoryEntries())) - } catch (ignored: NoSuchFileException) { - } - } - - private val _content = object : MutableLiveData<ModifiableDirectory.Content>() { - override fun onActive() { - setup() - } - - override fun onInactive() { - clear() - } - } - - private fun setup() { - val entries = path.listDirectoryEntries() - pathWatcher.add(path, this) - _content.value = makeContent(entries) - } - - private fun clear() { - pathWatcher.remove(path) - } - } - - private fun makeContent(entries: List<Path>): ModifiableDirectory.Content { - val directories = mutableListOf<ModifiableDirectory>() - val files = mutableListOf<ModifiableFile>() - val links = mutableListOf<ModifiableLink>() - entries.forEach { - if (it.isDirectory(LinkOption.NOFOLLOW_LINKS)) { - directories.add(PathDirectory(it, pathWatcher)) - } else if (it.isRegularFile(LinkOption.NOFOLLOW_LINKS)) { - files.add(PathFile(it)) - } else if (it.isSymbolicLink()) { - links.add(PathLink(it, pathWatcher)) - } - } - return ModifiableDirectory.Content(directories, files, links) - } -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathFile.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathFile.kt deleted file mode 100644 index 9a8d160..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/impl/PathFile.kt +++ /dev/null @@ -1,66 +0,0 @@ -package org.the_jk.cleversync.io.impl - -import org.the_jk.cleversync.io.ModifiableFile -import java.io.InputStream -import java.io.OutputStream -import java.nio.file.Files -import java.nio.file.LinkOption -import java.nio.file.Path -import java.nio.file.StandardCopyOption -import java.nio.file.StandardOpenOption -import java.time.Instant -import kotlin.io.path.exists -import kotlin.io.path.fileSize -import kotlin.io.path.getLastModifiedTime -import kotlin.io.path.inputStream -import kotlin.io.path.name -import kotlin.io.path.outputStream - -internal class PathFile(internal val path: Path) : ModifiableFile { - override fun write(): OutputStream { - // If file doesn't exist, write to it directly. - if (!path.exists(LinkOption.NOFOLLOW_LINKS)) - return path.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) - - // Otherwise, write to temp file, only overwriting when done. - val tmp = path.parent.resolve(".#" + path.name) - val os = tmp.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) - return object : OutputStream() { - override fun write(value: Int) { - os.write(value) - } - - override fun write(b: ByteArray) { - os.write(b) - } - - override fun write(b: ByteArray, off: Int, len: Int) { - os.write(b, off, len) - } - - override fun flush() { - os.flush() - } - - override fun close() { - os.close() - Files.move(tmp, path, StandardCopyOption.ATOMIC_MOVE) - } - } - } - - override val name: String - get() = path.name - override val size: ULong - get() = path.fileSize().toULong() - override val lastModified: Instant - get() = path.getLastModifiedTime().toInstant() - - override fun read(): InputStream { - return path.inputStream(StandardOpenOption.READ) - } - - override fun equals(other: Any?) = other is PathFile && other.path == path - override fun hashCode() = path.hashCode() - override fun toString() = path.toString() -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathLink.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathLink.kt deleted file mode 100644 index 9ae8b51..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/impl/PathLink.kt +++ /dev/null @@ -1,62 +0,0 @@ -package org.the_jk.cleversync.io.impl - -import org.the_jk.cleversync.io.Directory -import org.the_jk.cleversync.io.File -import org.the_jk.cleversync.io.Link -import org.the_jk.cleversync.io.ModifiableLink -import java.nio.file.Path -import kotlin.io.path.createSymbolicLinkPointingTo -import kotlin.io.path.deleteIfExists -import kotlin.io.path.isDirectory -import kotlin.io.path.isRegularFile -import kotlin.io.path.name -import kotlin.io.path.readSymbolicLink - -internal class PathLink( - private val path: Path, - private val pathWatcher: PathWatcher, -) : ModifiableLink { - override fun modifiableResolve(): ModifiableLink.ModifiableLinkTarget { - val target = path.readSymbolicLink() - return if (target.isDirectory()) { - ModifiableLink.ModifiableDirectoryTarget(PathDirectory(target.toRealPath(), pathWatcher)) - } else if (target.isRegularFile()) { - ModifiableLink.ModifiableFileTarget(PathFile(target.toRealPath())) - } else { - ModifiableLink.NoTarget - } - } - - override fun target(directory: Directory) { - path.deleteIfExists() - path.createSymbolicLinkPointingTo((directory as PathDirectory).path) - } - - override fun target(file: File) { - path.deleteIfExists() - path.createSymbolicLinkPointingTo((file as PathFile).path) - } - - override fun target(name: String) { - path.deleteIfExists() - path.createSymbolicLinkPointingTo(path.parent.resolve(name)) - } - - override val name: String - get() = path.name - - override fun equals(other: Any?) = other is PathLink && other.path == path - override fun hashCode() = path.hashCode() - override fun toString() = path.toString() - - override fun resolve(): Link.LinkTarget { - val target = path.readSymbolicLink() - return if (target.isDirectory()) { - Link.DirectoryTarget(PathDirectory(target.toRealPath(), pathWatcher)) - } else if (target.isRegularFile()) { - Link.FileTarget(PathFile(target.toRealPath())) - } else { - Link.NoTarget - } - } -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathTree.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathTree.kt deleted file mode 100644 index a8a74c5..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/impl/PathTree.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.the_jk.cleversync.io.impl - -import android.content.res.Resources -import org.the_jk.cleversync.R -import org.the_jk.cleversync.io.ModifiableTree -import java.nio.file.Path - -internal class PathTree(root: Path) : PathDirectory(root, PathWatcher()), ModifiableTree { - override fun description(resources: Resources) = resources.getString(R.string.local_directory) -} diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathWatcher.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathWatcher.kt deleted file mode 100644 index 945019a..0000000 --- a/app/src/main/java/org/the_jk/cleversync/io/impl/PathWatcher.kt +++ /dev/null @@ -1,82 +0,0 @@ -package org.the_jk.cleversync.io.impl - -import androidx.annotation.GuardedBy -import androidx.annotation.WorkerThread -import java.nio.file.ClosedWatchServiceException -import java.nio.file.FileSystems -import java.nio.file.Path -import java.nio.file.StandardWatchEventKinds -import java.nio.file.WatchKey -import java.nio.file.WatchService -import java.util.concurrent.Executors - -internal class PathWatcher { - private val executor = Executors.newSingleThreadExecutor() - private var service: WatchService? = null - private val keys = mutableMapOf<Path, WatchKey>() - private val lock = Object() - @GuardedBy("lock") - private val delegates = mutableMapOf<WatchKey, Delegate>() - - fun add(path: Path, delegate: Delegate) { - if (keys.isEmpty()) { - val service = FileSystems.getDefault().newWatchService() - executor.execute{ runner(service) } - this.service = service - } - val key = path.register(service!!, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE) - delegates[key] = delegate - keys[path] = key - } - - fun remove(path: Path) { - val key = keys.remove(path) ?: return - key.cancel() - synchronized(lock) { - delegates.remove(key) - } - if (keys.isEmpty()) { - service?.close() - service = null - } - } - - interface Delegate { - fun update(added: List<Path>, removed: List<Path>) - } - - @WorkerThread - private fun runner(service: WatchService) { - while (true) { - val key: WatchKey - try { - key = service.take() - } catch (ignored: InterruptedException) { - return - } catch (ignored: ClosedWatchServiceException) { - return - } - - val added = mutableListOf<Path>() - val removed = mutableListOf<Path>() - var overflow = false - - for (event in key.pollEvents()) { - when (event.kind()) { - StandardWatchEventKinds.OVERFLOW -> overflow = true - StandardWatchEventKinds.ENTRY_CREATE -> added.add(event.context() as Path) - StandardWatchEventKinds.ENTRY_DELETE -> removed.add(event.context() as Path) - } - } - - if (overflow || added.isNotEmpty() || removed.isNotEmpty()) { - val delegate = synchronized(lock) { - delegates[key] - } - delegate?.update(added, removed) - } - - key.reset() - } - } -} diff --git a/app/src/test/java/org/the_jk/cleversync/StringUtilsTest.kt b/app/src/test/java/org/the_jk/cleversync/StringUtilsTest.kt deleted file mode 100644 index 6a36156..0000000 --- a/app/src/test/java/org/the_jk/cleversync/StringUtilsTest.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.the_jk.cleversync - -import com.google.common.truth.Truth.assertThat -import org.junit.Test - -class StringUtilsTest { - @Test - fun splitEmpty() { - assertThat(StringUtils.split("", '.', keepEmpty = true)).containsExactly("") - assertThat(StringUtils.split("", '.', keepEmpty = false)).isEmpty() - } - - @Test - fun splitSanity() { - assertThat(StringUtils.split("a.bb.a", '.')).containsExactly("a", "bb", "a").inOrder() - assertThat(StringUtils.split(".a.bb.a", '.', keepEmpty = true)).containsExactly("", "a", "bb", "a").inOrder() - assertThat(StringUtils.split(".a.bb.a", '.', keepEmpty = false)).containsExactly("a", "bb", "a").inOrder() - assertThat(StringUtils.split(".a.bb.a.", '.', keepEmpty = true)) - .containsExactly("", "a", "bb", "a", "").inOrder() - assertThat(StringUtils.split(".a.bb.a.", '.', keepEmpty = false)).containsExactly("a", "bb", "a").inOrder() - } - - @Test - fun splitDouble() { - assertThat(StringUtils.split("foo..bar", '.', keepEmpty = true)).containsExactly("foo", "", "bar").inOrder() - assertThat(StringUtils.split("foo..bar", '.', keepEmpty = false)).containsExactly("foo", "bar").inOrder() - } - - @Test - fun splitLimit() { - assertThat(StringUtils.split("a.bb.a", '.', limit = 1)).containsExactly("a.bb.a") - assertThat(StringUtils.split("a.bb.a", '.', limit = 2)).containsExactly("a", "bb.a").inOrder() - assertThat(StringUtils.split("a.bb.a", '.', limit = 3)).containsExactly("a", "bb", "a").inOrder() - assertThat(StringUtils.split("a.bb.a.", '.', limit = 3, keepEmpty = true)) - .containsExactly("a", "bb", "a.").inOrder() - assertThat(StringUtils.split("a.bb.a.", '.', limit = 3, keepEmpty = false)) - .containsExactly("a", "bb", "a").inOrder() - assertThat(StringUtils.split("a.bb.a", '.', limit = 1000)).containsExactly("a", "bb", "a").inOrder() - } -} diff --git a/app/src/test/java/org/the_jk/cleversync/io/LocalTreeTest.kt b/app/src/test/java/org/the_jk/cleversync/io/LocalTreeTest.kt deleted file mode 100644 index 7b83c98..0000000 --- a/app/src/test/java/org/the_jk/cleversync/io/LocalTreeTest.kt +++ /dev/null @@ -1,323 +0,0 @@ -package org.the_jk.cleversync.io - -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.safeValue - -@Config(manifest=Config.NONE) -@RunWith(RobolectricTestRunner::class) -class LocalTreeTest { - @get:Rule - val folder = TemporaryFolder() - - private lateinit var tree: ModifiableTree - - @Before - fun setUp() { - tree = TreeFactory.localModifiableTree(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(4.toULong()) - } - 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() - } -} |
