1ccf23a609c01ce08a79f56353a1750f1067749c6cushon/* 2ccf23a609c01ce08a79f56353a1750f1067749c6cushon * Copyright 2017 Google Inc. All Rights Reserved. 3ccf23a609c01ce08a79f56353a1750f1067749c6cushon * 4ccf23a609c01ce08a79f56353a1750f1067749c6cushon * Licensed under the Apache License, Version 2.0 (the "License"); 5ccf23a609c01ce08a79f56353a1750f1067749c6cushon * you may not use this file except in compliance with the License. 6ccf23a609c01ce08a79f56353a1750f1067749c6cushon * You may obtain a copy of the License at 7ccf23a609c01ce08a79f56353a1750f1067749c6cushon * 8ccf23a609c01ce08a79f56353a1750f1067749c6cushon * http://www.apache.org/licenses/LICENSE-2.0 9ccf23a609c01ce08a79f56353a1750f1067749c6cushon * 10ccf23a609c01ce08a79f56353a1750f1067749c6cushon * Unless required by applicable law or agreed to in writing, software 11ccf23a609c01ce08a79f56353a1750f1067749c6cushon * distributed under the License is distributed on an "AS IS" BASIS, 12ccf23a609c01ce08a79f56353a1750f1067749c6cushon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ccf23a609c01ce08a79f56353a1750f1067749c6cushon * See the License for the specific language governing permissions and 14ccf23a609c01ce08a79f56353a1750f1067749c6cushon * limitations under the License. 15ccf23a609c01ce08a79f56353a1750f1067749c6cushon */ 16ccf23a609c01ce08a79f56353a1750f1067749c6cushon 17ccf23a609c01ce08a79f56353a1750f1067749c6cushonpackage com.google.turbine.zip; 18ccf23a609c01ce08a79f56353a1750f1067749c6cushon 19ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport static com.google.common.truth.Truth.assertThat; 20ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport static java.nio.charset.StandardCharsets.UTF_8; 2186665ea565aca9132d09988cd8de50920c814f47cushonimport static org.junit.Assert.fail; 22ccf23a609c01ce08a79f56353a1750f1067749c6cushon 232c831a8dd79cdc77293246b007dbddd55091b571cushonimport com.google.common.collect.ImmutableMap; 24ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport com.google.common.hash.Hashing; 25ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport com.google.common.io.ByteStreams; 26ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport java.io.IOException; 272c831a8dd79cdc77293246b007dbddd55091b571cushonimport java.net.URI; 282c831a8dd79cdc77293246b007dbddd55091b571cushonimport java.nio.file.FileSystem; 292c831a8dd79cdc77293246b007dbddd55091b571cushonimport java.nio.file.FileSystems; 30ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport java.nio.file.Files; 31ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport java.nio.file.Path; 3286665ea565aca9132d09988cd8de50920c814f47cushonimport java.nio.file.StandardOpenOption; 332c831a8dd79cdc77293246b007dbddd55091b571cushonimport java.nio.file.attribute.FileTime; 34ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport java.util.Enumeration; 35ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport java.util.LinkedHashMap; 36ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport java.util.Map; 37ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport java.util.jar.JarEntry; 38ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport java.util.jar.JarFile; 39ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport java.util.jar.JarOutputStream; 4086665ea565aca9132d09988cd8de50920c814f47cushonimport java.util.zip.ZipException; 4186665ea565aca9132d09988cd8de50920c814f47cushonimport java.util.zip.ZipOutputStream; 42ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport org.junit.Rule; 43ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport org.junit.Test; 44ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport org.junit.rules.TemporaryFolder; 45ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport org.junit.runner.RunWith; 46ccf23a609c01ce08a79f56353a1750f1067749c6cushonimport org.junit.runners.JUnit4; 47ccf23a609c01ce08a79f56353a1750f1067749c6cushon 48ccf23a609c01ce08a79f56353a1750f1067749c6cushon/** {@link Zip}Test */ 49ccf23a609c01ce08a79f56353a1750f1067749c6cushon@RunWith(JUnit4.class) 50ccf23a609c01ce08a79f56353a1750f1067749c6cushonpublic class ZipTest { 51ccf23a609c01ce08a79f56353a1750f1067749c6cushon 52ccf23a609c01ce08a79f56353a1750f1067749c6cushon @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); 53ccf23a609c01ce08a79f56353a1750f1067749c6cushon 54ccf23a609c01ce08a79f56353a1750f1067749c6cushon @Test 55ccf23a609c01ce08a79f56353a1750f1067749c6cushon public void testEntries() throws IOException { 56ccf23a609c01ce08a79f56353a1750f1067749c6cushon testEntries(1000); 57ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 58ccf23a609c01ce08a79f56353a1750f1067749c6cushon 59ccf23a609c01ce08a79f56353a1750f1067749c6cushon @Test 60ccf23a609c01ce08a79f56353a1750f1067749c6cushon public void zip64_testEntries() throws IOException { 61ccf23a609c01ce08a79f56353a1750f1067749c6cushon testEntries(70000); 62ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 63ccf23a609c01ce08a79f56353a1750f1067749c6cushon 64ccf23a609c01ce08a79f56353a1750f1067749c6cushon @Test 65ccf23a609c01ce08a79f56353a1750f1067749c6cushon public void compression() throws IOException { 66ccf23a609c01ce08a79f56353a1750f1067749c6cushon Path path = temporaryFolder.newFile("test.jar").toPath(); 67ccf23a609c01ce08a79f56353a1750f1067749c6cushon try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(path))) { 68ccf23a609c01ce08a79f56353a1750f1067749c6cushon for (int i = 0; i < 2; i++) { 69ccf23a609c01ce08a79f56353a1750f1067749c6cushon String name = "entry" + i; 70ccf23a609c01ce08a79f56353a1750f1067749c6cushon byte[] bytes = name.getBytes(UTF_8); 71ccf23a609c01ce08a79f56353a1750f1067749c6cushon jos.putNextEntry(new JarEntry(name)); 72ccf23a609c01ce08a79f56353a1750f1067749c6cushon jos.write(bytes); 73ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 74ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 75ccf23a609c01ce08a79f56353a1750f1067749c6cushon assertThat(actual(path)).isEqualTo(expected(path)); 76ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 77ccf23a609c01ce08a79f56353a1750f1067749c6cushon 78ccf23a609c01ce08a79f56353a1750f1067749c6cushon private void testEntries(int entries) throws IOException { 79ccf23a609c01ce08a79f56353a1750f1067749c6cushon Path path = temporaryFolder.newFile("test.jar").toPath(); 80ccf23a609c01ce08a79f56353a1750f1067749c6cushon try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(path))) { 81ccf23a609c01ce08a79f56353a1750f1067749c6cushon for (int i = 0; i < entries; i++) { 82ccf23a609c01ce08a79f56353a1750f1067749c6cushon String name = "entry" + i; 83ccf23a609c01ce08a79f56353a1750f1067749c6cushon byte[] bytes = name.getBytes(UTF_8); 84ccf23a609c01ce08a79f56353a1750f1067749c6cushon createEntry(jos, name, bytes); 85ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 86ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 87ccf23a609c01ce08a79f56353a1750f1067749c6cushon assertThat(actual(path)).isEqualTo(expected(path)); 88ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 89ccf23a609c01ce08a79f56353a1750f1067749c6cushon 9086665ea565aca9132d09988cd8de50920c814f47cushon private static void createEntry(ZipOutputStream jos, String name, byte[] bytes) 91ccf23a609c01ce08a79f56353a1750f1067749c6cushon throws IOException { 92ccf23a609c01ce08a79f56353a1750f1067749c6cushon JarEntry je = new JarEntry(name); 93ccf23a609c01ce08a79f56353a1750f1067749c6cushon je.setMethod(JarEntry.STORED); 94ccf23a609c01ce08a79f56353a1750f1067749c6cushon je.setSize(bytes.length); 95ccf23a609c01ce08a79f56353a1750f1067749c6cushon je.setCrc(Hashing.crc32().hashBytes(bytes).padToLong()); 96ccf23a609c01ce08a79f56353a1750f1067749c6cushon jos.putNextEntry(je); 97ccf23a609c01ce08a79f56353a1750f1067749c6cushon jos.write(bytes); 98ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 99ccf23a609c01ce08a79f56353a1750f1067749c6cushon 100ccf23a609c01ce08a79f56353a1750f1067749c6cushon private static Map<String, Long> actual(Path path) throws IOException { 101ccf23a609c01ce08a79f56353a1750f1067749c6cushon Map<String, Long> result = new LinkedHashMap<>(); 102ccf23a609c01ce08a79f56353a1750f1067749c6cushon for (Zip.Entry e : new Zip.ZipIterable(path)) { 103ccf23a609c01ce08a79f56353a1750f1067749c6cushon result.put(e.name(), Hashing.goodFastHash(128).hashBytes(e.data()).padToLong()); 104ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 105ccf23a609c01ce08a79f56353a1750f1067749c6cushon return result; 106ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 107ccf23a609c01ce08a79f56353a1750f1067749c6cushon 108ccf23a609c01ce08a79f56353a1750f1067749c6cushon private static Map<String, Long> expected(Path path) throws IOException { 109ccf23a609c01ce08a79f56353a1750f1067749c6cushon Map<String, Long> result = new LinkedHashMap<>(); 110ccf23a609c01ce08a79f56353a1750f1067749c6cushon try (JarFile jf = new JarFile(path.toFile())) { 111ccf23a609c01ce08a79f56353a1750f1067749c6cushon Enumeration<JarEntry> entries = jf.entries(); 112ccf23a609c01ce08a79f56353a1750f1067749c6cushon while (entries.hasMoreElements()) { 113ccf23a609c01ce08a79f56353a1750f1067749c6cushon JarEntry je = entries.nextElement(); 114ccf23a609c01ce08a79f56353a1750f1067749c6cushon result.put( 115ccf23a609c01ce08a79f56353a1750f1067749c6cushon je.getName(), 116ccf23a609c01ce08a79f56353a1750f1067749c6cushon Hashing.goodFastHash(128) 117ccf23a609c01ce08a79f56353a1750f1067749c6cushon .hashBytes(ByteStreams.toByteArray(jf.getInputStream(je))) 118ccf23a609c01ce08a79f56353a1750f1067749c6cushon .padToLong()); 119ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 120ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 121ccf23a609c01ce08a79f56353a1750f1067749c6cushon return result; 122ccf23a609c01ce08a79f56353a1750f1067749c6cushon } 1232c831a8dd79cdc77293246b007dbddd55091b571cushon 1242c831a8dd79cdc77293246b007dbddd55091b571cushon @Test 1252c831a8dd79cdc77293246b007dbddd55091b571cushon public void attributes() throws Exception { 1262c831a8dd79cdc77293246b007dbddd55091b571cushon Path path = temporaryFolder.newFile("test.jar").toPath(); 1272c831a8dd79cdc77293246b007dbddd55091b571cushon Files.delete(path); 1282c831a8dd79cdc77293246b007dbddd55091b571cushon try (FileSystem fs = 1292c831a8dd79cdc77293246b007dbddd55091b571cushon FileSystems.newFileSystem( 1302c831a8dd79cdc77293246b007dbddd55091b571cushon URI.create("jar:file:" + path.toAbsolutePath()), ImmutableMap.of("create", "true"))) { 1312c831a8dd79cdc77293246b007dbddd55091b571cushon for (int i = 0; i < 3; i++) { 1322c831a8dd79cdc77293246b007dbddd55091b571cushon String name = "entry" + i; 1332c831a8dd79cdc77293246b007dbddd55091b571cushon byte[] bytes = name.getBytes(UTF_8); 1342c831a8dd79cdc77293246b007dbddd55091b571cushon Path entry = fs.getPath(name); 1352c831a8dd79cdc77293246b007dbddd55091b571cushon Files.write(entry, bytes); 1362c831a8dd79cdc77293246b007dbddd55091b571cushon Files.setLastModifiedTime(entry, FileTime.fromMillis(0)); 1372c831a8dd79cdc77293246b007dbddd55091b571cushon } 1382c831a8dd79cdc77293246b007dbddd55091b571cushon } 1392c831a8dd79cdc77293246b007dbddd55091b571cushon assertThat(actual(path)).isEqualTo(expected(path)); 1402c831a8dd79cdc77293246b007dbddd55091b571cushon } 14186665ea565aca9132d09988cd8de50920c814f47cushon 14286665ea565aca9132d09988cd8de50920c814f47cushon @Test 14386665ea565aca9132d09988cd8de50920c814f47cushon public void zipFileCommentsAreSupported() throws Exception { 14486665ea565aca9132d09988cd8de50920c814f47cushon Path path = temporaryFolder.newFile("test.jar").toPath(); 14586665ea565aca9132d09988cd8de50920c814f47cushon Files.delete(path); 14686665ea565aca9132d09988cd8de50920c814f47cushon try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(path))) { 14786665ea565aca9132d09988cd8de50920c814f47cushon createEntry(zos, "hello", "world".getBytes(UTF_8)); 14886665ea565aca9132d09988cd8de50920c814f47cushon zos.setComment("this is a comment"); 14986665ea565aca9132d09988cd8de50920c814f47cushon } 15086665ea565aca9132d09988cd8de50920c814f47cushon assertThat(actual(path)).isEqualTo(expected(path)); 15186665ea565aca9132d09988cd8de50920c814f47cushon } 15286665ea565aca9132d09988cd8de50920c814f47cushon 15386665ea565aca9132d09988cd8de50920c814f47cushon @Test 15486665ea565aca9132d09988cd8de50920c814f47cushon public void malformedComment() throws Exception { 15586665ea565aca9132d09988cd8de50920c814f47cushon Path path = temporaryFolder.newFile("test.jar").toPath(); 15686665ea565aca9132d09988cd8de50920c814f47cushon Files.delete(path); 15786665ea565aca9132d09988cd8de50920c814f47cushon 15886665ea565aca9132d09988cd8de50920c814f47cushon try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(path))) { 15986665ea565aca9132d09988cd8de50920c814f47cushon createEntry(zos, "hello", "world".getBytes(UTF_8)); 16086665ea565aca9132d09988cd8de50920c814f47cushon zos.setComment("this is a comment"); 16186665ea565aca9132d09988cd8de50920c814f47cushon } 16286665ea565aca9132d09988cd8de50920c814f47cushon Files.write(path, "trailing garbage".getBytes(UTF_8), StandardOpenOption.APPEND); 16386665ea565aca9132d09988cd8de50920c814f47cushon 16486665ea565aca9132d09988cd8de50920c814f47cushon try { 16586665ea565aca9132d09988cd8de50920c814f47cushon actual(path); 16686665ea565aca9132d09988cd8de50920c814f47cushon fail(); 16786665ea565aca9132d09988cd8de50920c814f47cushon } catch (ZipException e) { 16886665ea565aca9132d09988cd8de50920c814f47cushon assertThat(e).hasMessage("zip file comment length was 33, expected 17"); 16986665ea565aca9132d09988cd8de50920c814f47cushon } 17086665ea565aca9132d09988cd8de50920c814f47cushon } 171ccf23a609c01ce08a79f56353a1750f1067749c6cushon} 172