forked from Writand/writand
feat: Filetree API
This commit is contained in:
parent
8def0b92a8
commit
b1fa2baf76
5 changed files with 221 additions and 18 deletions
|
@ -3,10 +3,10 @@ package be.re.writand
|
|||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
package be.re.writand.data.local
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.NotDirectoryException
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.PriorityQueue
|
||||
import java.util.Queue
|
||||
import kotlin.io.path.deleteExisting
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.name
|
||||
|
||||
class FileManagerLocal : IFileManager {
|
||||
|
||||
/**
|
||||
* Checks if a directory is empty.
|
||||
* Requires API 26!
|
||||
*
|
||||
* @param[path] The path of the directory to be checked.
|
||||
* @return returns true if the directory is empty. If [path] points to a file,
|
||||
* false is immediately returned.
|
||||
*/
|
||||
fun isDirectoryEmpty(path: Path): Boolean {
|
||||
if (!path.isDirectory()) return false
|
||||
return Files.list(path).count() == 0L
|
||||
}
|
||||
|
||||
override suspend fun create(name: String, basePath: Path) {
|
||||
if (!basePath.isDirectory()) throw NotDirectoryException(basePath.name)
|
||||
|
||||
val fullPath = Paths.get(basePath.toString(), name)
|
||||
withContext(Dispatchers.IO) {
|
||||
val res = fullPath.toFile().createNewFile()
|
||||
if (!res) throw FileAlreadyExistsException(
|
||||
fullPath.toFile(),
|
||||
reason = "File already exists."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun delete(path: Path) {
|
||||
// path is a directory and is not empty, delete everything in this directory
|
||||
if (path.isDirectory() && !isDirectoryEmpty(path)) {
|
||||
val queue: Queue<Path> = PriorityQueue()
|
||||
queue.add(path)
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
val top = queue.poll()!!
|
||||
if (top.isDirectory() && !isDirectoryEmpty(top)) {
|
||||
|
||||
// add all the elements of the current directory to the queue
|
||||
for (dir in top.iterator()) {
|
||||
queue.add(dir)
|
||||
}
|
||||
|
||||
// add the directory back into the queue (top is not an empty directory)
|
||||
queue.add(top)
|
||||
} else {
|
||||
|
||||
// top is a file or an empty directory
|
||||
top.deleteExisting()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
path.deleteExisting()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun move(from: Path, to: Path) {
|
||||
withContext(Dispatchers.IO) {
|
||||
Files.move(from, to)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun rename(path: Path, newName: String) {
|
||||
move(path, Paths.get(path.parent.toString(), newName))
|
||||
}
|
||||
|
||||
override suspend fun walk(root: Path): FileTreeWalk {
|
||||
return File(root.toUri()).walkTopDown()
|
||||
}
|
||||
}
|
64
app/src/main/java/be/re/writand/data/local/IFileManager.kt
Normal file
64
app/src/main/java/be/re/writand/data/local/IFileManager.kt
Normal file
|
@ -0,0 +1,64 @@
|
|||
package be.re.writand.data.local
|
||||
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.InvalidPathException
|
||||
import java.nio.file.NotDirectoryException
|
||||
import java.io.IOException
|
||||
import java.io.IOError
|
||||
|
||||
/**
|
||||
* An interface to handle the File trees.
|
||||
* File is used to mention both real files and directories at the same time.
|
||||
*/
|
||||
interface IFileManager {
|
||||
/**
|
||||
* Create a new File.
|
||||
* @param[name] the name of the new File.
|
||||
* @param[basePath] the path where the new File should be created in.
|
||||
* @throws InvalidPathException
|
||||
* @throws FileAlreadyExistsException
|
||||
* @throws NotDirectoryException
|
||||
* @throws IOException
|
||||
* @throws SecurityException
|
||||
* */
|
||||
suspend fun create(name: String, basePath: Path)
|
||||
|
||||
/**
|
||||
* Delete a File.
|
||||
* Symbolic links are followed.
|
||||
* @param[path] the path pointing to the File to be deleted.
|
||||
* @throws NoSuchFileException
|
||||
*/
|
||||
suspend fun delete(path: Path)
|
||||
|
||||
/**
|
||||
* Move a File to a different location.
|
||||
* If the File to be moved is a symbolic link, then the link itself is moved instead of the
|
||||
* target of the link.
|
||||
* @param[from] the start location.
|
||||
* @param[to] the destination.
|
||||
* @throws FileAlreadyExistsException
|
||||
* @throws IOException
|
||||
* @throws SecurityException
|
||||
*/
|
||||
suspend fun move(from: Path, to: Path)
|
||||
|
||||
/**
|
||||
* Rename a file to a new name
|
||||
* @param[path] the path to the File to rename
|
||||
* @param[newName] the new name to be given to [path]
|
||||
* @throws FileAlreadyExistsException
|
||||
* @throws IOException
|
||||
* @throws SecurityException
|
||||
*/
|
||||
suspend fun rename(path: Path, newName: String)
|
||||
|
||||
/**
|
||||
* Walk the directory and build a File tree.
|
||||
* @throws NullPointerException
|
||||
* @throws IllegalArgumentException
|
||||
* @throws IOError
|
||||
* @throws SecurityException
|
||||
*/
|
||||
suspend fun walk(root: Path): FileTreeWalk
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package be.re.writand
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
70
app/src/test/java/be/re/writand/FileManagerLocalTest.kt
Normal file
70
app/src/test/java/be/re/writand/FileManagerLocalTest.kt
Normal file
|
@ -0,0 +1,70 @@
|
|||
package be.re.writand
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import be.re.writand.data.local.FileManagerLocal
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.mockStatic
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.whenever
|
||||
import java.nio.file.FileSystem
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.spi.FileSystemProvider
|
||||
import java.util.stream.Stream
|
||||
import kotlin.io.path.isDirectory
|
||||
|
||||
/**
|
||||
* The class containing all the tests for [FileManagerLocal].
|
||||
*/
|
||||
class FileManagerLocalTest {
|
||||
private lateinit var fileManager: FileManagerLocal
|
||||
|
||||
@Mock
|
||||
private lateinit var path: Path
|
||||
|
||||
@Mock
|
||||
private lateinit var provider: FileSystemProvider
|
||||
|
||||
@Mock
|
||||
private lateinit var fileSystem: FileSystem
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
fileManager = FileManagerLocal()
|
||||
MockitoAnnotations.openMocks(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@SuppressLint("CheckResult")
|
||||
@JvmStatic
|
||||
@BeforeAll
|
||||
fun setupStatic(): Unit {
|
||||
mockStatic(Files::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
@Test
|
||||
fun testIsDirectoryEmptySuccess() {
|
||||
whenever(fileSystem.provider()).thenReturn(provider)
|
||||
whenever(path.fileSystem).thenReturn(fileSystem)
|
||||
whenever(Files.list(any())).thenReturn(Stream.empty())
|
||||
whenever(path.isDirectory()).thenReturn(true)
|
||||
assertTrue(fileManager.isDirectoryEmpty(path))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsDirectoryNotEmptySuccess() {
|
||||
whenever(fileSystem.provider()).thenReturn(provider)
|
||||
whenever(path.fileSystem).thenReturn(fileSystem)
|
||||
whenever(Files.list(any())).thenReturn(Stream.of(path))
|
||||
whenever(path.isDirectory()).thenReturn(true)
|
||||
assertFalse(fileManager.isDirectoryEmpty(path))
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue