/* * Copyright 2017 Google Inc. All Rights Reserved. * * 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 com.google.turbine.zip; import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.fail; import com.google.common.collect.ImmutableMap; import com.google.common.hash.Hashing; import com.google.common.io.ByteStreams; import java.io.IOException; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileTime; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.zip.ZipException; import java.util.zip.ZipOutputStream; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** {@link Zip}Test */ @RunWith(JUnit4.class) public class ZipTest { @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void testEntries() throws IOException { testEntries(1000); } @Test public void zip64_testEntries() throws IOException { testEntries(70000); } @Test public void compression() throws IOException { Path path = temporaryFolder.newFile("test.jar").toPath(); try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(path))) { for (int i = 0; i < 2; i++) { String name = "entry" + i; byte[] bytes = name.getBytes(UTF_8); jos.putNextEntry(new JarEntry(name)); jos.write(bytes); } } assertThat(actual(path)).isEqualTo(expected(path)); } private void testEntries(int entries) throws IOException { Path path = temporaryFolder.newFile("test.jar").toPath(); try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(path))) { for (int i = 0; i < entries; i++) { String name = "entry" + i; byte[] bytes = name.getBytes(UTF_8); createEntry(jos, name, bytes); } } assertThat(actual(path)).isEqualTo(expected(path)); } private static void createEntry(ZipOutputStream jos, String name, byte[] bytes) throws IOException { JarEntry je = new JarEntry(name); je.setMethod(JarEntry.STORED); je.setSize(bytes.length); je.setCrc(Hashing.crc32().hashBytes(bytes).padToLong()); jos.putNextEntry(je); jos.write(bytes); } private static Map<String, Long> actual(Path path) throws IOException { Map<String, Long> result = new LinkedHashMap<>(); for (Zip.Entry e : new Zip.ZipIterable(path)) { result.put(e.name(), Hashing.goodFastHash(128).hashBytes(e.data()).padToLong()); } return result; } private static Map<String, Long> expected(Path path) throws IOException { Map<String, Long> result = new LinkedHashMap<>(); try (JarFile jf = new JarFile(path.toFile())) { Enumeration<JarEntry> entries = jf.entries(); while (entries.hasMoreElements()) { JarEntry je = entries.nextElement(); result.put( je.getName(), Hashing.goodFastHash(128) .hashBytes(ByteStreams.toByteArray(jf.getInputStream(je))) .padToLong()); } } return result; } @Test public void attributes() throws Exception { Path path = temporaryFolder.newFile("test.jar").toPath(); Files.delete(path); try (FileSystem fs = FileSystems.newFileSystem( URI.create("jar:" + path.toUri()), ImmutableMap.of("create", "true"))) { for (int i = 0; i < 3; i++) { String name = "entry" + i; byte[] bytes = name.getBytes(UTF_8); Path entry = fs.getPath(name); Files.write(entry, bytes); Files.setLastModifiedTime(entry, FileTime.fromMillis(0)); } } assertThat(actual(path)).isEqualTo(expected(path)); } @Test public void zipFileCommentsAreSupported() throws Exception { Path path = temporaryFolder.newFile("test.jar").toPath(); Files.delete(path); try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(path))) { createEntry(zos, "hello", "world".getBytes(UTF_8)); zos.setComment("this is a comment"); } assertThat(actual(path)).isEqualTo(expected(path)); } @Test public void malformedComment() throws Exception { Path path = temporaryFolder.newFile("test.jar").toPath(); Files.delete(path); try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(path))) { createEntry(zos, "hello", "world".getBytes(UTF_8)); zos.setComment("this is a comment"); } Files.write(path, "trailing garbage".getBytes(UTF_8), StandardOpenOption.APPEND); try { actual(path); fail(); } catch (ZipException e) { assertThat(e).hasMessageThat().isEqualTo("zip file comment length was 33, expected 17"); } } }