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

jvmTest.okio.internal.ResourceFileSystemTest.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0.0-RC3
Show newest version
/*
 * Copyright (C) 2021 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.internal

import java.net.URL
import java.net.URLClassLoader
import java.util.Enumeration
import kotlin.reflect.KClass
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.fail
import okio.BufferedSource
import okio.ByteString
import okio.FileSystem
import okio.IOException
import okio.Path
import okio.Path.Companion.toPath
import okio.ZipBuilder
import okio.randomToken
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

class ResourceFileSystemTest {
  private val fileSystem = FileSystem.RESOURCES as ResourceFileSystem
  private var base = FileSystem.SYSTEM_TEMPORARY_DIRECTORY / randomToken(16)

  @Test
  fun testResourceA() {
    val path = "okio/resourcefilesystem/a.txt".toPath()

    val metadata = fileSystem.metadataOrNull(path)!!

    assertThat(metadata.size).isEqualTo(1L)
    assertThat(metadata.isRegularFile).isTrue()
    assertThat(metadata.isDirectory).isFalse()

    val content = fileSystem.read(path) { readUtf8() }

    assertThat(content).isEqualTo("a")
  }

  @Test
  fun testResourceB() {
    val path = "okio/resourcefilesystem/b/b.txt".toPath()

    val metadata = fileSystem.metadataOrNull(path)!!

    assertThat(metadata.size).isEqualTo(3L)
    assertThat(metadata.isRegularFile).isTrue()
    assertThat(metadata.isDirectory).isFalse()

    val content = fileSystem.read(path) { readUtf8() }

    assertThat(content).isEqualTo("b/b")
  }

  @Test
  fun testSingleArchive() {
    val zipPath = ZipBuilder(base)
      .addEntry("hello.txt", "Hello World")
      .addEntry("directory/subdirectory/child.txt", "Another file!")
      .addEntry("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n")
      .build()
    val resourceFileSystem = ResourceFileSystem(
      classLoader = URLClassLoader(arrayOf(zipPath.toFile().toURI().toURL()), null),
      indexEagerly = false,
    )

    assertThat(resourceFileSystem.read("hello.txt".toPath()) { readUtf8() })
      .isEqualTo("Hello World")

    assertThat(resourceFileSystem.read("directory/subdirectory/child.txt".toPath()) { readUtf8() })
      .isEqualTo("Another file!")

    assertThat(resourceFileSystem.list("/".toPath()))
      .hasSameElementsAs(listOf("/META-INF".toPath(), "/hello.txt".toPath(), "/directory".toPath()))
    assertThat(resourceFileSystem.list("/directory".toPath()))
      .containsExactly("/directory/subdirectory".toPath())
    assertThat(resourceFileSystem.list("/directory/subdirectory".toPath()))
      .containsExactly("/directory/subdirectory/child.txt".toPath())

    val metadata = resourceFileSystem.metadata(".".toPath())
    assertThat(metadata.isDirectory).isTrue()
  }

  @Test
  fun testDirectoryAndJarOverlap() {
    val filesAPath = base / "filesA"
    FileSystem.SYSTEM.createDirectories(filesAPath / "colors")
    FileSystem.SYSTEM.write(filesAPath / "colors" / "red.txt") { writeUtf8("Apples are red") }
    FileSystem.SYSTEM.write(filesAPath / "colors" / "green.txt") { writeUtf8("Grass is green") }
    val zipBPath = ZipBuilder(base)
      .addEntry("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n")
      .addEntry("colors/blue.txt", "The sky is blue")
      .addEntry("colors/green.txt", "Limes are green")
      .build()

    val resourceFileSystem = ResourceFileSystem(
      classLoader = URLClassLoader(
        arrayOf(
          filesAPath.toFile().toURI().toURL(),
          zipBPath.toFile().toURI().toURL(),
        ),
        null,
      ),
      indexEagerly = false,
    )

    assertThat(resourceFileSystem.read("/colors/red.txt".toPath()) { readUtf8() })
      .isEqualTo("Apples are red")
    assertThat(resourceFileSystem.read("/colors/green.txt".toPath()) { readUtf8() })
      .isEqualTo("Grass is green")
    assertThat(resourceFileSystem.read("/colors/blue.txt".toPath()) { readUtf8() })
      .isEqualTo("The sky is blue")

    assertThat(resourceFileSystem.list("/".toPath()))
      .hasSameElementsAs(listOf("/META-INF".toPath(), "/colors".toPath()))
    assertThat(resourceFileSystem.list("/colors".toPath())).hasSameElementsAs(
      listOf(
        "/colors/red.txt".toPath(),
        "/colors/green.txt".toPath(),
        "/colors/blue.txt".toPath(),
      ),
    )

    assertThat(resourceFileSystem.metadata("/".toPath()).isDirectory).isTrue()
    assertThat(resourceFileSystem.metadata("/colors".toPath()).isDirectory).isTrue()
  }

  @Test
  fun testDirectoryAndDirectoryOverlap() {
    val filesAPath = base / "filesA"
    FileSystem.SYSTEM.createDirectories(filesAPath / "colors")
    FileSystem.SYSTEM.write(filesAPath / "colors" / "red.txt") { writeUtf8("Apples are red") }
    FileSystem.SYSTEM.write(filesAPath / "colors" / "green.txt") { writeUtf8("Grass is green") }
    val filesBPath = base / "filesB"
    FileSystem.SYSTEM.createDirectories(filesBPath / "colors")
    FileSystem.SYSTEM.write(filesBPath / "colors" / "blue.txt") { writeUtf8("The sky is blue") }
    FileSystem.SYSTEM.write(filesBPath / "colors" / "green.txt") { writeUtf8("Limes are green") }

    val resourceFileSystem = ResourceFileSystem(
      classLoader = URLClassLoader(
        arrayOf(
          filesAPath.toFile().toURI().toURL(),
          filesBPath.toFile().toURI().toURL(),
        ),
        null,
      ),
      indexEagerly = false,
    )

    assertThat(resourceFileSystem.read("/colors/red.txt".toPath()) { readUtf8() })
      .isEqualTo("Apples are red")
    assertThat(resourceFileSystem.read("/colors/green.txt".toPath()) { readUtf8() })
      .isEqualTo("Grass is green")
    assertThat(resourceFileSystem.read("/colors/blue.txt".toPath()) { readUtf8() })
      .isEqualTo("The sky is blue")

    assertThat(resourceFileSystem.list("/".toPath()))
      .hasSameElementsAs(listOf("/colors".toPath()))
    assertThat(resourceFileSystem.list("/colors".toPath())).hasSameElementsAs(
      listOf(
        "/colors/red.txt".toPath(),
        "/colors/green.txt".toPath(),
        "/colors/blue.txt".toPath(),
      ),
    )

    assertThat(resourceFileSystem.metadata("/".toPath()).isDirectory).isTrue()
    assertThat(resourceFileSystem.metadata("/colors".toPath()).isDirectory).isTrue()
  }

  @Test
  fun testJarAndJarOverlap() {
    val zipAPath = ZipBuilder(base)
      .addEntry("colors/red.txt", "Apples are red")
      .addEntry("colors/green.txt", "Grass is green")
      .addEntry("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n")
      .build()
    val zipBPath = ZipBuilder(base)
      .addEntry("colors/blue.txt", "The sky is blue")
      .addEntry("colors/green.txt", "Limes are green")
      .addEntry("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n")
      .build()
    val resourceFileSystem = ResourceFileSystem(
      classLoader = URLClassLoader(
        arrayOf(
          zipAPath.toFile().toURI().toURL(),
          zipBPath.toFile().toURI().toURL(),
        ),
        null,
      ),
      indexEagerly = false,
    )

    assertThat(resourceFileSystem.read("/colors/red.txt".toPath()) { readUtf8() })
      .isEqualTo("Apples are red")
    assertThat(resourceFileSystem.read("/colors/green.txt".toPath()) { readUtf8() })
      .isEqualTo("Grass is green")
    assertThat(resourceFileSystem.read("/colors/blue.txt".toPath()) { readUtf8() })
      .isEqualTo("The sky is blue")

    assertThat(resourceFileSystem.list("/".toPath()))
      .hasSameElementsAs(listOf("/META-INF".toPath(), "/colors".toPath()))
    assertThat(resourceFileSystem.list("/colors".toPath())).hasSameElementsAs(
      listOf(
        "/colors/red.txt".toPath(),
        "/colors/green.txt".toPath(),
        "/colors/blue.txt".toPath(),
      ),
    )

    assertThat(resourceFileSystem.metadata("/".toPath()).isDirectory).isTrue()
    assertThat(resourceFileSystem.metadata("/colors".toPath()).isDirectory).isTrue()
  }

  @Test
  fun testResourceMissing() {
    val path = "okio/resourcefilesystem/b/c.txt".toPath()

    assertThat(fileSystem.metadataOrNull(path)).isNull()

    try {
      fileSystem.read(path) { readUtf8() }
      fail()
    } catch (ioe: IOException) {
      assertThat(ioe.message).isEqualTo("file not found: okio/resourcefilesystem/b/c.txt")
    }
  }

  @Test
  fun testProjectIsListable() {
    val path = "okio/resourcefilesystem/b/".toPath()

    val metadata = fileSystem.metadataOrNull(path)!!

    assertThat(metadata.isDirectory).isTrue()
    assertThat(metadata.createdAtMillis).isGreaterThan(1L)

    assertThat(fileSystem.list(path).map { it.name }).containsExactly("b.txt")
  }

  @Test
  fun testResourceFromJar() {
    val path = "LICENSE-junit.txt".toPath()

    val metadata = fileSystem.metadataOrNull(path)!!

    assertThat(metadata.size).isGreaterThan(10000L)
    assertThat(metadata.isRegularFile).isTrue()
    assertThat(metadata.isDirectory).isFalse()

    val content = fileSystem.read(path) { readUtf8Line() }

    assertThat(content).isEqualTo("JUnit")
  }

  @Test
  fun testClassFilesOmittedFromJar() {
    assertThat(fileSystem.list("/org/junit/rules".toPath())).isEmpty()
    assertThat(fileSystem.metadataOrNull("/org/junit/Test.class".toPath())).isNull()
  }

  @Test
  fun testClassFilesOmittedFromDirectory() {
    val filesPath = base / "files"
    val packagePath = filesPath / "com" / "example" / "project"
    FileSystem.SYSTEM.createDirectories(packagePath)
    FileSystem.SYSTEM.write(packagePath / "Hello.class") { writeUtf8("cafebabe") }

    val resourceFileSystem = ResourceFileSystem(
      classLoader = URLClassLoader(
        arrayOf(filesPath.toFile().toURI().toURL()),
        null,
      ),
      indexEagerly = false,
    )

    assertThat(resourceFileSystem.list("/com/example/project".toPath())).isEmpty()
    assertThat(resourceFileSystem.metadataOrNull("/com/example/project/Hello.class".toPath()))
      .isNull()
  }

  @Test
  fun testDirectoryFromJar() {
    val path = "org/junit/".toPath()

    val metadata = fileSystem.metadataOrNull(path)
    assertThat(metadata?.isDirectory).isTrue()

    val files = fileSystem.list(path).map { it.name }
    assertThat(files).contains("matchers", "rules")
    assertThat(files.filter { it.endsWith(".class") }).isEmpty()
  }

  @Test
  fun packagePath() {
    val path = ByteString::class.java.`package`.toPath()

    assertThat((path / "a.txt").toString())
      .isEqualTo("okio${Path.DIRECTORY_SEPARATOR}a.txt")
  }

  @Test
  fun classResource() {
    val path = ByteString::class.packagePath!!

    assertThat((path / "a.txt").toString())
      .isEqualTo("okio${Path.DIRECTORY_SEPARATOR}a.txt")
  }

  /**
   * Confirm that class loaders aren't accessed until the file system is used. This should save
   * resources so that zip files aren't decoded if they're unused.
   */
  @Test
  fun testIndexLazily() {
    val classLoader = object : ClassLoader() {
      override fun findResources(name: String?): Enumeration {
        throw Exception("finding a resource")
      }
    }

    val resourceFileSystem = ResourceFileSystem(
      classLoader = classLoader,
      indexEagerly = false,
    )

    assertThat(
      assertFailsWith {
        resourceFileSystem.list("/".toPath())
      },
    ).hasMessage("finding a resource")
  }

  @Test
  fun testIndexEagerly() {
    val classLoader = object : ClassLoader() {
      override fun findResources(name: String?): Enumeration {
        throw Exception("finding a resource")
      }
    }

    assertThat(
      assertFailsWith {
        ResourceFileSystem(
          classLoader = classLoader,
          indexEagerly = true,
        )
      },
    ).hasMessage("finding a resource")
  }

  /**
   * Our resource file system uses [URLClassLoader] internally, which means we need to go back and
   * forth between [File], [URL], and [URI] models for component paths. This is a big hazard for
   * escaping special characters and it's likely that some file paths won't survive the round trip!
   */
  @Test
  fun fileNameWithSpaceInPath() {
    val zipPath = ZipBuilder(base / "space in directory name")
      .addEntry("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n")
      .addEntry("hello.txt", "Hello World")
      .build()
    val resourceFileSystem = ResourceFileSystem(
      classLoader = URLClassLoader(arrayOf(zipPath.toFile().toURI().toURL()), null),
      indexEagerly = false,
    )

    assertThat(resourceFileSystem.read("hello.txt".toPath()) { readUtf8() })
      .isEqualTo("Hello World")
  }

  @Test
  fun missingResourceSilentlyIgnored() {
    val zipAPath = ZipBuilder(base)
      .addEntry("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n")
      .addEntry("hello.txt", "Hello World")
      .build()
    val resourceFileSystem = ResourceFileSystem(
      classLoader = URLClassLoader(
        arrayOf(
          zipAPath.toFile().toURI().toURL(),
          (base / "missing.zip").toFile().toURI().toURL(),
        ),
        null,
      ),
      indexEagerly = false,
    )

    assertThat(resourceFileSystem.read("hello.txt".toPath()) { readUtf8() })
      .isEqualTo("Hello World")
  }

  @Test fun listSpecialCharacterNamedFiles() {
    val path = "okio/resourcefilesystem/non-ascii".toPath()

    assertThat(fileSystem.listRecursively(path).toList()).containsExactly(
      "/okio/resourcefilesystem/non-ascii/ギリシア神話".toPath(),
      "/okio/resourcefilesystem/non-ascii/ギリシア神話/Ἰλιάς".toPath(),
    )

    val content = fileSystem.read(
      "/okio/resourcefilesystem/non-ascii/ギリシア神話/Ἰλιάς".toPath(),
      BufferedSource::readUtf8,
    )
    assertEquals("Chante, ô déesse, le courroux du Péléide Achille,\n", content)
  }

  private fun Package.toPath(): Path = name.replace(".", "/").toPath()

  private val KClass<*>.packagePath: Path?
    get() = qualifiedName?.replace(".", "/")?.toPath()?.parent
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy