aboutsummaryrefslogtreecommitdiff
path: root/okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt
diff options
context:
space:
mode:
Diffstat (limited to 'okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt')
-rw-r--r--okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt670
1 files changed, 0 insertions, 670 deletions
diff --git a/okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt b/okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt
deleted file mode 100644
index 669138f5..00000000
--- a/okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt
+++ /dev/null
@@ -1,670 +0,0 @@
-/*
- * Copyright (C) 2020 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package okio
-
-import kotlinx.datetime.Clock
-import kotlinx.datetime.Instant
-import okio.ByteString.Companion.toByteString
-import okio.Path.Companion.toPath
-import okio.fakefilesystem.FakeFileSystem
-import kotlin.random.Random
-import kotlin.test.BeforeTest
-import kotlin.test.Ignore
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-import kotlin.test.assertFalse
-import kotlin.test.assertNull
-import kotlin.test.assertTrue
-import kotlin.time.ExperimentalTime
-import kotlin.time.seconds
-
-/** This test assumes that okio-files/ is the current working directory when executed. */
-@ExperimentalTime
-@ExperimentalFileSystem
-abstract class AbstractFileSystemTest(
- val clock: Clock,
- val fileSystem: FileSystem,
- val windowsLimitations: Boolean,
- temporaryDirectory: Path
-) {
- val base: Path = temporaryDirectory / "${this::class.simpleName}-${randomToken()}"
- private val isJs = fileSystem::class.simpleName?.startsWith("NodeJs") ?: false
-
- @BeforeTest
- fun setUp() {
- fileSystem.createDirectory(base)
- }
-
- @Test
- fun canonicalizeDotReturnsCurrentWorkingDirectory() {
- if (fileSystem is FakeFileSystem || fileSystem is ForwardingFileSystem) return
- val cwd = fileSystem.canonicalize(".".toPath())
- val cwdString = cwd.toString()
- assertTrue(cwdString) {
- cwdString.endsWith("okio${Path.DIRECTORY_SEPARATOR}okio") ||
- cwdString.endsWith("${Path.DIRECTORY_SEPARATOR}okio-parent-okio-test") || // JS
- cwdString.contains("/CoreSimulator/Devices/") || // iOS simulator.
- cwdString == "/" // Android emulator.
- }
- }
-
- @Test
- fun canonicalizeNoSuchFile() {
- assertFailsWith<FileNotFoundException> {
- fileSystem.canonicalize(base / "no-such-file")
- }
- }
-
- @Test
- fun list() {
- val target = base / "list"
- target.writeUtf8("hello, world!")
- val entries = fileSystem.list(base)
- assertTrue(entries.toString()) { target in entries }
- }
-
- @Test
- fun listResultsAreSorted() {
- val fileA = base / "a"
- val fileB = base / "b"
- val fileC = base / "c"
- val fileD = base / "d"
-
- // Create files in a different order than the sorted order, so a file system that returns files
- // in creation-order or reverse-creation order won't pass by accident.
- fileD.writeUtf8("fileD")
- fileB.writeUtf8("fileB")
- fileC.writeUtf8("fileC")
- fileA.writeUtf8("fileA")
-
- val entries = fileSystem.list(base)
- assertEquals(entries, listOf(fileA, fileB, fileC, fileD))
- }
-
- @Test
- fun listNoSuchDirectory() {
- assertFailsWith<FileNotFoundException> {
- fileSystem.list(base / "no-such-directory")
- }
- }
-
- @Test
- fun listFile() {
- val target = base / "list"
- target.writeUtf8("hello, world!")
- assertFailsWith<IOException> {
- fileSystem.list(target)
- }
- }
-
- @Test
- fun fileSourceNoSuchDirectory() {
- assertFailsWith<FileNotFoundException> {
- fileSystem.source(base / "no-such-directory" / "file")
- }
- }
-
- @Test
- fun fileSource() {
- val path = base / "file-source"
- path.writeUtf8("hello, world!")
-
- val source = fileSystem.source(path)
- val buffer = Buffer()
- assertTrue(source.read(buffer, 100L) == 13L)
- assertEquals(-1L, source.read(buffer, 100L))
- assertEquals("hello, world!", buffer.readUtf8())
- source.close()
- }
-
- @Test
- fun readPath() {
- val path = base / "read-path"
- val string = "hello, read with a Path"
- path.writeUtf8(string)
-
- val result = fileSystem.read(path) {
- assertEquals("hello", readUtf8(5))
- assertEquals(", read with ", readUtf8(12))
- assertEquals("a Path", readUtf8())
- return@read "success"
- }
- assertEquals("success", result)
- }
-
- @Test
- fun fileSink() {
- val path = base / "file-sink"
- val sink = fileSystem.sink(path)
- val buffer = Buffer().writeUtf8("hello, world!")
- sink.write(buffer, buffer.size)
- sink.close()
- assertTrue(path in fileSystem.list(base))
- assertEquals(0, buffer.size)
- assertEquals("hello, world!", path.readUtf8())
- }
-
- @Test
- fun writePath() {
- val path = base / "write-path"
- val content = fileSystem.write(path) {
- val string = "hello, write with a Path"
- writeUtf8(string)
- return@write string
- }
- assertTrue(path in fileSystem.list(base))
- assertEquals(content, path.readUtf8())
- }
-
- @Test
- fun appendingSinkAppendsToExistingFile() {
- val path = base / "appending-sink-appends-to-existing-file"
- path.writeUtf8("hello, world!\n")
- val sink = fileSystem.appendingSink(path)
- val buffer = Buffer().writeUtf8("this is added later!")
- sink.write(buffer, buffer.size)
- sink.close()
- assertTrue(path in fileSystem.list(base))
- assertEquals("hello, world!\nthis is added later!", path.readUtf8())
- }
-
- @Test
- fun appendingSinkDoesNotImpactExistingFile() {
- val path = base / "appending-sink-does-not-impact-existing-file"
- path.writeUtf8("hello, world!\n")
- val sink = fileSystem.appendingSink(path)
- assertEquals("hello, world!\n", path.readUtf8())
- sink.close()
- assertEquals("hello, world!\n", path.readUtf8())
- }
-
- @Test
- fun appendingSinkCreatesNewFile() {
- val path = base / "appending-sink-creates-new-file"
- val sink = fileSystem.appendingSink(path)
- val buffer = Buffer().writeUtf8("this is all there is!")
- sink.write(buffer, buffer.size)
- sink.close()
- assertTrue(path in fileSystem.list(base))
- assertEquals("this is all there is!", path.readUtf8())
- }
-
- @Test
- fun fileSinkFlush() {
- val path = base / "file-sink"
- val sink = fileSystem.sink(path)
-
- val buffer = Buffer().writeUtf8("hello,")
- sink.write(buffer, buffer.size)
- sink.flush()
- assertEquals("hello,", path.readUtf8())
-
- buffer.writeUtf8(" world!")
- sink.write(buffer, buffer.size)
- sink.close()
- assertEquals("hello, world!", path.readUtf8())
- }
-
- @Test
- fun fileSinkNoSuchDirectory() {
- assertFailsWith<FileNotFoundException> {
- fileSystem.sink(base / "no-such-directory" / "file")
- }
- }
-
- @Test
- fun createDirectory() {
- val path = base / "create-directory"
- fileSystem.createDirectory(path)
- assertTrue(path in fileSystem.list(base))
- }
-
- @Test
- fun createDirectoryAlreadyExists() {
- val path = base / "already-exists"
- fileSystem.createDirectory(path)
- assertFailsWith<IOException> {
- fileSystem.createDirectory(path)
- }
- }
-
- @Test
- fun createDirectoryParentDirectoryDoesNotExist() {
- val path = base / "no-such-directory" / "created"
- assertFailsWith<IOException> {
- fileSystem.createDirectory(path)
- }
- }
-
- @Test
- fun createDirectoriesSingle() {
- val path = base / "create-directories-single"
- fileSystem.createDirectories(path)
- assertTrue(path in fileSystem.list(base))
- assertTrue(fileSystem.metadata(path).isDirectory)
- }
-
- @Test
- fun createDirectoriesAlreadyExists() {
- val path = base / "already-exists"
- fileSystem.createDirectory(path)
- fileSystem.createDirectories(path)
- assertTrue(fileSystem.metadata(path).isDirectory)
- }
-
- @Test
- fun createDirectoriesParentDirectoryDoesNotExist() {
- fileSystem.createDirectories(base / "a" / "b" / "c")
- assertTrue(base / "a" in fileSystem.list(base))
- assertTrue(base / "a" / "b" in fileSystem.list(base / "a"))
- assertTrue(base / "a" / "b" / "c" in fileSystem.list(base / "a" / "b"))
- assertTrue(fileSystem.metadata(base / "a" / "b" / "c").isDirectory)
- }
-
- @Test
- fun createDirectoriesParentIsFile() {
- val file = base / "simple-file"
- file.writeUtf8("just a file")
- assertFailsWith<IOException> {
- fileSystem.createDirectories(file / "child")
- }
- }
-
- @Test
- fun atomicMoveFile() {
- val source = base / "source"
- source.writeUtf8("hello, world!")
- val target = base / "target"
- fileSystem.atomicMove(source, target)
- assertEquals("hello, world!", target.readUtf8())
- assertTrue(source !in fileSystem.list(base))
- assertTrue(target in fileSystem.list(base))
- }
-
- @Test
- fun atomicMoveDirectory() {
- val source = base / "source"
- fileSystem.createDirectory(source)
- val target = base / "target"
- fileSystem.atomicMove(source, target)
- assertTrue(source !in fileSystem.list(base))
- assertTrue(target in fileSystem.list(base))
- }
-
- @Test
- fun atomicMoveSourceIsTarget() {
- val source = base / "source"
- source.writeUtf8("hello, world!")
- fileSystem.atomicMove(source, source)
- assertEquals("hello, world!", source.readUtf8())
- assertTrue(source in fileSystem.list(base))
- }
-
- @Test
- fun atomicMoveClobberExistingFile() {
- val source = base / "source"
- source.writeUtf8("hello, world!")
- val target = base / "target"
- target.writeUtf8("this file will be clobbered!")
- fileSystem.atomicMove(source, target)
- assertEquals("hello, world!", target.readUtf8())
- assertTrue(source !in fileSystem.list(base))
- assertTrue(target in fileSystem.list(base))
- }
-
- @Test
- fun atomicMoveSourceDoesNotExist() {
- val source = base / "source"
- val target = base / "target"
- assertFailsWith<FileNotFoundException> {
- fileSystem.atomicMove(source, target)
- }
- }
-
- @Test
- fun atomicMoveSourceIsFileAndTargetIsDirectory() {
- val source = base / "source"
- source.writeUtf8("hello, world!")
- val target = base / "target"
- fileSystem.createDirectory(target)
- assertFailsWith<IOException> {
- fileSystem.atomicMove(source, target)
- }
- }
-
- @Test
- fun atomicMoveSourceIsDirectoryAndTargetIsFile() {
- val source = base / "source"
- fileSystem.createDirectory(source)
- val target = base / "target"
- target.writeUtf8("hello, world!")
- expectIOExceptionOnEverythingButWindows {
- fileSystem.atomicMove(source, target)
- }
- }
-
- @Test
- fun copyFile() {
- val source = base / "source"
- source.writeUtf8("hello, world!")
- val target = base / "target"
- fileSystem.copy(source, target)
- assertTrue(target in fileSystem.list(base))
- assertEquals("hello, world!", source.readUtf8())
- assertEquals("hello, world!", target.readUtf8())
- }
-
- @Test
- fun copySourceDoesNotExist() {
- val source = base / "source"
- val target = base / "target"
- assertFailsWith<FileNotFoundException> {
- fileSystem.copy(source, target)
- }
- assertFalse(target in fileSystem.list(base))
- }
-
- @Test
- fun copyTargetIsClobbered() {
- val source = base / "source"
- source.writeUtf8("hello, world!")
- val target = base / "target"
- target.writeUtf8("this file will be clobbered!")
- fileSystem.copy(source, target)
- assertTrue(target in fileSystem.list(base))
- assertEquals("hello, world!", target.readUtf8())
- }
-
- @Test
- fun deleteFile() {
- val path = base / "delete-file"
- path.writeUtf8("delete me")
- fileSystem.delete(path)
- assertTrue(path !in fileSystem.list(base))
- }
-
- @Test
- fun deleteEmptyDirectory() {
- val path = base / "delete-empty-directory"
- fileSystem.createDirectory(path)
- fileSystem.delete(path)
- assertTrue(path !in fileSystem.list(base))
- }
-
- @Test
- fun deleteFailsOnNoSuchFile() {
- val path = base / "no-such-file"
- // TODO(jwilson): fix Windows to throw FileNotFoundException on deleting an absent file.
- if (windowsLimitations) {
- assertFailsWith<IOException> {
- fileSystem.delete(path)
- }
- } else {
- assertFailsWith<FileNotFoundException> {
- fileSystem.delete(path)
- }
- }
- }
-
- @Test
- fun deleteFailsOnNonemptyDirectory() {
- val path = base / "non-empty-directory"
- fileSystem.createDirectory(path)
- (path / "file.txt").writeUtf8("inside directory")
- assertFailsWith<IOException> {
- fileSystem.delete(path)
- }
- }
-
- @Test
- fun deleteRecursivelyFile() {
- val path = base / "delete-recursively-file"
- path.writeUtf8("delete me")
- fileSystem.deleteRecursively(path)
- assertTrue(path !in fileSystem.list(base))
- }
-
- @Test
- fun deleteRecursivelyEmptyDirectory() {
- val path = base / "delete-recursively-empty-directory"
- fileSystem.createDirectory(path)
- fileSystem.deleteRecursively(path)
- assertTrue(path !in fileSystem.list(base))
- }
-
- @Test
- fun deleteRecursivelyFailsOnNoSuchFile() {
- val path = base / "no-such-file"
- assertFailsWith<FileNotFoundException> {
- fileSystem.deleteRecursively(path)
- }
- }
-
- @Test
- fun deleteRecursivelyNonemptyDirectory() {
- val path = base / "delete-recursively-non-empty-directory"
- fileSystem.createDirectory(path)
- (path / "file.txt").writeUtf8("inside directory")
- fileSystem.deleteRecursively(path)
- assertTrue(path !in fileSystem.list(base))
- assertTrue((path / "file.txt") !in fileSystem.list(base))
- }
-
- @Test
- fun deleteRecursivelyDeepHierarchy() {
- fileSystem.createDirectory(base / "a")
- fileSystem.createDirectory(base / "a" / "b")
- fileSystem.createDirectory(base / "a" / "b" / "c")
- (base / "a" / "b" / "c" / "d.txt").writeUtf8("inside deep hierarchy")
- fileSystem.deleteRecursively(base / "a")
- assertEquals(fileSystem.list(base), listOf())
- }
-
- @Test
- fun fileMetadata() {
- val minTime = clock.now().minFileSystemTime()
- val path = base / "file-metadata"
- path.writeUtf8("hello, world!")
- val maxTime = clock.now().maxFileSystemTime()
-
- val metadata = fileSystem.metadata(path)
- assertTrue(metadata.isRegularFile)
- assertFalse(metadata.isDirectory)
- assertEquals(13, metadata.size)
- assertInRange(metadata.createdAt, minTime, maxTime)
- assertInRange(metadata.lastModifiedAt, minTime, maxTime)
- assertInRange(metadata.lastAccessedAt, minTime, maxTime)
- }
-
- @Test
- fun directoryMetadata() {
- val minTime = clock.now().minFileSystemTime()
- val path = base / "directory-metadata"
- fileSystem.createDirectory(path)
- val maxTime = clock.now().maxFileSystemTime()
-
- val metadata = fileSystem.metadata(path)
- assertFalse(metadata.isRegularFile)
- assertTrue(metadata.isDirectory)
- // Note that the size check is omitted; we'd expect null but the JVM returns values like 64.
- assertInRange(metadata.createdAt, minTime, maxTime)
- assertInRange(metadata.lastModifiedAt, minTime, maxTime)
- assertInRange(metadata.lastAccessedAt, minTime, maxTime)
- }
-
- @Test
- fun absentMetadataOrNull() {
- val path = base / "no-such-file"
- assertNull(fileSystem.metadataOrNull(path))
- }
-
- @Test
- @Ignore
- fun inaccessibleMetadata() {
- // TODO(swankjesse): configure a test directory in CI that exists, but that this process doesn't
- // have permission to read metadata of. Perhaps a file in another user's /home directory?
- }
-
- @Test
- fun absentMetadata() {
- val path = base / "no-such-file"
- assertFailsWith<FileNotFoundException> {
- fileSystem.metadata(path)
- }
- }
-
- @Test
- fun fileExists() {
- val path = base / "file-exists"
- assertFalse(fileSystem.exists(path))
- path.writeUtf8("hello, world!")
- assertTrue(fileSystem.exists(path))
- }
-
- @Test
- fun directoryExists() {
- val path = base / "directory-exists"
- assertFalse(fileSystem.exists(path))
- fileSystem.createDirectory(path)
- assertTrue(fileSystem.exists(path))
- }
-
- @Test
- fun deleteOpenForWritingFailsOnWindows() {
- val file = base / "file.txt"
- expectIOExceptionOnWindows(exceptJs = true) {
- fileSystem.sink(file).use {
- fileSystem.delete(file)
- }
- }
- }
-
- @Test
- fun deleteOpenForReadingFailsOnWindows() {
- val file = base / "file.txt"
- file.writeUtf8("abc")
- expectIOExceptionOnWindows(exceptJs = true) {
- fileSystem.source(file).use {
- fileSystem.delete(file)
- }
- }
- }
-
- @Test
- fun renameSourceIsOpenFailsOnWindows() {
- val from = base / "from.txt"
- val to = base / "to.txt"
- from.writeUtf8("source file")
- to.writeUtf8("target file")
- expectIOExceptionOnWindows(exceptJs = true) {
- fileSystem.source(from).use {
- fileSystem.atomicMove(from, to)
- }
- }
- }
-
- @Test
- fun renameTargetIsOpenFailsOnWindows() {
- val from = base / "from.txt"
- val to = base / "to.txt"
- from.writeUtf8("source file")
- to.writeUtf8("target file")
- expectIOExceptionOnWindows {
- fileSystem.source(to).use {
- fileSystem.atomicMove(from, to)
- }
- }
- }
-
- @Test
- fun deleteContentsOfParentOfFileOpenForReadingFailsOnWindows() {
- val parentA = (base / "a")
- fileSystem.createDirectory(parentA)
- val parentAB = parentA / "b"
- fileSystem.createDirectory(parentAB)
- val parentABC = parentAB / "c"
- fileSystem.createDirectory(parentABC)
- val file = parentABC / "file.txt"
- file.writeUtf8("child file")
- expectIOExceptionOnWindows {
- fileSystem.source(file).use {
- fileSystem.delete(file)
- fileSystem.delete(parentABC)
- fileSystem.delete(parentAB)
- fileSystem.delete(parentA)
- }
- }
- }
-
- private fun expectIOExceptionOnWindows(exceptJs: Boolean = false, block: () -> Unit) {
- val expectCrash = windowsLimitations && (!isJs || !exceptJs)
- try {
- block()
- assertFalse(expectCrash)
- } catch (_: IOException) {
- assertTrue(expectCrash)
- }
- }
-
- private fun expectIOExceptionOnEverythingButWindows(block: () -> Unit) {
- try {
- block()
- assertTrue(windowsLimitations)
- } catch (e: IOException) {
- assertFalse(windowsLimitations)
- }
- }
-
- private fun randomToken() = Random.nextBytes(16).toByteString(0, 16).hex()
-
- fun Path.readUtf8(): String {
- return fileSystem.source(this).buffer().use {
- it.readUtf8()
- }
- }
-
- fun Path.writeUtf8(string: String) {
- fileSystem.sink(this).buffer().use {
- it.writeUtf8(string)
- }
- }
-
- /**
- * Returns the earliest file system time that could be recorded for an event occurring at this
- * instant. This truncates fractional seconds because most host file systems do not use precise
- * timestamps for file metadata.
- */
- private fun Instant.minFileSystemTime(): Instant {
- return Instant.fromEpochSeconds(epochSeconds)
- }
-
- /**
- * Returns the latest file system time that could be recorded for an event occurring at this
- * instant. This adds 2 seconds and truncates fractional seconds because file systems may defer
- * assigning the timestamp.
- *
- * https://docs.microsoft.com/en-us/windows/win32/sysinfo/file-times
- */
- private fun Instant.maxFileSystemTime(): Instant {
- return Instant.fromEpochSeconds(plus(2.seconds).epochSeconds)
- }
-
- private fun assertInRange(sampled: Instant?, minTime: Instant, maxTime: Instant) {
- if (sampled == null) return
- assertTrue("expected $sampled in $minTime..$maxTime") { sampled in minTime..maxTime }
- }
-}