Skip to content

Port ZipArchiveTest #10099

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion compiler/src/dotty/tools/io/ZipArchive.scala
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ abstract class ZipArchive(override val jpath: JPath) extends AbstractFile with E
if (entry.isDirectory) ensureDir(dirs, entry.getName)
else ensureDir(dirs, dirName(entry.getName))
}

def close(): Unit
}
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
Expand Down Expand Up @@ -176,6 +178,7 @@ final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
}
} finally {
if (ZipArchive.closeZipFile) zipFile.close()
else closeables ::= zipFile
}
(root, dirs)
}
Expand All @@ -194,15 +197,24 @@ final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
case x: FileZipArchive => jpath.toAbsolutePath == x.jpath.toAbsolutePath
case _ => false
}

private[this] var closeables: List[java.io.Closeable] = Nil
override def close(): Unit = {
closeables.foreach(_.close)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can simpli

Suggested change
closeables.foreach(_.close)
closeables.foreach(_.close)
closeables = Nil

closeables = Nil
}
}

final class ManifestResources(val url: URL) extends ZipArchive(null) {
def iterator(): Iterator[AbstractFile] = {
val root = new DirEntry("/", null)
val dirs = mutable.HashMap[String, DirEntry]("/" -> root)
val manifest = new Manifest(input)
val stream = input
val manifest = new Manifest(stream)
val iter = manifest.getEntries().keySet().iterator().asScala.filter(_.endsWith(".class")).map(new ZipEntry(_))

closeables ::= stream

for (zipEntry <- iter) {
val dir = getDir(dirs, zipEntry)
if (!zipEntry.isDirectory) {
Expand Down Expand Up @@ -251,4 +263,10 @@ final class ManifestResources(val url: URL) extends ZipArchive(null) {
}
}
}

private[this] var closeables: List[java.io.Closeable] = Nil
override def close(): Unit = {
closeables.foreach(_.close())
closeables = Nil
}
}
137 changes: 137 additions & 0 deletions compiler/test/dotty/tools/io/ZipArchiveTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package dotty.tools.io

import java.io.IOException
import java.net.{URI, URL, URLClassLoader}
import java.nio.file.{Files, Path, Paths}
import java.util.jar.{Attributes, Manifest, JarEntry, JarOutputStream}
import java.lang.invoke.{MethodHandles, MethodType}

import org.junit.Assert._
import org.junit.Test

import scala.util.chaining._
import scala.util.Using

class ZipArchiveTest {

@Test
def corruptZip(): Unit = {
val f = Files.createTempFile("test", ".jar")
val fza = new FileZipArchive(f)
try {
fza.iterator
assert(false)
}
catch {
case ex: IOException =>
}
finally {
Files.delete(f)
}
}

@Test
def missingFile(): Unit = {
val f = Paths.get("xxx.does.not.exist")
val fza = new FileZipArchive(f)
try {
fza.iterator
assert(false)
}
catch {
case ex: IOException =>
}
}

private val bootClassLoader: ClassLoader = {
if (!util.Properties.isJavaAtLeast("9")) null
else {
try {
MethodHandles
.lookup()
.findStatic(
classOf[ClassLoader],
"getPlatformClassLoader",
MethodType.methodType(classOf[ClassLoader])
)
.invoke()
.asInstanceOf[ClassLoader]
} catch {
case _: Throwable =>
null
}
}
}

private def classLoader(location: URI): ClassLoader =
new URLClassLoader(Array(location.toURL), bootClassLoader)

private def manifestAt(location: URI): URL = classLoader(location).getResource("META-INF/MANIFEST.MF")


// ZipArchive.fromManifestURL(URL)
@Test def `manifest resources just works`(): Unit = {
val jar = createTestJar()
val archive = new ManifestResources(manifestAt(jar.toUri))
try {
val it = archive.iterator
assertTrue(it.hasNext)
val f = it.next()
assertFalse(it.hasNext)
assertEquals("foo.class", f.name)
}
finally {
archive.close()
// The following results in IOException on Windows (file in use by another process).
// As jar created with Files.createTempFile, it will be deleted automatically.
try Files.delete(jar) catch case _: IOException => ()
}
}

private def createTestJar(): Path = Files.createTempFile("junit", ".jar").tap { f =>
val man = new Manifest()
man.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0")
man.getEntries().put("foo.class", new Attributes(0))
Using.resource(new JarOutputStream(Files.newOutputStream(f), man)) { jout =>
jout.putNextEntry(new JarEntry("foo.class"))
val bytes = "hello, world".getBytes
jout.write(bytes, 0, bytes.length)
()
}
}

private def createTestZip(): Path = Files.createTempFile("junit", ".zip").tap { f =>
import java.util.zip._
Using.resource(new ZipOutputStream(Files.newOutputStream(f))) { zout =>
zout.setLevel(Deflater.NO_COMPRESSION)
zout.setMethod(ZipOutputStream.STORED)
val entry = new ZipEntry("foo.class")
val bytes = "hello, world".getBytes
entry.setSize(bytes.length)
entry.setCompressedSize(bytes.length)
entry.setCrc(new CRC32().tap(_.update(bytes, 0, bytes.length)).getValue)
zout.putNextEntry(entry)
zout.write(bytes, 0, bytes.length)
zout.closeEntry()
()
}
}
/* zipfs doesn't write size field in file header as required by URLZipArchive
private def createTestZip2(): Path = {
import java.nio.file.FileSystems
import java.net.URI
import scala.util.chaining._
import scala.jdk.CollectionConverters._
val f = Files.createTempFile("junit", ".zip")
Files.delete(f)
val uri = URI.create(s"jar:${f.toUri}")
val env = Map("create" -> "true").asJava
Using.resource(FileSystems.newFileSystem(uri, env)) { fs =>
val path = fs.getPath("foo.class")
val bytes = "hello, world".getBytes
Files.write(path, bytes)
}
f.tap(println(_))
}
*/
}