Skip to content

Commit 756fe47

Browse files
authored
Merge pull request #10099 from dotty-staging/zip-test
Port ZipArchiveTest
2 parents d09600d + f6fb5d5 commit 756fe47

File tree

2 files changed

+156
-1
lines changed

2 files changed

+156
-1
lines changed

compiler/src/dotty/tools/io/ZipArchive.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ abstract class ZipArchive(override val jpath: JPath) extends AbstractFile with E
108108
if (entry.isDirectory) ensureDir(dirs, entry.getName)
109109
else ensureDir(dirs, dirName(entry.getName))
110110
}
111+
112+
def close(): Unit
111113
}
112114
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
113115
final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
@@ -176,6 +178,7 @@ final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
176178
}
177179
} finally {
178180
if (ZipArchive.closeZipFile) zipFile.close()
181+
else closeables ::= zipFile
179182
}
180183
(root, dirs)
181184
}
@@ -194,15 +197,24 @@ final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
194197
case x: FileZipArchive => jpath.toAbsolutePath == x.jpath.toAbsolutePath
195198
case _ => false
196199
}
200+
201+
private[this] var closeables: List[java.io.Closeable] = Nil
202+
override def close(): Unit = {
203+
closeables.foreach(_.close)
204+
closeables = Nil
205+
}
197206
}
198207

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

216+
closeables ::= stream
217+
206218
for (zipEntry <- iter) {
207219
val dir = getDir(dirs, zipEntry)
208220
if (!zipEntry.isDirectory) {
@@ -251,4 +263,10 @@ final class ManifestResources(val url: URL) extends ZipArchive(null) {
251263
}
252264
}
253265
}
266+
267+
private[this] var closeables: List[java.io.Closeable] = Nil
268+
override def close(): Unit = {
269+
closeables.foreach(_.close())
270+
closeables = Nil
271+
}
254272
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package dotty.tools.io
2+
3+
import java.io.IOException
4+
import java.net.{URI, URL, URLClassLoader}
5+
import java.nio.file.{Files, Path, Paths}
6+
import java.util.jar.{Attributes, Manifest, JarEntry, JarOutputStream}
7+
import java.lang.invoke.{MethodHandles, MethodType}
8+
9+
import org.junit.Assert._
10+
import org.junit.Test
11+
12+
import scala.util.chaining._
13+
import scala.util.Using
14+
15+
class ZipArchiveTest {
16+
17+
@Test
18+
def corruptZip(): Unit = {
19+
val f = Files.createTempFile("test", ".jar")
20+
val fza = new FileZipArchive(f)
21+
try {
22+
fza.iterator
23+
assert(false)
24+
}
25+
catch {
26+
case ex: IOException =>
27+
}
28+
finally {
29+
Files.delete(f)
30+
}
31+
}
32+
33+
@Test
34+
def missingFile(): Unit = {
35+
val f = Paths.get("xxx.does.not.exist")
36+
val fza = new FileZipArchive(f)
37+
try {
38+
fza.iterator
39+
assert(false)
40+
}
41+
catch {
42+
case ex: IOException =>
43+
}
44+
}
45+
46+
private val bootClassLoader: ClassLoader = {
47+
if (!util.Properties.isJavaAtLeast("9")) null
48+
else {
49+
try {
50+
MethodHandles
51+
.lookup()
52+
.findStatic(
53+
classOf[ClassLoader],
54+
"getPlatformClassLoader",
55+
MethodType.methodType(classOf[ClassLoader])
56+
)
57+
.invoke()
58+
.asInstanceOf[ClassLoader]
59+
} catch {
60+
case _: Throwable =>
61+
null
62+
}
63+
}
64+
}
65+
66+
private def classLoader(location: URI): ClassLoader =
67+
new URLClassLoader(Array(location.toURL), bootClassLoader)
68+
69+
private def manifestAt(location: URI): URL = classLoader(location).getResource("META-INF/MANIFEST.MF")
70+
71+
72+
// ZipArchive.fromManifestURL(URL)
73+
@Test def `manifest resources just works`(): Unit = {
74+
val jar = createTestJar()
75+
val archive = new ManifestResources(manifestAt(jar.toUri))
76+
try {
77+
val it = archive.iterator
78+
assertTrue(it.hasNext)
79+
val f = it.next()
80+
assertFalse(it.hasNext)
81+
assertEquals("foo.class", f.name)
82+
}
83+
finally {
84+
archive.close()
85+
// The following results in IOException on Windows (file in use by another process).
86+
// As jar created with Files.createTempFile, it will be deleted automatically.
87+
try Files.delete(jar) catch case _: IOException => ()
88+
}
89+
}
90+
91+
private def createTestJar(): Path = Files.createTempFile("junit", ".jar").tap { f =>
92+
val man = new Manifest()
93+
man.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0")
94+
man.getEntries().put("foo.class", new Attributes(0))
95+
Using.resource(new JarOutputStream(Files.newOutputStream(f), man)) { jout =>
96+
jout.putNextEntry(new JarEntry("foo.class"))
97+
val bytes = "hello, world".getBytes
98+
jout.write(bytes, 0, bytes.length)
99+
()
100+
}
101+
}
102+
103+
private def createTestZip(): Path = Files.createTempFile("junit", ".zip").tap { f =>
104+
import java.util.zip._
105+
Using.resource(new ZipOutputStream(Files.newOutputStream(f))) { zout =>
106+
zout.setLevel(Deflater.NO_COMPRESSION)
107+
zout.setMethod(ZipOutputStream.STORED)
108+
val entry = new ZipEntry("foo.class")
109+
val bytes = "hello, world".getBytes
110+
entry.setSize(bytes.length)
111+
entry.setCompressedSize(bytes.length)
112+
entry.setCrc(new CRC32().tap(_.update(bytes, 0, bytes.length)).getValue)
113+
zout.putNextEntry(entry)
114+
zout.write(bytes, 0, bytes.length)
115+
zout.closeEntry()
116+
()
117+
}
118+
}
119+
/* zipfs doesn't write size field in file header as required by URLZipArchive
120+
private def createTestZip2(): Path = {
121+
import java.nio.file.FileSystems
122+
import java.net.URI
123+
import scala.util.chaining._
124+
import scala.jdk.CollectionConverters._
125+
val f = Files.createTempFile("junit", ".zip")
126+
Files.delete(f)
127+
val uri = URI.create(s"jar:${f.toUri}")
128+
val env = Map("create" -> "true").asJava
129+
Using.resource(FileSystems.newFileSystem(uri, env)) { fs =>
130+
val path = fs.getPath("foo.class")
131+
val bytes = "hello, world".getBytes
132+
Files.write(path, bytes)
133+
}
134+
f.tap(println(_))
135+
}
136+
*/
137+
}

0 commit comments

Comments
 (0)