All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmTest.com.bkahlert.kommons.io.ClassPathFileSystemProviderTest.kt Maven / Gradle / Ivy

There is a newer version: 2.8.0
Show newest version
package com.bkahlert.kommons.io

import com.bkahlert.kommons.test.fixtures.EmojiTextDocumentFixture
import com.bkahlert.kommons.test.testAll
import com.bkahlert.kommons.test.url
import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.assertions.withClue
import io.kotest.inspectors.forAll
import io.kotest.inspectors.forAny
import io.kotest.matchers.collections.shouldEndWith
import io.kotest.matchers.longs.shouldBeGreaterThan
import io.kotest.matchers.nulls.shouldNotBeNull
import io.kotest.matchers.paths.shouldExist
import io.kotest.matchers.should
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.io.IOException
import java.net.URI
import java.nio.channels.NonWritableChannelException
import java.nio.file.AccessMode
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.ReadOnlyFileSystemException
import java.nio.file.attribute.BasicFileAttributeView
import java.nio.file.attribute.BasicFileAttributes
import kotlin.io.path.copyTo
import kotlin.io.path.fileStore
import kotlin.io.path.pathString
import kotlin.io.path.readBytes
import kotlin.io.path.toPath

class ClassPathFileSystemProviderTest {

    private val provider = ClassPathFileSystemProvider()

    @Test fun schema() = testAll {
        ClassPathFileSystemProvider.URI_SCHEME should {
            it shouldBe "classpath"
            it shouldBe provider.scheme
        }
    }

    @Test fun new_file_system() = testAll(
        fileBasedFileClassPathUri,
        jarBasedFileClassPathUri,
        jarBasedClassClassPathUri,
        URI("classPath:$fileBasedFileResource"),
    ) {
        shouldThrow { provider.newFileSystem(it) }
    }

    @Test fun get_file_system() = testAll {
        provider.getFileSystem(fileBasedFileClassPathUri) should { fileSystem ->
            fileSystem shouldBe provider.getPath(fileBasedFileClassPathUri).fileSystem
            fileSystem shouldBe provider.getFileSystem(URI("classPath:$fileBasedFileResource"))
        }

        shouldThrow { provider.getFileSystem(URI("classpath:missing.file")) }
        shouldThrow { provider.getFileSystem(URI("illegal:file")) }
    }

    @Test fun get_path() = testAll {
        withClue("file-based file") {
            provider.getPath(fileBasedFileClassPathUri) should { path ->
                path.fileSystem.provider().scheme shouldBe "file"
                path.shouldExist()
                path shouldBe fileBasedFileUrl.toPath()
                path.readBytes() shouldBe fileBasedFileBytes
            }
        }

        withClue("jar-based file") {
            provider.getPath(jarBasedFileClassPathUri) should { path ->
                path.fileSystem.provider().scheme shouldBe "jar"
                path.shouldExist()
                path shouldBe jarBasedFileUrl.toPath()
                path.readBytes() shouldBe jarBasedFileBytes
            }
        }

        withClue("jar-based class") {
            provider.getPath(jarBasedClassClassPathUri) should { path ->
                path.fileSystem.provider().scheme shouldBe "jar"
                path.shouldExist()
                path shouldBe jarBasedClassUrl.toPath()
                path.readBytes() shouldBe jarBasedClassBytes
            }
        }

        withClue("ignore scheme case") {
            provider.getPath(URI("classPath:$fileBasedFileResource")) shouldBe provider.getPath(fileBasedFileClassPathUri)
        }

        shouldThrow { provider.getPath(URI("classpath:missing.file")) }
        shouldThrow { provider.getPath(URI("illegal:file")) }
    }

    @Test fun paths_get() = testAll(
        fileBasedFileClassPathUri,
        jarBasedFileClassPathUri,
        jarBasedClassClassPathUri,
    ) { uri: URI ->
        Paths.get(uri) should { path ->
            path shouldBe provider.getPath(uri)
            path shouldBe uri.toPath()
        }
    }

    @Test fun new_byte_channel() = testAll {
        listOf(
            fileBasedFileClassPathUri to fileBasedFileBytes,
            jarBasedFileClassPathUri to jarBasedFileBytes,
            jarBasedClassClassPathUri to jarBasedClassBytes,
        ).forAll { (uri: URI, bytes) ->
            provider.newByteChannel(uri.toPath()).readBytes() shouldBe bytes
        }

        shouldThrow {
            provider.newByteChannel(jarBasedFileClassPathUri.toPath()).newOutputStream().write(jarBasedFileBytes)
        }

        shouldThrow { provider.newByteChannel(UnsupportedPath) }
            .message shouldBe "URI scheme unsupported is not supported. Only paths returned by getPath are supported."
    }


    @Test fun new_directory_stream() = testAll {
        listOf(
            fileBasedFileClassPathUri,
            jarBasedFileClassPathUri,
            jarBasedClassClassPathUri,
        ).forAll { uri: URI ->
            val path = uri.toPath()
            provider.newDirectoryStream(path.parent).toList().forAny { entry ->
                entry.shouldEndWith(path.fileName)
            }
        }

        shouldThrow { provider.newDirectoryStream(UnsupportedPath) }
            .message shouldBe "URI scheme unsupported is not supported. Only paths returned by getPath are supported."
    }

    @Test fun create_directory() = testAll {
        shouldThrow { provider.createDirectory(jarBasedFileClassPathUri.toPath()) }
        shouldThrow { provider.createDirectory(UnsupportedPath) }
    }

    @Test fun delete() = testAll {
        shouldThrow { provider.delete(jarBasedFileClassPathUri.toPath()) }
        shouldThrow { provider.delete(UnsupportedPath) }
    }

    @Test fun copy(@TempDir tempDir: Path) = testAll {
        listOf(
            fileBasedFileClassPathUri to fileBasedFileBytes,
            jarBasedFileClassPathUri to jarBasedFileBytes,
            jarBasedClassClassPathUri to jarBasedClassBytes,
        ).forAll { (uri: URI, bytes) ->
            val source = uri.toPath()
            val target = tempDir.resolve(source.fileName.pathString)
            provider.copy(source, target)
            target.readBytes() shouldBe bytes
        }

        shouldThrow { provider.copy(UnsupportedPath, UnsupportedPath) }
            .message shouldBe "URI scheme unsupported is not supported. Only paths returned by getPath are supported."
    }

    @Test fun move() = testAll {
        shouldThrow { provider.move(jarBasedFileClassPathUri.toPath(), UnsupportedPath) }
        shouldThrow { provider.move(UnsupportedPath, UnsupportedPath) }
    }

    @Test fun is_same_file() = testAll {
        listOf(
            fileBasedFileClassPathUri,
            jarBasedFileClassPathUri,
            jarBasedClassClassPathUri,
        ).forAll { uri: URI ->
            provider.isSameFile(uri.toPath(), uri.toPath()) shouldBe true
            provider.isSameFile(uri.toPath(), UnsupportedPath) shouldBe false
        }

        shouldThrow { provider.isSameFile(UnsupportedPath, UnsupportedPath) }
            .message shouldBe "URI scheme unsupported is not supported. Only paths returned by getPath are supported."
    }

    @Test fun is_hidden() = testAll {
        listOf(
            fileBasedFileClassPathUri,
            jarBasedFileClassPathUri,
            jarBasedClassClassPathUri,
        ).forAll { uri: URI ->
            provider.isHidden(uri.toPath()) shouldBe false
        }

        shouldThrow { provider.isHidden(UnsupportedPath) }
            .message shouldBe "URI scheme unsupported is not supported. Only paths returned by getPath are supported."
    }

    @Test fun get_file_store() = testAll {
        provider.getFileStore(jarBasedFileClassPathUri.toPath()).type() shouldBe jarBasedFileClassPathUri.toPath().fileStore().type()
        shouldThrow { provider.getFileStore(UnsupportedPath) }
    }

    @Test fun check_access() = testAll {
        listOf(
            fileBasedFileClassPathUri,
            jarBasedFileClassPathUri,
            jarBasedClassClassPathUri,
        ).forAll { uri: URI ->
            shouldNotThrowAny { provider.checkAccess(uri.toPath()) }
            shouldThrow { provider.checkAccess(uri.toPath(), AccessMode.WRITE) }
        }

        shouldThrow { provider.checkAccess(UnsupportedPath) }
            .message shouldBe "URI scheme unsupported is not supported. Only paths returned by getPath are supported."
        shouldThrow { provider.checkAccess(UnsupportedPath, AccessMode.WRITE) }
            .message shouldBe "URI scheme unsupported is not supported. Only paths returned by getPath are supported."
    }

    @Test fun get_file_attribute_view() = testAll {
        listOf(
            fileBasedFileClassPathUri,
            jarBasedFileClassPathUri,
            jarBasedClassClassPathUri,
        ).forAll { uri: URI ->
            val view = provider.getFileAttributeView(uri.toPath(), BasicFileAttributeView::class).shouldNotBeNull()
            view.readAttributes().size() shouldBeGreaterThan 0L
        }

        shouldThrow { provider.getFileAttributeView(UnsupportedPath, BasicFileAttributeView::class) }
            .message shouldBe "URI scheme unsupported is not supported. Only paths returned by getPath are supported."
    }

    @Test fun read_attributes() = testAll {
        listOf(
            fileBasedFileClassPathUri,
            jarBasedFileClassPathUri,
            jarBasedClassClassPathUri,
        ).forAll { uri: URI ->
            provider.readAttributes(uri.toPath(), BasicFileAttributes::class).size() shouldBeGreaterThan 0L
            provider.readAttributes(uri.toPath(), "*")["size"].shouldBeInstanceOf() shouldBeGreaterThan 0L
        }

        shouldThrow { provider.readAttributes(UnsupportedPath, BasicFileAttributes::class) }
            .message shouldBe "URI scheme unsupported is not supported. Only paths returned by getPath are supported."
        shouldThrow { provider.readAttributes(UnsupportedPath, "*") }
            .message shouldBe "URI scheme unsupported is not supported. Only paths returned by getPath are supported."
    }

    @Test fun set_attribute() = testAll {
        shouldThrow { provider.setAttribute(jarBasedFileClassPathUri.toPath(), "custom", true) }
        shouldThrow { provider.setAttribute(UnsupportedPath, "custom", true) }
    }


    @Test fun class_path() = testAll {
        listOf(
            fileBasedFileResource,
            jarBasedFileResource,
            jarBasedClassResource,
        ).forAll { resource: String ->
            ClassPath(resource) should {
                it.shouldExist()
                it shouldBe ClassPath("classpath:$resource")
                it shouldBe Paths.get(URI("classpath:$resource"))
            }
        }

        shouldThrow { ClassPath("invalid.file") }
    }


    @Test fun manually_copy_class_path(@TempDir tempDir: Path) = testAll {
        ClassPath(fileBasedFileResource).useBufferedInputStream {
            tempDir.resolve("classPathTextFile-streamed-copy").useOutputStream { out -> it.copyTo(out) }
        }.readBytes() shouldBe fileBasedFileBytes

        ClassPath(jarBasedClassResource).useBufferedInputStream {
            tempDir.resolve("standardLibraryClassPathClass-streamed-copy").useOutputStream { out -> it.copyTo(out) }
        }.readBytes() shouldBe jarBasedClassBytes
    }

    @Test fun kotlin_copy_class_path(@TempDir tempDir: Path) = testAll {
        ClassPath(fileBasedFileResource).copyTo(tempDir.resolve("classPathTextFile-kotlin-copy")) should {
            it.pathString shouldBe tempDir.resolve("classPathTextFile-kotlin-copy").pathString
            it.readBytes() shouldBe fileBasedFileBytes
        }

        ClassPath(jarBasedClassResource).copyTo(tempDir.resolve("standardLibraryClassPathClass-kotlin-copy")) should {
            it.pathString shouldBe tempDir.resolve("standardLibraryClassPathClass-kotlin-copy").pathString
            it.readBytes() shouldBe jarBasedClassBytes
        }
    }

    @Test fun kotlin_copy_class_path_to_directory(@TempDir tempDir: Path) = testAll {
        ClassPath(fileBasedFileResource).copyToDirectory(tempDir) should {
            it.pathString shouldBe tempDir.resolve(fileBasedFileResource).pathString
            it.readBytes() shouldBe fileBasedFileBytes
        }

        ClassPath(jarBasedClassResource).copyToDirectory(tempDir) should {
            it.pathString shouldBe tempDir.resolve("Regex.class").pathString
            it.readBytes() shouldBe jarBasedClassBytes
        }
    }

    @Test fun kotlin_copy_class_path_to_directory__multiple(@TempDir tempDir: Path) = testAll {
        for (i in 0..20) {
            ClassPath(fileBasedFileResource).copyTo(tempDir.resolve(fileBasedFileResource + i)) should {
                it.pathString shouldBe tempDir.resolve(fileBasedFileResource + i).pathString
                it.readBytes() shouldBe fileBasedFileBytes
            }

            ClassPath(jarBasedClassResource).copyTo(tempDir.resolve("Regex.class$i")) should {
                it.pathString shouldBe tempDir.resolve("Regex.class$i").pathString
                it.readBytes() shouldBe jarBasedClassBytes
            }
        }
    }
}

internal const val fileBasedFileResource = "61C285F09D95930D0AE298B00AF09F918B0A.txt"
internal val fileBasedFileClassPathUri = URI("classpath:$fileBasedFileResource")
internal val fileBasedFileUrl = checkNotNull(Thread.currentThread().contextClassLoader.getResource(fileBasedFileResource)) {
    "unable to locate $fileBasedFileResource"
}
internal val fileBasedFileBytes = fileBasedFileUrl.readBytes()

internal val jarBasedFileResource = "fixtures/${EmojiTextDocumentFixture.name}"
internal val jarBasedFileClassPathUri = URI("classpath:$jarBasedFileResource")
internal val jarBasedFileUrl = EmojiTextDocumentFixture.url
internal val jarBasedFileBytes = jarBasedFileUrl.readBytes()

internal const val jarBasedClassResource = "kotlin.text.Regex.class"
internal val jarBasedClassClassPathUri = URI("classpath:$jarBasedClassResource")
internal val jarBasedClassUrl = checkNotNull(Regex::class.java.getResource("Regex.class")) {
    "unable to locate $jarBasedClassResource"
}
internal val jarBasedClassBytes = jarBasedClassUrl.readBytes()




© 2015 - 2024 Weber Informatics LLC | Privacy Policy