diff options
Diffstat (limited to 'src/java_tools/singlejar/javatests/com/google/devtools/build')
8 files changed, 1461 insertions, 452 deletions
diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/MockSimpleFileSystem.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/MockSimpleFileSystem.java index 8fec585fe0..d6f801f23e 100644 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/MockSimpleFileSystem.java +++ b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/MockSimpleFileSystem.java @@ -21,10 +21,13 @@ import static org.junit.Assert.assertNull; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.HashMap; import java.util.Map; @@ -74,6 +77,17 @@ public final class MockSimpleFileSystem implements SimpleFileSystem { } @Override + public File getFile(String filename) throws IOException { + byte[] data = files.get(filename); + if (data == null) { + throw new FileNotFoundException(); + } + File file = File.createTempFile(filename, null); + Files.copy(new ByteArrayInputStream(data), file.toPath(), StandardCopyOption.REPLACE_EXISTING); + return file; + } + + @Override public boolean delete(String filename) { assertEquals(outputFileName, filename); assertNotNull(out); diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTest.java index 0c67b61e18..34c4cc0bf5 100644 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTest.java +++ b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTest.java @@ -14,8 +14,9 @@ package com.google.devtools.build.singlejar; +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.base.Joiner; @@ -78,7 +79,7 @@ public class SingleJarTest { private final List<String> manifestLines; public ManifestValidator(List<String> manifestLines) { - this.manifestLines = new ArrayList<String>(manifestLines); + this.manifestLines = new ArrayList<>(manifestLines); Collections.sort(this.manifestLines); } @@ -146,7 +147,7 @@ public class SingleJarTest { private void assertStripFirstLine(String expected, String testCase) { byte[] result = SingleJar.stripFirstLine(testCase.getBytes(StandardCharsets.UTF_8)); - assertEquals(expected, new String(result)); + assertEquals(expected, new String(result, UTF_8)); } @Test @@ -428,7 +429,7 @@ public class SingleJarTest { MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); SingleJar singleJar = new SingleJar(mockFs); - List<String> args = new ArrayList<String>(); + List<String> args = new ArrayList<>(); args.add("--output"); args.add("output.jar"); args.addAll(infoPropertyArguments(buildInfo)); @@ -591,8 +592,8 @@ public class SingleJarTest { singleJar.run(ImmutableList.of("--output", "output.jar", "--exclude_build_data", "--resources", "a/b/c", "a/b/c")); fail(); - } catch (IllegalStateException e) { - assertTrue(e.getMessage().contains("already contains a file named a/b/c")); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).contains("already contains a file named 'a/b/c'."); } } @@ -616,19 +617,20 @@ public class SingleJarTest { public void testCanAddPreamble() throws IOException { MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); String preamble = "WeThePeople"; - mockFs.addFile(preamble, preamble.getBytes()); + mockFs.addFile(preamble, preamble.getBytes(UTF_8)); SingleJar singleJar = new SingleJar(mockFs); singleJar.run(ImmutableList.of("--output", "output.jar", "--java_launcher", preamble, "--main_class", "SomeClass")); - FakeZipFile expectedResult = new FakeZipFile() - .addPreamble(preamble.getBytes()) - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar", - "Main-Class: SomeClass")) - .addEntry("build-data.properties", redactedBuildData("output.jar", "SomeClass")); + FakeZipFile expectedResult = + new FakeZipFile() + .addPreamble(preamble.getBytes(UTF_8)) + .addEntry("META-INF/", EXTRA_FOR_META_INF) + .addEntry( + JarFile.MANIFEST_NAME, + new ManifestValidator("Manifest-Version: 1.0", "Created-By: blaze-singlejar", + "Main-Class: SomeClass")) + .addEntry("build-data.properties", redactedBuildData("output.jar", "SomeClass")); expectedResult.assertSame(mockFs.toByteArray()); } } diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipCombinerTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipCombinerTest.java index e5345cb1f8..aba561bfd3 100644 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipCombinerTest.java +++ b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipCombinerTest.java @@ -23,34 +23,44 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import com.google.devtools.build.singlejar.ZipCombiner.OutputMode; import com.google.devtools.build.singlejar.ZipEntryFilter.CustomMergeStrategy; +import com.google.devtools.build.zip.ExtraData; +import com.google.devtools.build.zip.ZipFileEntry; +import com.google.devtools.build.zip.ZipReader; +import com.google.devtools.build.zip.ZipUtil; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.EOFException; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Arrays; -import java.util.Calendar; +import java.util.Collection; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; +import java.util.zip.ZipException; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; @@ -59,39 +69,45 @@ import java.util.zip.ZipOutputStream; */ @RunWith(JUnit4.class) public class ZipCombinerTest { + @Rule public TemporaryFolder tmp = new TemporaryFolder(); + @Rule public ExpectedException thrown = ExpectedException.none(); - private static final Date DOS_EPOCH = ZipCombiner.DOS_EPOCH; - - private InputStream sampleZip() { + private File sampleZip() throws IOException { ZipFactory factory = new ZipFactory(); factory.addFile("hello.txt", "Hello World!"); - return factory.toInputStream(); + return writeInputStreamToFile(factory.toInputStream()); } - private InputStream sampleZip2() { + private File sampleZip2() throws IOException { ZipFactory factory = new ZipFactory(); factory.addFile("hello2.txt", "Hello World 2!"); - return factory.toInputStream(); + return writeInputStreamToFile(factory.toInputStream()); } - private InputStream sampleZipWithTwoEntries() { + private File sampleZipWithTwoEntries() throws IOException { ZipFactory factory = new ZipFactory(); factory.addFile("hello.txt", "Hello World!"); factory.addFile("hello2.txt", "Hello World 2!"); - return factory.toInputStream(); + return writeInputStreamToFile(factory.toInputStream()); } - private InputStream sampleZipWithOneUncompressedEntry() { + private File sampleZipWithOneUncompressedEntry() throws IOException { ZipFactory factory = new ZipFactory(); factory.addFile("hello.txt", "Hello World!", false); - return factory.toInputStream(); + return writeInputStreamToFile(factory.toInputStream()); } - private InputStream sampleZipWithTwoUncompressedEntries() { + private File sampleZipWithTwoUncompressedEntries() throws IOException { ZipFactory factory = new ZipFactory(); factory.addFile("hello.txt", "Hello World!", false); factory.addFile("hello2.txt", "Hello World 2!", false); - return factory.toInputStream(); + return writeInputStreamToFile(factory.toInputStream()); + } + + private File writeInputStreamToFile(InputStream in) throws IOException { + File out = tmp.newFile(); + Files.copy(in, out.toPath(), StandardCopyOption.REPLACE_EXISTING); + return out; } private void assertEntry(ZipInputStream zipInput, String filename, long time, byte[] content) @@ -111,7 +127,7 @@ public class ZipCombinerTest { private void assertEntry(ZipInputStream zipInput, String filename, byte[] content) throws IOException { - assertEntry(zipInput, filename, ZipCombiner.DOS_EPOCH.getTime(), content); + assertEntry(zipInput, filename, ZipUtil.DOS_EPOCH, content); } private void assertEntry(ZipInputStream zipInput, String filename, String content) @@ -124,210 +140,112 @@ public class ZipCombinerTest { assertEntry(zipInput, filename, date.getTime(), content.getBytes(ISO_8859_1)); } - @Test - public void testDateToDosTime() { - assertEquals(0x210000, ZipCombiner.dateToDosTime(ZipCombiner.DOS_EPOCH)); - Calendar calendar = new GregorianCalendar(); - for (int i = 1980; i <= 2107; i++) { - calendar.set(i, 0, 1, 0, 0, 0); - int result = ZipCombiner.dateToDosTime(calendar.getTime()); - assertEquals(i - 1980, result >>> 25); - assertEquals(1, (result >> 21) & 0xf); - assertEquals(1, (result >> 16) & 0x1f); - assertEquals(0, result & 0xffff); - } - } - - @Test - public void testDateToDosTimeFailsForBadValues() { - try { - Calendar calendar = new GregorianCalendar(); - calendar.set(1979, 0, 1, 0, 0, 0); - ZipCombiner.dateToDosTime(calendar.getTime()); - fail(); - } catch (IllegalArgumentException e) { - /* Expected exception. */ - } - try { - Calendar calendar = new GregorianCalendar(); - calendar.set(2108, 0, 1, 0, 0, 0); - ZipCombiner.dateToDosTime(calendar.getTime()); - fail(); - } catch (IllegalArgumentException e) { - /* Expected exception. */ - } - } - - @Test - public void testCompressedDontCare() throws IOException { + @Test public void testCompressedDontCare() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addZip(sampleZip()); - singleJar.close(); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("hello.txt", "Hello World!", true); + try (ZipCombiner zipCombiner = new ZipCombiner(out)) { + zipCombiner.addZip(sampleZip()); + } + FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", true); expectedResult.assertSame(out.toByteArray()); } - @Test - public void testCompressedForceDeflate() throws IOException { + @Test public void testCompressedForceDeflate() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(OutputMode.FORCE_DEFLATE, out); - singleJar.addZip(sampleZip()); - singleJar.close(); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("hello.txt", "Hello World!", true); + try (ZipCombiner zipCombiner = new ZipCombiner(OutputMode.FORCE_DEFLATE, out)) { + zipCombiner.addZip(sampleZip()); + } + FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", true); expectedResult.assertSame(out.toByteArray()); } - @Test - public void testCompressedForceStored() throws IOException { + @Test public void testCompressedForceStored() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(OutputMode.FORCE_STORED, out); - singleJar.addZip(sampleZip()); - singleJar.close(); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("hello.txt", "Hello World!", false); + try (ZipCombiner zipCombiner = new ZipCombiner(OutputMode.FORCE_STORED, out)) { + zipCombiner.addZip(sampleZip()); + } + FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", false); expectedResult.assertSame(out.toByteArray()); } - @Test - public void testUncompressedDontCare() throws IOException { + @Test public void testUncompressedDontCare() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addZip(sampleZipWithOneUncompressedEntry()); - singleJar.close(); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("hello.txt", "Hello World!", false); + try (ZipCombiner zipCombiner = new ZipCombiner(out)) { + zipCombiner.addZip(sampleZipWithOneUncompressedEntry()); + } + FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", false); expectedResult.assertSame(out.toByteArray()); } - @Test - public void testUncompressedForceDeflate() throws IOException { + @Test public void testUncompressedForceDeflate() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(OutputMode.FORCE_DEFLATE, out); - singleJar.addZip(sampleZipWithOneUncompressedEntry()); - singleJar.close(); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("hello.txt", "Hello World!", true); + try (ZipCombiner zipCombiner = new ZipCombiner(OutputMode.FORCE_DEFLATE, out)) { + zipCombiner.addZip(sampleZipWithOneUncompressedEntry()); + } + FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", true); expectedResult.assertSame(out.toByteArray()); } - @Test - public void testUncompressedForceStored() throws IOException { + @Test public void testUncompressedForceStored() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(OutputMode.FORCE_STORED, out); - singleJar.addZip(sampleZipWithOneUncompressedEntry()); - singleJar.close(); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("hello.txt", "Hello World!", false); + try (ZipCombiner zipCombiner = new ZipCombiner(OutputMode.FORCE_STORED, out)) { + zipCombiner.addZip(sampleZipWithOneUncompressedEntry()); + } + FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", false); expectedResult.assertSame(out.toByteArray()); } - @Test - public void testCopyTwoEntries() throws IOException { + @Test public void testCopyTwoEntries() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(out)) { + zipCombiner.addZip(sampleZipWithTwoEntries()); + } ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello.txt", "Hello World!"); assertEntry(zipInput, "hello2.txt", "Hello World 2!"); assertNull(zipInput.getNextEntry()); } - @Test - public void testCopyTwoUncompressedEntries() throws IOException { + @Test public void testCopyTwoUncompressedEntries() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addZip(sampleZipWithTwoUncompressedEntries()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(out)) { + zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); + } ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello.txt", "Hello World!"); assertEntry(zipInput, "hello2.txt", "Hello World 2!"); assertNull(zipInput.getNextEntry()); } - @Test - public void testCombine() throws IOException { + @Test public void testCombine() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addZip(sampleZip()); - singleJar.addZip(sampleZip2()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(out)) { + zipCombiner.addZip(sampleZip()); + zipCombiner.addZip(sampleZip2()); + } ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello.txt", "Hello World!"); assertEntry(zipInput, "hello2.txt", "Hello World 2!"); assertNull(zipInput.getNextEntry()); } - @Test - public void testDuplicateEntry() throws IOException { + @Test public void testDuplicateEntry() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addZip(sampleZip()); - singleJar.addZip(sampleZip()); - singleJar.close(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!"); - assertNull(zipInput.getNextEntry()); - } - - // Returns an input stream that can only read one byte at a time. - private InputStream slowRead(final InputStream in) { - return new InputStream() { - @Override - public int read() throws IOException { - return in.read(); - } - @Override - public int read(byte b[], int off, int len) throws IOException { - Preconditions.checkArgument(b != null); - Preconditions.checkArgument((len >= 0) && (off >= 0)); - Preconditions.checkArgument(len <= b.length - off); - if (len == 0) { - return 0; - } - int value = read(); - if (value == -1) { - return -1; - } - b[off] = (byte) value; - return 1; - } - }; - } - - @Test - public void testDuplicateUncompressedEntryWithSlowRead() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addZip(slowRead(sampleZipWithOneUncompressedEntry())); - singleJar.addZip(slowRead(sampleZipWithOneUncompressedEntry())); - singleJar.close(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!"); - assertNull(zipInput.getNextEntry()); - } - - @Test - public void testDuplicateEntryWithSlowRead() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addZip(slowRead(sampleZip())); - singleJar.addZip(slowRead(sampleZip())); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(out)) { + zipCombiner.addZip(sampleZip()); + zipCombiner.addZip(sampleZip()); + } ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello.txt", "Hello World!"); assertNull(zipInput.getNextEntry()); } - @Test - public void testBadZipFileNoEntry() throws IOException { + @Test public void testBadZipFileNoEntry() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addZip(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4 })); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(out)) { + thrown.expect(ZipException.class); + thrown.expectMessage("It does not contain an end of central directory record."); + zipCombiner.addZip(writeInputStreamToFile(new ByteArrayInputStream(new byte[] {1, 2, 3, 4}))); + } ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertNull(zipInput.getNextEntry()); } @@ -336,24 +254,22 @@ public class ZipCombinerTest { return new ByteArrayInputStream(content.getBytes(UTF_8)); } - @Test - public void testAddFile() throws IOException { + @Test public void testAddFile() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addFile("hello.txt", DOS_EPOCH, asStream("Hello World!")); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(out)) { + zipCombiner.addFile("hello.txt", ZipCombiner.DOS_EPOCH, asStream("Hello World!")); + } ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello.txt", "Hello World!"); assertNull(zipInput.getNextEntry()); } - @Test - public void testAddFileAndDuplicateZipEntry() throws IOException { + @Test public void testAddFileAndDuplicateZipEntry() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addFile("hello.txt", DOS_EPOCH, asStream("Hello World!")); - singleJar.addZip(sampleZip()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(out)) { + zipCombiner.addFile("hello.txt", ZipCombiner.DOS_EPOCH, asStream("Hello World!")); + zipCombiner.addZip(sampleZip()); + } ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello.txt", "Hello World!"); assertNull(zipInput.getNextEntry()); @@ -381,7 +297,7 @@ public class ZipCombinerTest { */ class MockZipEntryFilter implements ZipEntryFilter { - private Date date = DOS_EPOCH; + private Date date = ZipCombiner.DOS_EPOCH; private final List<String> calls = new ArrayList<>(); // File name to merge strategy map. private final Map<String, CustomMergeStrategy> behavior = @@ -416,36 +332,33 @@ public class ZipCombinerTest { } } - @Test - public void testCopyCallsFilter() throws IOException { + @Test public void testCopyCallsFilter() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZip()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZip()); + } assertEquals(Arrays.asList("hello.txt"), mockFilter.calls); } - @Test - public void testDuplicateEntryCallsFilterOnce() throws IOException { + @Test public void testDuplicateEntryCallsFilterOnce() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZip()); - singleJar.addZip(sampleZip()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZip()); + zipCombiner.addZip(sampleZip()); + } assertEquals(Arrays.asList("hello.txt"), mockFilter.calls); } - @Test - public void testMergeStrategy() throws IOException { + @Test public void testMergeStrategy() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", new ConcatenateStrategy()); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZip()); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZip()); + zipCombiner.addZip(sampleZipWithTwoEntries()); + } assertEquals(Arrays.asList("hello.txt", "hello2.txt"), mockFilter.calls); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello2.txt", "Hello World 2!"); @@ -453,47 +366,29 @@ public class ZipCombinerTest { assertNull(zipInput.getNextEntry()); } - @Test - public void testMergeStrategyWithUncompressedFiles() throws IOException { + @Test public void testMergeStrategyWithUncompressedFiles() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", new ConcatenateStrategy()); mockFilter.behavior.put("hello2.txt", SKIP_PLACEHOLDER); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZipWithTwoUncompressedEntries()); - singleJar.addZip(sampleZipWithTwoUncompressedEntries()); - singleJar.close(); - assertEquals(Arrays.asList("hello.txt", "hello2.txt"), mockFilter.calls); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!\nHello World!"); - assertNull(zipInput.getNextEntry()); - } - - @Test - public void testMergeStrategyWithUncompressedEntriesAndSlowRead() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", new ConcatenateStrategy()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(slowRead(sampleZipWithOneUncompressedEntry())); - singleJar.addZip(slowRead(sampleZipWithTwoUncompressedEntries())); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); + zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); + } assertEquals(Arrays.asList("hello.txt", "hello2.txt"), mockFilter.calls); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello2.txt", "Hello World 2!"); assertEntry(zipInput, "hello.txt", "Hello World!\nHello World!"); assertNull(zipInput.getNextEntry()); } - @Test - public void testMergeStrategyWithSlowCopy() throws IOException { + @Test public void testMergeStrategyWithSlowCopy() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", new SlowConcatenateStrategy()); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZip()); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZip()); + zipCombiner.addZip(sampleZipWithTwoEntries()); + } assertEquals(Arrays.asList("hello.txt", "hello2.txt"), mockFilter.calls); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello2.txt", "Hello World 2!"); @@ -501,44 +396,41 @@ public class ZipCombinerTest { assertNull(zipInput.getNextEntry()); } - @Test - public void testMergeStrategyWithUncompressedFilesAndSlowCopy() throws IOException { + @Test public void testMergeStrategyWithUncompressedFilesAndSlowCopy() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", new SlowConcatenateStrategy()); mockFilter.behavior.put("hello2.txt", SKIP_PLACEHOLDER); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZipWithTwoUncompressedEntries()); - singleJar.addZip(sampleZipWithTwoUncompressedEntries()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); + zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); + } assertEquals(Arrays.asList("hello.txt", "hello2.txt"), mockFilter.calls); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello.txt", "Hello World!Hello World!"); assertNull(zipInput.getNextEntry()); } - private InputStream specialZipWithMinusOne() { + private File specialZipWithMinusOne() throws IOException { ZipFactory factory = new ZipFactory(); factory.addFile("hello.txt", new byte[] {-1}); - return factory.toInputStream(); + return writeInputStreamToFile(factory.toInputStream()); } - @Test - public void testMergeStrategyWithSlowCopyAndNegativeBytes() throws IOException { + @Test public void testMergeStrategyWithSlowCopyAndNegativeBytes() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", new SlowConcatenateStrategy()); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(specialZipWithMinusOne()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(specialZipWithMinusOne()); + } assertEquals(Arrays.asList("hello.txt"), mockFilter.calls); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello.txt", new byte[] { -1 }); assertNull(zipInput.getNextEntry()); } - @Test - public void testCopyDateHandling() throws IOException { + @Test public void testCopyDateHandling() throws IOException { final Date date = new GregorianCalendar(2009, 8, 2, 0, 0, 0).getTime(); ZipEntryFilter mockFilter = new ZipEntryFilter() { @Override @@ -548,33 +440,31 @@ public class ZipCombinerTest { } }; ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZip()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZip()); + } ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello.txt", date, "Hello World!"); assertNull(zipInput.getNextEntry()); } - @Test - public void testMergeDateHandling() throws IOException { + @Test public void testMergeDateHandling() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", new ConcatenateStrategy()); mockFilter.date = new GregorianCalendar(2009, 8, 2, 0, 0, 0).getTime(); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZip()); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.close(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZip()); + zipCombiner.addZip(sampleZipWithTwoEntries()); + } assertEquals(Arrays.asList("hello.txt", "hello2.txt"), mockFilter.calls); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello2.txt", DOS_EPOCH, "Hello World 2!"); + assertEntry(zipInput, "hello2.txt", ZipCombiner.DOS_EPOCH, "Hello World 2!"); assertEntry(zipInput, "hello.txt", mockFilter.date, "Hello World!\nHello World!"); assertNull(zipInput.getNextEntry()); } - @Test - public void testDuplicateCallThrowsException() throws IOException { + @Test public void testDuplicateCallThrowsException() throws IOException { ZipEntryFilter badFilter = new ZipEntryFilter() { @Override public void accept(String filename, StrategyCallback callback) throws IOException { @@ -584,16 +474,15 @@ public class ZipCombinerTest { } }; ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner singleJar = new ZipCombiner(badFilter, out)) { - singleJar.addZip(sampleZip()); + try (ZipCombiner zipCombiner = new ZipCombiner(badFilter, out)) { + zipCombiner.addZip(sampleZip()); fail(); } catch (IllegalStateException e) { // Expected exception. } } - @Test - public void testNoCallThrowsException() throws IOException { + @Test public void testNoCallThrowsException() throws IOException { ZipEntryFilter badFilter = new ZipEntryFilter() { @Override public void accept(String filename, StrategyCallback callback) { @@ -601,8 +490,8 @@ public class ZipCombinerTest { } }; ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner singleJar = new ZipCombiner(badFilter, out)) { - singleJar.addZip(sampleZip()); + try (ZipCombiner zipCombiner = new ZipCombiner(badFilter, out)) { + zipCombiner.addZip(sampleZip()); fail(); } catch (IllegalStateException e) { // Expected exception. @@ -612,20 +501,20 @@ public class ZipCombinerTest { // This test verifies that if an entry A is renamed as A (identy mapping), // then subsequent entries named A are still subject to filtering. // Note: this is different from a copy, where subsequent entries are skipped. - @Test - public void testRenameIdentityMapping() throws IOException { + @Test public void testRenameIdentityMapping() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); mockFilter.behavior.put("hello2.txt", COPY_PLACEHOLDER); mockFilter.renameMap.put("hello.txt", "hello.txt"); // identity rename, not copy mockFilter.renameMap.put("hello2.txt", "hello2.txt"); // identity rename, not copy ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.close(); - assertThat(mockFilter.calls).containsExactly("hello.txt", "hello2.txt", - "hello.txt", "hello2.txt").inOrder(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZipWithTwoEntries()); + zipCombiner.addZip(sampleZipWithTwoEntries()); + } + assertThat(mockFilter.calls) + .containsExactly("hello.txt", "hello2.txt", "hello.txt", "hello2.txt") + .inOrder(); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello.txt", "Hello World!"); assertEntry(zipInput, "hello2.txt", "Hello World 2!"); @@ -634,20 +523,20 @@ public class ZipCombinerTest { // This test verifies that multiple entries with the same name can be // renamed to unique names. - @Test - public void testRenameNoConflictMapping() throws IOException { + @Test public void testRenameNoConflictMapping() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); mockFilter.behavior.put("hello2.txt", COPY_PLACEHOLDER); mockFilter.renameMap.putAll("hello.txt", Arrays.asList("hello1.txt", "hello2.txt")); mockFilter.renameMap.putAll("hello2.txt", Arrays.asList("world1.txt", "world2.txt")); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.close(); - assertThat(mockFilter.calls).containsExactly("hello.txt", "hello2.txt", - "hello.txt", "hello2.txt").inOrder(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZipWithTwoEntries()); + zipCombiner.addZip(sampleZipWithTwoEntries()); + } + assertThat(mockFilter.calls) + .containsExactly("hello.txt", "hello2.txt", "hello.txt", "hello2.txt") + .inOrder(); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello1.txt", "Hello World!"); assertEntry(zipInput, "world1.txt", "Hello World 2!"); @@ -659,8 +548,7 @@ public class ZipCombinerTest { // This tests verifies that an attempt to rename an entry to a // name already written, results in the entry being skipped, after // calling the filter. - @Test - public void testRenameSkipUsedName() throws IOException { + @Test public void testRenameSkipUsedName() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); mockFilter.behavior.put("hello2.txt", COPY_PLACEHOLDER); @@ -668,13 +556,15 @@ public class ZipCombinerTest { Arrays.asList("hello1.txt", "hello2.txt", "hello3.txt")); mockFilter.renameMap.put("hello2.txt", "hello2.txt"); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.close(); - assertThat(mockFilter.calls).containsExactly("hello.txt", "hello2.txt", - "hello.txt", "hello2.txt", "hello.txt", "hello2.txt").inOrder(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZipWithTwoEntries()); + zipCombiner.addZip(sampleZipWithTwoEntries()); + zipCombiner.addZip(sampleZipWithTwoEntries()); + } + assertThat(mockFilter.calls) + .containsExactly( + "hello.txt", "hello2.txt", "hello.txt", "hello2.txt", "hello.txt", "hello2.txt") + .inOrder(); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello1.txt", "Hello World!"); assertEntry(zipInput, "hello2.txt", "Hello World 2!"); @@ -685,21 +575,21 @@ public class ZipCombinerTest { // This tests verifies that if an entry has been copied, then // further entries of the same name are skipped (filter not invoked), // and entries renamed to the same name are skipped (after calling filter). - @Test - public void testRenameAndCopy() throws IOException { + @Test public void testRenameAndCopy() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); mockFilter.behavior.put("hello2.txt", COPY_PLACEHOLDER); mockFilter.renameMap.putAll("hello.txt", Arrays.asList("hello1.txt", "hello2.txt", "hello3.txt")); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.close(); - assertThat(mockFilter.calls).containsExactly("hello.txt", "hello2.txt", - "hello.txt", "hello.txt").inOrder(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZipWithTwoEntries()); + zipCombiner.addZip(sampleZipWithTwoEntries()); + zipCombiner.addZip(sampleZipWithTwoEntries()); + } + assertThat(mockFilter.calls) + .containsExactly("hello.txt", "hello2.txt", "hello.txt", "hello.txt") + .inOrder(); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello1.txt", "Hello World!"); assertEntry(zipInput, "hello2.txt", "Hello World 2!"); @@ -710,21 +600,21 @@ public class ZipCombinerTest { // This tests verifies that if an entry has been skipped, then // further entries of the same name are skipped (filter not invoked), // and entries renamed to the same name are skipped (after calling filter). - @Test - public void testRenameAndSkip() throws IOException { + @Test public void testRenameAndSkip() throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); mockFilter.behavior.put("hello2.txt", SKIP_PLACEHOLDER); mockFilter.renameMap.putAll("hello.txt", Arrays.asList("hello1.txt", "hello2.txt", "hello3.txt")); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.addZip(sampleZipWithTwoEntries()); - singleJar.close(); - assertThat(mockFilter.calls).containsExactly("hello.txt", "hello2.txt", - "hello.txt", "hello.txt").inOrder(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZipWithTwoEntries()); + zipCombiner.addZip(sampleZipWithTwoEntries()); + zipCombiner.addZip(sampleZipWithTwoEntries()); + } + assertThat(mockFilter.calls) + .containsExactly("hello.txt", "hello2.txt", "hello.txt", "hello.txt") + .inOrder(); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello1.txt", "Hello World!"); assertEntry(zipInput, "hello3.txt", "Hello World!"); @@ -734,8 +624,7 @@ public class ZipCombinerTest { // This test verifies that renaming works when input and output // disagree on compression method. This is the simple case, where // content is read and rewritten, and no header repair is needed. - @Test - public void testRenameWithUncompressedFiles () throws IOException { + @Test public void testRenameWithUncompressedFiles () throws IOException { MockZipEntryFilter mockFilter = new MockZipEntryFilter(); mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); mockFilter.behavior.put("hello2.txt", COPY_PLACEHOLDER); @@ -743,13 +632,15 @@ public class ZipCombinerTest { Arrays.asList("hello1.txt", "hello2.txt", "hello3.txt")); mockFilter.renameMap.put("hello2.txt", "hello2.txt"); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(mockFilter, out); - singleJar.addZip(sampleZipWithTwoUncompressedEntries()); - singleJar.addZip(sampleZipWithTwoUncompressedEntries()); - singleJar.addZip(sampleZipWithTwoUncompressedEntries()); - singleJar.close(); - assertThat(mockFilter.calls).containsExactly("hello.txt", "hello2.txt", - "hello.txt", "hello2.txt", "hello.txt", "hello2.txt").inOrder(); + try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { + zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); + zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); + zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); + } + assertThat(mockFilter.calls) + .containsExactly( + "hello.txt", "hello2.txt", "hello.txt", "hello2.txt", "hello.txt", "hello2.txt") + .inOrder(); ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); assertEntry(zipInput, "hello1.txt", "Hello World!"); assertEntry(zipInput, "hello2.txt", "Hello World 2!"); @@ -762,66 +653,6 @@ public class ZipCombinerTest { // the data descriptor marker. It's unfortunately a bit tricky to create such // a ZIP. private static final int LOCAL_FILE_HEADER_MARKER = 0x04034b50; - private static final int DATA_DESCRIPTOR_MARKER = 0x08074b50; - private static final byte[] DATA_DESCRIPTOR_MARKER_AS_BYTES = new byte[] { - 0x50, 0x4b, 0x07, 0x08 - }; - - // Create a ZIP with an data descriptor marker in the DEFLATE content of a - // file. To do that, we build the ZIP byte by byte. - private InputStream zipWithUnexpectedDataDescriptorMarker() { - ByteBuffer out = ByteBuffer.wrap(new byte[200]).order(ByteOrder.LITTLE_ENDIAN); - out.clear(); - // file header - out.putInt(LOCAL_FILE_HEADER_MARKER); // file header signature - out.putShort((short) 6); // version to extract - out.putShort((short) 8); // general purpose bit flag - out.putShort((short) ZipOutputStream.DEFLATED); // compression method - out.putShort((short) 0); // mtime (00:00:00) - out.putShort((short) 0x21); // mdate (1.1.1980) - out.putInt(0); // crc32 - out.putInt(0); // compressed size - out.putInt(0); // uncompressed size - out.putShort((short) 1); // file name length - out.putShort((short) 0); // extra field length - out.put((byte) 'a'); // file name - - // file contents - out.put((byte) 0x01); // deflated content block is last block and uncompressed - out.putShort((short) 4); // uncompressed block length - out.putShort((short) ~4); // negated uncompressed block length - out.putInt(DATA_DESCRIPTOR_MARKER); // 4 bytes uncompressed data - - // data descriptor - out.putInt(DATA_DESCRIPTOR_MARKER); // data descriptor with marker - out.putInt((int) ZipFactory.calculateCrc32(DATA_DESCRIPTOR_MARKER_AS_BYTES)); - out.putInt(9); - out.putInt(4); - // We omit the central directory here. It's currently not used by - // ZipCombiner or by java.util.zip.ZipInputStream, so that shouldn't be a - // problem. - return new ByteArrayInputStream(out.array()); - } - - // Check that the created ZIP is correct. - @Test - public void testZipWithUnexpectedDataDescriptorMarkerIsCorrect() throws IOException { - ZipInputStream zipInput = new ZipInputStream(zipWithUnexpectedDataDescriptorMarker()); - assertEntry(zipInput, "a", DATA_DESCRIPTOR_MARKER_AS_BYTES); - assertNull(zipInput.getNextEntry()); - } - - // Check that ZipCombiner handles the ZIP correctly. - @Test - public void testZipWithUnexpectedDataDescriptorMarker() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addZip(zipWithUnexpectedDataDescriptorMarker()); - singleJar.close(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "a", DATA_DESCRIPTOR_MARKER_AS_BYTES); - assertNull(zipInput.getNextEntry()); - } // Create a ZIP with a partial entry. private InputStream zipWithPartialEntry() { @@ -848,89 +679,96 @@ public class ZipCombinerTest { return new ByteArrayInputStream(out.array()); } - @Test - public void testBadZipFilePartialEntry() throws IOException { + @Test public void testBadZipFilePartialEntry() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner singleJar = new ZipCombiner(out)) { - singleJar.addZip(zipWithPartialEntry()); - fail(); - } catch (EOFException e) { - // Expected exception. + try (ZipCombiner zipCombiner = new ZipCombiner(out)) { + thrown.expect(ZipException.class); + thrown.expectMessage("It does not contain an end of central directory record."); + zipCombiner.addZip(writeInputStreamToFile(zipWithPartialEntry())); } } - @Test - public void testSimpleJarAgainstJavaUtil() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - JarOutputStream jarOut = new JarOutputStream(out); - ZipEntry entry; - entry = new ZipEntry("META-INF/"); - entry.setTime(DOS_EPOCH.getTime()); - entry.setMethod(JarOutputStream.STORED); - entry.setSize(0); - entry.setCompressedSize(0); - entry.setCrc(0); - jarOut.putNextEntry(entry); - entry = new ZipEntry("META-INF/MANIFEST.MF"); - entry.setTime(DOS_EPOCH.getTime()); - entry.setMethod(JarOutputStream.DEFLATED); - jarOut.putNextEntry(entry); - jarOut.write(new byte[] { 1, 2, 3, 4 }); - jarOut.close(); - byte[] javaFile = out.toByteArray(); + @Test public void testZipCombinerAgainstJavaUtil() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (JarOutputStream jarOut = new JarOutputStream(out)) { + ZipEntry entry; + entry = new ZipEntry("META-INF/"); + entry.setTime(ZipCombiner.DOS_EPOCH.getTime()); + entry.setMethod(JarOutputStream.STORED); + entry.setSize(0); + entry.setCompressedSize(0); + entry.setCrc(0); + jarOut.putNextEntry(entry); + entry = new ZipEntry("META-INF/MANIFEST.MF"); + entry.setTime(ZipCombiner.DOS_EPOCH.getTime()); + entry.setMethod(JarOutputStream.DEFLATED); + jarOut.putNextEntry(entry); + jarOut.write(new byte[] {1, 2, 3, 4}); + } + File javaFile = writeInputStreamToFile(new ByteArrayInputStream(out.toByteArray())); out.reset(); - ZipCombiner singleJar = new ZipCombiner(out); - singleJar.addDirectory("META-INF/", DOS_EPOCH, - new ExtraData[] { new ExtraData((short) 0xCAFE, new byte[0]) }); - singleJar.addFile("META-INF/MANIFEST.MF", DOS_EPOCH, - new ByteArrayInputStream(new byte[] { 1, 2, 3, 4 })); - singleJar.close(); - byte[] singlejarFile = out.toByteArray(); - - new ZipTester(singlejarFile).validate(); - assertZipFilesEquivalent(singlejarFile, javaFile); - } - - void assertZipFilesEquivalent(byte[] x, byte[] y) { - assertEquals(x.length, y.length); - - for (int i = 0; i < x.length; i++) { - if (x[i] != y[i]) { - // Allow general purpose bit 11 (UTF-8 encoding) used in jdk7 to differ - assertEquals("at position " + i, 0x08, x[i] ^ y[i]); - // Check that x[i] is the second byte of a general purpose bit flag. - // Phil Katz, you will never be forgotten. - assertTrue( - // Local header - x[i-7] == 'P' && x[i-6] == 'K' && x[i-5] == 3 && x[i-4] == 4 || - // Central directory header - x[i-9] == 'P' && x[i-8] == 'K' && x[i-7] == 1 && x[i-6] == 2); - } + try (ZipCombiner zipcombiner = new ZipCombiner(out)) { + zipcombiner.addDirectory("META-INF/", ZipCombiner.DOS_EPOCH, + new ExtraData[] {new ExtraData((short) 0xCAFE, new byte[0])}); + zipcombiner.addFile("META-INF/MANIFEST.MF", ZipCombiner.DOS_EPOCH, + new ByteArrayInputStream(new byte[] {1, 2, 3, 4})); } + File zipCombinerFile = writeInputStreamToFile(new ByteArrayInputStream(out.toByteArray())); + byte[] zipCombinerRaw = out.toByteArray(); + + new ZipTester(zipCombinerRaw).validate(); + assertZipFilesEquivalent(new ZipReader(zipCombinerFile), new ZipReader(javaFile)); + } + + void assertZipFilesEquivalent(ZipReader x, ZipReader y) { + Collection<ZipFileEntry> xEntries = x.entries(); + Collection<ZipFileEntry> yEntries = y.entries(); + assertThat(xEntries).hasSize(yEntries.size()); + Iterator<ZipFileEntry> xIter = xEntries.iterator(); + Iterator<ZipFileEntry> yIter = yEntries.iterator(); + for (int i = 0; i < xEntries.size(); i++) { + assertZipEntryEquivalent(xIter.next(), yIter.next()); + } + } + + void assertZipEntryEquivalent(ZipFileEntry x, ZipFileEntry y) { + assertThat(x.getComment()).isEqualTo(y.getComment()); + assertThat(x.getCompressedSize()).isEqualTo(y.getCompressedSize()); + assertThat(x.getCrc()).isEqualTo(y.getCrc()); + assertThat(x.getExternalAttributes()).isEqualTo(y.getExternalAttributes()); + assertThat(x.getExtra()).isEqualTo(y.getExtra()); + assertThat(x.getInternalAttributes()).isEqualTo(y.getInternalAttributes()); + assertThat(x.getMethod()).isEqualTo(y.getMethod()); + assertThat(x.getName()).isEqualTo(y.getName()); + assertThat(x.getSize()).isEqualTo(y.getSize()); + assertThat(x.getTime()).isEqualTo(y.getTime()); + assertThat(x.getVersion()).isEqualTo(y.getVersion()); + assertThat(x.getVersionNeeded()).isEqualTo(y.getVersionNeeded()); + // Allow general purpose bit 3 (data descriptor) used in jdk7 to differ. + // Allow general purpose bit 11 (UTF-8 encoding) used in jdk7 to differ. + assertThat(x.getFlags() | (1 << 3) | (1 << 11)) + .isEqualTo(y.getFlags() | (1 << 3) | (1 << 11)); } /** * Ensures that the code that grows the central directory and the code that patches it is not * obviously broken. */ - @Test - public void testLotsOfFiles() throws IOException { + @Test public void testLotsOfFiles() throws IOException { int fileCount = 100; - for (int blockSize : new int[] { 1, 2, 3, 4, 10, 1000 }) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipCombiner zipCombiner = new ZipCombiner( - OutputMode.DONT_CARE, new CopyEntryFilter(), out, blockSize); - for (int i = 0; i < fileCount; i++) { - zipCombiner.addFile("hello" + i, DOS_EPOCH, asStream("Hello " + i + "!")); - } - zipCombiner.close(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (ZipCombiner zipCombiner = + new ZipCombiner(OutputMode.DONT_CARE, new CopyEntryFilter(), out)) { for (int i = 0; i < fileCount; i++) { - assertEntry(zipInput, "hello" + i, "Hello " + i + "!"); + zipCombiner.addFile("hello" + i, ZipCombiner.DOS_EPOCH, asStream("Hello " + i + "!")); } - assertNull(zipInput.getNextEntry()); - new ZipTester(out.toByteArray()).validate(); } + ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); + for (int i = 0; i < fileCount; i++) { + assertEntry(zipInput, "hello" + i, "Hello " + i + "!"); + } + assertNull(zipInput.getNextEntry()); + new ZipTester(out.toByteArray()).validate(); } } diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipFileEntryTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipFileEntryTest.java new file mode 100644 index 0000000000..de05c4a6e8 --- /dev/null +++ b/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipFileEntryTest.java @@ -0,0 +1,193 @@ +// Copyright 2015 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.devtools.build.zip; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.NullPointerTester; +import com.google.devtools.build.zip.ZipFileEntry.Compression; +import com.google.devtools.build.zip.ZipFileEntry.Flag; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class ZipFileEntryTest { + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test public void testNulls() { + NullPointerTester tester = new NullPointerTester(); + tester.testAllPublicConstructors(ZipFileEntry.class); + tester.testAllPublicInstanceMethods(new ZipFileEntry("foo")); + } + + @Test public void testCrc() { + ZipFileEntry foo = new ZipFileEntry("foo"); + foo.setCrc(32); + } + + @Test public void testCrc_Negative() { + ZipFileEntry foo = new ZipFileEntry("foo"); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("invalid entry crc-32"); + foo.setCrc(-1); + } + + @Test public void testCrc_Large() { + ZipFileEntry foo = new ZipFileEntry("foo"); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("invalid entry crc-32"); + foo.setCrc(0x100000000L); + } + + @Test public void testSize() { + ZipFileEntry foo = new ZipFileEntry("foo"); + foo.setSize(32); + } + + @Test public void testSize_Negative() { + ZipFileEntry foo = new ZipFileEntry("foo"); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("invalid entry size"); + foo.setSize(-1); + } + + @Test public void testSize_Large() { + ZipFileEntry foo = new ZipFileEntry("foo"); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("invalid entry size"); + foo.setSize(0x100000000L); + } + + @Test public void testCompressedSize() { + ZipFileEntry foo = new ZipFileEntry("foo"); + foo.setCompressedSize(32); + } + + @Test public void testCompressedSize_Negative() { + ZipFileEntry foo = new ZipFileEntry("foo"); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("invalid entry size"); + foo.setCompressedSize(-1); + } + + @Test public void testCompressedSize_Large() { + ZipFileEntry foo = new ZipFileEntry("foo"); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("invalid entry size"); + foo.setCompressedSize(0x100000000L); + } + + @Test public void testMinVersion_MethodUpdated() { + ZipFileEntry foo = new ZipFileEntry("foo"); + assertThat(foo.getVersion()).isEqualTo((short) -1); + foo.setMethod(Compression.STORED); + assertThat(foo.getVersion()).isEqualTo((short) 0x0a); + } + + @Test public void testMinVersion_Update() { + ZipFileEntry foo = new ZipFileEntry("foo"); + foo.setMethod(Compression.STORED); + foo.setVersion((short) 0x14); + assertThat(foo.getVersion()).isEqualTo((short) 0x14); + } + + @Test public void testMinVersion_BelowRequired() { + ZipFileEntry foo = new ZipFileEntry("foo"); + foo.setMethod(Compression.STORED); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("The minimum allowable version for method STORED is 0x0a."); + foo.setVersion((short) 0); + } + + @Test public void testMinVersionNeeded_MethodUpdated() { + ZipFileEntry foo = new ZipFileEntry("foo"); + assertThat(foo.getVersionNeeded()).isEqualTo((short) -1); + foo.setMethod(Compression.DEFLATED); + assertThat(foo.getVersionNeeded()).isEqualTo((short) 0x14); + } + + @Test public void testMinVersionNeeded_Update() { + ZipFileEntry foo = new ZipFileEntry("foo"); + foo.setMethod(Compression.STORED); + foo.setVersionNeeded((short) 0x28); + assertThat(foo.getVersionNeeded()).isEqualTo((short) 0x28); + } + + @Test public void testMinVersionNeeded_BelowRequired() { + ZipFileEntry foo = new ZipFileEntry("foo"); + foo.setMethod(Compression.DEFLATED); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("The minimum allowable version for method DEFLATED is 0x14."); + foo.setVersionNeeded((short) 0x0a); + } + + @Test public void testSetFlag() { + ZipFileEntry foo = new ZipFileEntry("foo"); + foo.setFlag(Flag.DATA_DESCRIPTOR, true); + assertThat(foo.getFlags()).isEqualTo((short) 0x08); + foo.setFlag(Flag.DATA_DESCRIPTOR, true); + assertThat(foo.getFlags()).isEqualTo((short) 0x08); + foo.setFlag(Flag.DATA_DESCRIPTOR, false); + assertThat(foo.getFlags()).isEqualTo((short) 0x00); + foo.setFlag(Flag.DATA_DESCRIPTOR, false); + assertThat(foo.getFlags()).isEqualTo((short) 0x00); + } + + @Test public void testLocalHeaderOffset() { + ZipFileEntry foo = new ZipFileEntry("foo"); + foo.setLocalHeaderOffset(32); + } + + @Test public void testLocalHeaderOffset_Negative() { + ZipFileEntry foo = new ZipFileEntry("foo"); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("invalid local header offset"); + foo.setLocalHeaderOffset(-1); + } + + @Test public void testLocalHeaderOffset_Large() { + ZipFileEntry foo = new ZipFileEntry("foo"); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("invalid local header offset"); + foo.setLocalHeaderOffset(0x100000000L); + } + + @Test public void testExtra() { + ZipFileEntry foo = new ZipFileEntry("foo"); + foo.setExtra(new byte[32]); + } + + @Test public void testExtra_Large() { + ZipFileEntry foo = new ZipFileEntry("foo"); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("invalid extra field length"); + foo.setExtra(new byte[0x10000]); + } + + @Test public void testExtraData() { + ZipFileEntry foo = new ZipFileEntry("foo"); + ExtraData[] extra = new ExtraData[] { + new ExtraData((short) 0xCAFE, new byte[] { 0x01, 0x02 }), + new ExtraData((short) 0xADDE, new byte[] { (byte) 0xBE, (byte) 0xEF }) }; + foo.setExtra(extra); + assertThat(foo.getExtra()).isEqualTo(new byte[] { + (byte) 0xFE, (byte) 0xCA, 0x02, 0x00, 0x01, 0x02, + (byte) 0xDE, (byte) 0xAD, 0x02, 0x00, (byte) 0xBE, (byte) 0xEF }); + } +} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipReaderTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipReaderTest.java new file mode 100644 index 0000000000..67346932d3 --- /dev/null +++ b/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipReaderTest.java @@ -0,0 +1,390 @@ +// Copyright 2015 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.devtools.build.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.devtools.build.zip.ZipFileEntry.Compression; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Calendar; +import java.util.Date; +import java.util.zip.CRC32; +import java.util.zip.Deflater; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipOutputStream; + +@RunWith(JUnit4.class) +public class ZipReaderTest { + private void assertDateWithin(Date testDate, Date start, Date end) { + if (testDate.before(start) || testDate.after(end)) { + fail(); + } + } + + private void assertDateAboutNow(Date testDate) { + Date now = new Date(); + Calendar cal = Calendar.getInstance(); + cal.setTime(now); + cal.add(Calendar.MINUTE, -30); + Date start = cal.getTime(); + cal.add(Calendar.HOUR, 1); + Date end = cal.getTime(); + assertDateWithin(testDate, start, end); + } + + @Rule public TemporaryFolder tmp = new TemporaryFolder(); + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test public void testMalformed_Empty() throws IOException { + File test = tmp.newFile("test.zip"); + try (FileOutputStream out = new FileOutputStream(test)) { + } + thrown.expect(ZipException.class); + thrown.expectMessage("is malformed. It does not contain an end of central directory record."); + try (ZipReader reader = new ZipReader(test, UTF_8)) { + } + } + + @Test public void testMalformed_ShorterThanSignature() throws IOException { + File test = tmp.newFile("test.zip"); + try (FileOutputStream out = new FileOutputStream(test)) { + out.write(new byte[] { 1, 2, 3 }); + } + thrown.expect(ZipException.class); + thrown.expectMessage("is malformed. It does not contain an end of central directory record."); + try (ZipReader reader = new ZipReader(test, UTF_8)) { + } + } + + @Test public void testMalformed_SignatureLength() throws IOException { + File test = tmp.newFile("test.zip"); + try (FileOutputStream out = new FileOutputStream(test)) { + out.write(new byte[] { 1, 2, 3, 4 }); + } + thrown.expect(ZipException.class); + thrown.expectMessage("is malformed. It does not contain an end of central directory record."); + try (ZipReader reader = new ZipReader(test, UTF_8)) { + } + } + + @Test public void testEmpty() throws IOException { + File test = tmp.newFile("test.zip"); + try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(test))) { + } + try (ZipReader reader = new ZipReader(test, UTF_8)) { + assertThat(reader.entries()).isEmpty(); + } + } + + @Test public void testFileComment() throws IOException { + File test = tmp.newFile("test.zip"); + try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(test))) { + zout.setComment("test comment"); + } + try (ZipReader reader = new ZipReader(test, UTF_8)) { + assertThat(reader.entries()).isEmpty(); + assertThat(reader.getComment()).isEqualTo("test comment"); + } + } + + @Test public void testFileCommentWithSignature() throws IOException { + File test = tmp.newFile("test.zip"); + try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(test))) { + zout.setComment("test comment\u0050\u004b\u0005\u0006abcdefghijklmnopqrstuvwxyz"); + } + try (ZipReader reader = new ZipReader(test, UTF_8)) { + assertThat(reader.entries()).isEmpty(); + assertThat(reader.getComment()) + .isEqualTo("test comment\u0050\u004b\u0005\u0006abcdefghijklmnopqrstuvwxyz"); + } + } + + @Test public void testSingleEntry() throws IOException { + File test = tmp.newFile("test.zip"); + try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(test))) { + zout.putNextEntry(new ZipEntry("test")); + zout.write("foo".getBytes(UTF_8)); + zout.closeEntry(); + } + try (ZipReader reader = new ZipReader(test, UTF_8)) { + assertThat(reader.entries()).hasSize(1); + } + } + + @Test public void testMultipleEntries() throws IOException { + File test = tmp.newFile("test.zip"); + String[] names = new String[] { "test", "foo", "bar", "baz" }; + try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(test))) { + for (String name : names) { + zout.putNextEntry(new ZipEntry(name)); + zout.write(name.getBytes(UTF_8)); + zout.closeEntry(); + } + } + + try (ZipReader reader = new ZipReader(test, UTF_8)) { + assertThat(reader.entries()).hasSize(names.length); + int i = 0; + for (ZipFileEntry entry : reader.entries()) { + assertThat(entry.getName()).isEqualTo(names[i++]); + } + assertThat(i).isEqualTo(names.length); + } + } + + @Test public void testZipEntryFields() throws IOException { + File test = tmp.newFile("test.zip"); + CRC32 crc = new CRC32(); + Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true); + long date = 791784306000L; // 2/3/1995 04:05:06 + byte[] extra = new byte[] { (byte) 0xaa, (byte) 0xbb, (byte) 0xcd }; + byte[] tmp = new byte[128]; + try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(test))) { + + ZipEntry foo = new ZipEntry("foo"); + foo.setComment("foo comment."); + foo.setMethod(ZipEntry.DEFLATED); + foo.setTime(date); + foo.setExtra(extra); + zout.putNextEntry(foo); + zout.write("foo".getBytes(UTF_8)); + zout.closeEntry(); + + ZipEntry bar = new ZipEntry("bar"); + bar.setComment("bar comment."); + bar.setMethod(ZipEntry.STORED); + bar.setSize("bar".length()); + bar.setCompressedSize("bar".length()); + crc.reset(); + crc.update("bar".getBytes(UTF_8)); + bar.setCrc(crc.getValue()); + zout.putNextEntry(bar); + zout.write("bar".getBytes(UTF_8)); + zout.closeEntry(); + } + + try (ZipReader reader = new ZipReader(test, UTF_8)) { + ZipFileEntry fooEntry = reader.getEntry("foo"); + assertThat(fooEntry.getName()).isEqualTo("foo"); + assertThat(fooEntry.getComment()).isEqualTo("foo comment."); + assertThat(fooEntry.getMethod()).isEqualTo(Compression.DEFLATED); + assertThat(fooEntry.getVersion()).isEqualTo(Compression.DEFLATED.getMinVersion()); + assertThat(fooEntry.getTime()).isEqualTo(date); + assertThat(fooEntry.getSize()).isEqualTo("foo".length()); + deflater.reset(); + deflater.setInput("foo".getBytes(UTF_8)); + deflater.finish(); + assertThat(fooEntry.getCompressedSize()).isEqualTo(deflater.deflate(tmp)); + crc.reset(); + crc.update("foo".getBytes(UTF_8)); + assertThat(fooEntry.getCrc()).isEqualTo(crc.getValue()); + assertThat(fooEntry.getExtra()).isEqualTo(extra); + + ZipFileEntry barEntry = reader.getEntry("bar"); + assertThat(barEntry.getName()).isEqualTo("bar"); + assertThat(barEntry.getComment()).isEqualTo("bar comment."); + assertThat(barEntry.getMethod()).isEqualTo(Compression.STORED); + assertThat(barEntry.getVersion()).isEqualTo(Compression.STORED.getMinVersion()); + assertDateAboutNow(new Date(barEntry.getTime())); + assertThat(barEntry.getSize()).isEqualTo("bar".length()); + assertThat(barEntry.getCompressedSize()).isEqualTo("bar".length()); + crc.reset(); + crc.update("bar".getBytes(UTF_8)); + assertThat(barEntry.getCrc()).isEqualTo(crc.getValue()); + assertThat(barEntry.getExtra()).isEqualTo(new byte[] {}); + } + } + + @Test public void testZipEntryInvalidTime() throws IOException { + File test = tmp.newFile("test.zip"); + long date = 312796800000L; // 11/30/1979 00:00:00, which is also 0 in DOS format + byte[] extra = new byte[] { (byte) 0xaa, (byte) 0xbb, (byte) 0xcd }; + try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(test))) { + ZipEntry foo = new ZipEntry("foo"); + foo.setComment("foo comment."); + foo.setMethod(ZipEntry.DEFLATED); + foo.setTime(date); + foo.setExtra(extra); + zout.putNextEntry(foo); + zout.write("foo".getBytes(UTF_8)); + zout.closeEntry(); + } + + try (ZipReader reader = new ZipReader(test, UTF_8)) { + ZipFileEntry fooEntry = reader.getEntry("foo"); + assertThat(fooEntry.getTime()).isEqualTo(ZipUtil.DOS_EPOCH); + } + } + + @Test public void testRawFileData() throws IOException { + File test = tmp.newFile("test.zip"); + CRC32 crc = new CRC32(); + Deflater deflator = new Deflater(Deflater.DEFAULT_COMPRESSION, true); + try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(test))) { + ZipEntry foo = new ZipEntry("foo"); + foo.setComment("foo comment."); + foo.setMethod(ZipEntry.DEFLATED); + zout.putNextEntry(foo); + zout.write("foo".getBytes(UTF_8)); + zout.closeEntry(); + + ZipEntry bar = new ZipEntry("bar"); + bar.setComment("bar comment."); + bar.setMethod(ZipEntry.STORED); + bar.setSize("bar".length()); + bar.setCompressedSize("bar".length()); + crc.reset(); + crc.update("bar".getBytes(UTF_8)); + bar.setCrc(crc.getValue()); + zout.putNextEntry(bar); + zout.write("bar".getBytes(UTF_8)); + zout.closeEntry(); + } + + try (ZipReader reader = new ZipReader(test, UTF_8)) { + ZipFileEntry fooEntry = reader.getEntry("foo"); + InputStream fooIn = reader.getRawInputStream(fooEntry); + byte[] fooData = new byte[10]; + fooIn.read(fooData); + byte[] expectedFooData = new byte[10]; + deflator.reset(); + deflator.setInput("foo".getBytes(UTF_8)); + deflator.finish(); + deflator.deflate(expectedFooData); + assertThat(fooData).isEqualTo(expectedFooData); + + ZipFileEntry barEntry = reader.getEntry("bar"); + InputStream barIn = reader.getRawInputStream(barEntry); + byte[] barData = new byte[3]; + barIn.read(barData); + byte[] expectedBarData = "bar".getBytes(UTF_8); + assertThat(barData).isEqualTo(expectedBarData); + + assertThat(barIn.read()).isEqualTo(-1); + assertThat(barIn.read(barData)).isEqualTo(-1); + assertThat(barIn.read(barData, 0, 3)).isEqualTo(-1); + + thrown.expect(IOException.class); + thrown.expectMessage("Reset is not supported on this type of stream."); + barIn.reset(); + } + } + + @Test public void testFileData() throws IOException { + File test = tmp.newFile("test.zip"); + CRC32 crc = new CRC32(); + try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(test))) { + ZipEntry foo = new ZipEntry("foo"); + foo.setComment("foo comment."); + foo.setMethod(ZipEntry.DEFLATED); + zout.putNextEntry(foo); + zout.write("foo".getBytes(UTF_8)); + zout.closeEntry(); + + ZipEntry bar = new ZipEntry("bar"); + bar.setComment("bar comment."); + bar.setMethod(ZipEntry.STORED); + bar.setSize("bar".length()); + bar.setCompressedSize("bar".length()); + crc.reset(); + crc.update("bar".getBytes(UTF_8)); + bar.setCrc(crc.getValue()); + zout.putNextEntry(bar); + zout.write("bar".getBytes(UTF_8)); + zout.closeEntry(); + } + + try (ZipReader reader = new ZipReader(test, UTF_8)) { + ZipFileEntry fooEntry = reader.getEntry("foo"); + InputStream fooIn = reader.getInputStream(fooEntry); + byte[] fooData = new byte[3]; + fooIn.read(fooData); + byte[] expectedFooData = "foo".getBytes(UTF_8); + assertThat(fooData).isEqualTo(expectedFooData); + + assertThat(fooIn.read()).isEqualTo(-1); + assertThat(fooIn.read(fooData)).isEqualTo(-1); + assertThat(fooIn.read(fooData, 0, 3)).isEqualTo(-1); + + ZipFileEntry barEntry = reader.getEntry("bar"); + InputStream barIn = reader.getInputStream(barEntry); + byte[] barData = new byte[3]; + barIn.read(barData); + byte[] expectedBarData = "bar".getBytes(UTF_8); + assertThat(barData).isEqualTo(expectedBarData); + + assertThat(barIn.read()).isEqualTo(-1); + assertThat(barIn.read(barData)).isEqualTo(-1); + assertThat(barIn.read(barData, 0, 3)).isEqualTo(-1); + + thrown.expect(IOException.class); + thrown.expectMessage("Reset is not supported on this type of stream."); + barIn.reset(); + } + } + + @Test public void testSimultaneousReads() throws IOException { + File test = tmp.newFile("test.zip"); + byte[] expectedFooData = "This if file foo. It contains a foo.".getBytes(UTF_8); + byte[] expectedBarData = "This is a different file bar. It contains only a bar." + .getBytes(UTF_8); + try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(test))) { + ZipEntry foo = new ZipEntry("foo"); + foo.setComment("foo comment."); + foo.setMethod(ZipEntry.DEFLATED); + zout.putNextEntry(foo); + zout.write(expectedFooData); + zout.closeEntry(); + + ZipEntry bar = new ZipEntry("bar"); + bar.setComment("bar comment."); + bar.setMethod(ZipEntry.DEFLATED); + zout.putNextEntry(bar); + zout.write(expectedBarData); + zout.closeEntry(); + } + + try (ZipReader reader = new ZipReader(test, UTF_8)) { + ZipFileEntry fooEntry = reader.getEntry("foo"); + ZipFileEntry barEntry = reader.getEntry("bar"); + InputStream fooIn = reader.getInputStream(fooEntry); + InputStream barIn = reader.getInputStream(barEntry); + byte[] fooData = new byte[expectedFooData.length]; + byte[] barData = new byte[expectedBarData.length]; + fooIn.read(fooData, 0, 10); + barIn.read(barData, 0, 10); + fooIn.read(fooData, 10, 10); + barIn.read(barData, 10, 10); + fooIn.read(fooData, 20, fooData.length - 20); + barIn.read(barData, 20, barData.length - 20); + assertThat(fooData).isEqualTo(expectedFooData); + assertThat(barData).isEqualTo(expectedBarData); + } + } +} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipTests.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipTests.java new file mode 100644 index 0000000000..2b05b5c2a5 --- /dev/null +++ b/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipTests.java @@ -0,0 +1,27 @@ +// Copyright 2015 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.devtools.build.zip; + + +import com.google.devtools.build.lib.testutil.ClasspathSuite; + +import org.junit.runner.RunWith; + +/** + * A test-suite builder for this package. + */ +@RunWith(ClasspathSuite.class) +public class ZipTests { +} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipUtilTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipUtilTest.java new file mode 100644 index 0000000000..60f2b732c7 --- /dev/null +++ b/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipUtilTest.java @@ -0,0 +1,172 @@ +// Copyright 2015 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.devtools.build.zip; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Calendar; +import java.util.GregorianCalendar; + +@RunWith(JUnit4.class) +public class ZipUtilTest { + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test public void testShortToLittleEndian() { + byte[] bytes = ZipUtil.shortToLittleEndian((short) 4660); + assertThat(bytes).isEqualTo(new byte[]{ 0x34, 0x12 }); + } + + @Test public void testShortToLittleEndian_Signed() { + byte[] bytes = ZipUtil.shortToLittleEndian((short) -3532); + assertThat(bytes).isEqualTo(new byte[]{ 0x34, (byte) 0xf2 }); + } + + @Test public void testIntToLittleEndian() { + byte[] bytes = ZipUtil.intToLittleEndian(305419896); + assertThat(bytes).isEqualTo(new byte[]{ 0x78, 0x56, 0x34, 0x12 }); + } + + @Test public void testIntToLittleEndian_Signed() { + byte[] bytes = ZipUtil.intToLittleEndian(-231451016); + assertThat(bytes).isEqualTo(new byte[]{ 0x78, 0x56, 0x34, (byte) 0xf2 }); + } + + @Test public void testLongToLittleEndian() { + byte[] bytes = ZipUtil.longToLittleEndian(305419896); + assertThat(bytes).isEqualTo(new byte[]{ 0x78, 0x56, 0x34, 0x12, 0x0, 0x0, 0x0, 0x0 }); + } + + @Test public void testLongToLittleEndian_Signed() { + byte[] bytes = ZipUtil.longToLittleEndian(-231451016); + assertThat(bytes).isEqualTo(new byte[]{ 0x78, 0x56, 0x34, (byte) 0xf2, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }); + } + + @Test public void testGet16() { + short result = ZipUtil.get16(new byte[]{ 0x34, 0x12 }, 0); + assertThat(result).isEqualTo((short) 0x1234); + assertThat(result).isEqualTo((short) 4660); + } + + @Test public void testGet16_Signed() { + short result = ZipUtil.get16(new byte[]{ 0x34, (byte) 0xff }, 0); + assertThat(result).isEqualTo((short) 0xff34); + assertThat(result).isEqualTo((short) -204); + } + + @Test public void testGet32() { + int result = ZipUtil.get32(new byte[]{ 0x78, 0x56, 0x34, 0x12 }, 0); + assertThat(result).isEqualTo(0x12345678); + assertThat(result).isEqualTo(305419896); + } + + @Test public void testGet32_Short() { + int result = ZipUtil.get32(new byte[]{ 0x34, (byte) 0xff, 0x0, 0x0 }, 0); + assertThat(result).isEqualTo(0xff34); + assertThat(result).isEqualTo(65332); + } + + @Test public void testGet32_Signed() { + int result = ZipUtil.get32(new byte[]{ 0x34, (byte) 0xff, (byte) 0xff, (byte) 0xff }, 0); + assertThat(result).isEqualTo(0xffffff34); + assertThat(result).isEqualTo(-204); + } + + @Test public void testGetUnsignedShort() { + int result = ZipUtil.getUnsignedShort(new byte[]{ 0x34, 0x12 }, 0); + assertThat(result).isEqualTo(0x1234); + assertThat(result).isEqualTo(4660); + } + + @Test public void testGetUnsignedShort_Big() { + int result = ZipUtil.getUnsignedShort(new byte[]{ 0x34, (byte) 0xff }, 0); + assertThat(result).isEqualTo(0xff34); + assertThat(result).isEqualTo(65332); + } + + @Test public void testGetUnsignedInt() { + long result = ZipUtil.getUnsignedInt(new byte[]{ 0x34, 0x12, 0x0, 0x0 }, 0); + assertThat(result).isEqualTo(0x1234); + assertThat(result).isEqualTo(4660); + } + + @Test public void testGetUnsignedInt_Big() { + long result = ZipUtil.getUnsignedInt( + new byte[]{ 0x34, (byte) 0xff, (byte) 0xff, (byte) 0xff }, 0); + assertThat(result).isEqualTo(0xffffff34L); + assertThat(result).isEqualTo(4294967092L); + } + + @Test public void testTimeConversion_DosToUnix() { + int dos = (20 << 25) | (2 << 21) | (14 << 16) | (3 << 11) | (7 << 5) | (15 >> 1); + + Calendar time = new GregorianCalendar(2000, Calendar.FEBRUARY, 14, 3, 7, 14); + long expectedUnixTime = time.getTimeInMillis(); + assertThat(ZipUtil.dosToUnixTime(dos)).isEqualTo(expectedUnixTime); + } + + @Test public void testTimeConversion_UnixToDos() { + Calendar time = new GregorianCalendar(2000, Calendar.FEBRUARY, 14, 3, 7, 14); + long unix = time.getTimeInMillis(); + int expectedDosTime = (20 << 25) | (2 << 21) | (14 << 16) | (3 << 11) | (7 << 5) | (15 >> 1); + assertThat(ZipUtil.unixToDosTime(unix)).isEqualTo(expectedDosTime); + } + + @Test public void testTimeConversion_UnixToDos_LowBound() { + Calendar time = Calendar.getInstance(); + time.setTimeInMillis(ZipUtil.DOS_EPOCH); + time.add(Calendar.SECOND, -1); + thrown.expect(IllegalArgumentException.class); + ZipUtil.unixToDosTime(time.getTimeInMillis()); + } + + @Test public void testTimeConversion_UnixToDos_HighBound_Rounding() { + Calendar time = Calendar.getInstance(); + time.setTimeInMillis(ZipUtil.MAX_DOS_DATE); + ZipUtil.unixToDosTime(time.getTimeInMillis()); + } + + @Test public void testTimeConversion_UnixToDos_HighBound() { + Calendar time = Calendar.getInstance(); + time.setTimeInMillis(ZipUtil.MAX_DOS_DATE); + time.add(Calendar.SECOND, 1); + thrown.expect(IllegalArgumentException.class); + ZipUtil.unixToDosTime(time.getTimeInMillis()); + } + + @Test public void testTimeConversion_UnixToUnix() { + Calendar from = new GregorianCalendar(2000, Calendar.FEBRUARY, 14, 3, 7, 15); + Calendar to = new GregorianCalendar(2000, Calendar.FEBRUARY, 14, 3, 7, 14); + assertThat(ZipUtil.dosToUnixTime(ZipUtil.unixToDosTime(from.getTimeInMillis()))) + .isEqualTo(to.getTimeInMillis()); + } + + @Test public void testTimeConversion_DosToDos() { + int dos = (20 << 25) | (2 << 21) | (14 << 16) | (3 << 11) | (7 << 5) | (15 >> 1); + assertThat(ZipUtil.unixToDosTime(ZipUtil.dosToUnixTime(dos))).isEqualTo(dos); + } + + @Test public void testTimeConversion_DosToDos_Zero() { + int dos = 0; + thrown.expect(IllegalArgumentException.class); + assertThat(ZipUtil.unixToDosTime(ZipUtil.dosToUnixTime(dos))).isEqualTo(0); + } +} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipWriterTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipWriterTest.java new file mode 100644 index 0000000000..e58a90c923 --- /dev/null +++ b/src/java_tools/singlejar/javatests/com/google/devtools/build/zip/ZipWriterTest.java @@ -0,0 +1,373 @@ +// Copyright 2015 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.devtools.build.zip; + +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.primitives.Bytes; +import com.google.devtools.build.zip.ZipFileEntry.Compression; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Calendar; +import java.util.Random; +import java.util.zip.CRC32; +import java.util.zip.Deflater; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +@RunWith(JUnit4.class) +public class ZipWriterTest { + @Rule public TemporaryFolder tmp = new TemporaryFolder(); + @Rule public ExpectedException thrown = ExpectedException.none(); + + private Random rand; + private Calendar cal; + private CRC32 crc; + private Deflater deflater; + private File test; + + @Before public void setup() throws IOException { + rand = new Random(); + cal = Calendar.getInstance(); + cal.clear(); + cal.set(Calendar.YEAR, rand.nextInt(128) + 1980); // Zip files have 7-bit year resolution. + cal.set(Calendar.MONTH, rand.nextInt(12)); + cal.set(Calendar.DAY_OF_MONTH, rand.nextInt(29)); + cal.set(Calendar.HOUR_OF_DAY, rand.nextInt(24)); + cal.set(Calendar.MINUTE, rand.nextInt(60)); + cal.set(Calendar.SECOND, rand.nextInt(30) * 2); // Zip files have 2 second resolution. + + crc = new CRC32(); + deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true); + test = tmp.newFile("test.zip"); + } + + @Test public void testEmpty() throws IOException { + try (ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8)) { + } + + try (ZipFile zipFile = new ZipFile(test)) { + assertThat(zipFile.entries().hasMoreElements()).isFalse(); + } + } + + @Test public void testComment() throws IOException { + try (ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8)) { + writer.setComment("test comment"); + } + + try (ZipFile zipFile = new ZipFile(test)) { + assertThat(zipFile.entries().hasMoreElements()).isFalse(); + assertThat(zipFile.getComment()).isEqualTo("test comment"); + } + } + + @Test public void testFileDataBeforeEntry() throws IOException { + try (ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8)) { + thrown.expect(ZipException.class); + thrown.expectMessage("Cannot write zip contents without first setting a ZipEntry or starting" + + " a prefix file."); + writer.write(new byte[] { 0xf, 0xa, 0xb }); + } + + try (ZipFile zipFile = new ZipFile(test)) { + assertThat(zipFile.entries().hasMoreElements()).isFalse(); + } + } + + @Test public void testSingleEntry() throws IOException { + ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8); + byte[] content = "content".getBytes(UTF_8); + crc.update(content); + ZipFileEntry entry = new ZipFileEntry("foo"); + entry.setMethod(Compression.STORED); + entry.setSize(content.length); + entry.setCompressedSize(content.length); + entry.setCrc(crc.getValue()); + entry.setTime(cal.getTimeInMillis()); + + writer.putNextEntry(entry); + writer.write(content); + writer.closeEntry(); + writer.close(); + + byte[] buf = new byte[128]; + try (ZipFile zipFile = new ZipFile(test)) { + ZipEntry foo = zipFile.getEntry("foo"); + assertThat(foo.getMethod()).isEqualTo(ZipEntry.STORED); + assertThat(foo.getSize()).isEqualTo(content.length); + assertThat(foo.getCompressedSize()).isEqualTo(content.length); + assertThat(foo.getCrc()).isEqualTo(crc.getValue()); + assertThat(foo.getTime()).isEqualTo(cal.getTimeInMillis()); + zipFile.getInputStream(foo).read(buf); + assertThat(Bytes.indexOf(buf, content)).isEqualTo(0); + } + } + + @Test public void testMultipleEntry() throws IOException { + ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8); + writer.setComment("file comment"); + + byte[] fooContent = "content".getBytes(UTF_8); + crc.update(fooContent); + long fooCrc = crc.getValue(); + ZipFileEntry rawFoo = new ZipFileEntry("foo"); + rawFoo.setMethod(Compression.STORED); + rawFoo.setSize(fooContent.length); + rawFoo.setCompressedSize(fooContent.length); + rawFoo.setCrc(crc.getValue()); + rawFoo.setTime(cal.getTimeInMillis()); + rawFoo.setComment("foo comment"); + + writer.putNextEntry(rawFoo); + writer.write(fooContent); + writer.closeEntry(); + + byte[] barContent = "stuff".getBytes(UTF_8); + byte[] deflatedBarContent = new byte[128]; + crc.reset(); + crc.update(barContent); + long barCrc = crc.getValue(); + deflater.setInput(barContent); + deflater.finish(); + int deflatedSize = deflater.deflate(deflatedBarContent); + ZipFileEntry rawBar = new ZipFileEntry("bar"); + rawBar.setMethod(Compression.DEFLATED); + rawBar.setSize(barContent.length); + rawBar.setCompressedSize(deflatedSize); + rawBar.setCrc(barCrc); + rawBar.setTime(cal.getTimeInMillis()); + + writer.putNextEntry(rawBar); + writer.write(deflatedBarContent, 0, deflatedSize); + writer.closeEntry(); + + writer.close(); + + byte[] buf = new byte[128]; + try (ZipFile zipFile = new ZipFile(test)) { + assertThat(zipFile.getComment()).isEqualTo("file comment"); + + ZipEntry foo = zipFile.getEntry("foo"); + assertThat(foo.getMethod()).isEqualTo(ZipEntry.STORED); + assertThat(foo.getSize()).isEqualTo(fooContent.length); + assertThat(foo.getCompressedSize()).isEqualTo(fooContent.length); + assertThat(foo.getCrc()).isEqualTo(fooCrc); + assertThat(foo.getTime()).isEqualTo(cal.getTimeInMillis()); + assertThat(foo.getComment()).isEqualTo("foo comment"); + zipFile.getInputStream(foo).read(buf); + assertThat(Bytes.indexOf(buf, fooContent)).isEqualTo(0); + + ZipEntry bar = zipFile.getEntry("bar"); + assertThat(bar.getMethod()).isEqualTo(ZipEntry.DEFLATED); + assertThat(bar.getSize()).isEqualTo(barContent.length); + assertThat(bar.getCompressedSize()).isEqualTo(deflatedSize); + assertThat(bar.getCrc()).isEqualTo(barCrc); + assertThat(bar.getTime()).isEqualTo(cal.getTimeInMillis()); + zipFile.getInputStream(bar).read(buf); + assertThat(Bytes.indexOf(buf, barContent)).isEqualTo(0); + } + } + + @Test public void testWrongSizeContent() throws IOException { + try (ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8)) { + byte[] content = "content".getBytes(UTF_8); + crc.update(content); + ZipFileEntry entry = new ZipFileEntry("foo"); + entry.setMethod(Compression.STORED); + entry.setSize(content.length); + entry.setCompressedSize(content.length); + entry.setCrc(crc.getValue()); + entry.setTime(cal.getTimeInMillis()); + + writer.putNextEntry(entry); + writer.write("some other content".getBytes(UTF_8)); + thrown.expect(ZipException.class); + thrown.expectMessage("Number of bytes written for the entry"); + writer.closeEntry(); + } + } + + @Test public void testRawZipEntry() throws IOException { + ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8); + byte[] content = "content".getBytes(UTF_8); + crc.update(content); + ZipFileEntry entry = new ZipFileEntry("foo"); + entry.setVersion((short) 1); + entry.setVersionNeeded((short) 2); + entry.setMethod(Compression.STORED); + entry.setSize(content.length); + entry.setCompressedSize(content.length); + entry.setCrc(crc.getValue()); + entry.setTime(cal.getTimeInMillis()); + entry.setFlags(ZipUtil.get16(new byte[]{ 0x08, 0x00 }, 0)); + entry.setInternalAttributes(ZipUtil.get16(new byte[]{ 0x34, 0x12 }, 0)); + entry.setExternalAttributes(ZipUtil.get32(new byte[]{ 0x0a, 0x09, 0x78, 0x56 }, 0)); + entry.setLocalHeaderOffset(rand.nextInt(Integer.MAX_VALUE)); + + writer.putNextEntry(entry); + writer.write(content); + writer.closeEntry(); + writer.close(); + + byte[] buf = new byte[128]; + try (ZipFile zipFile = new ZipFile(test)) { + ZipEntry foo = zipFile.getEntry("foo"); + assertThat(foo.getMethod()).isEqualTo(ZipEntry.STORED); + assertThat(foo.getSize()).isEqualTo(content.length); + assertThat(foo.getCompressedSize()).isEqualTo(content.length); + assertThat(foo.getCrc()).isEqualTo(crc.getValue()); + assertThat(foo.getTime()).isEqualTo(cal.getTimeInMillis()); + zipFile.getInputStream(foo).read(buf); + assertThat(Bytes.indexOf(buf, content)).isEqualTo(0); + } + + try (ZipReader zipFile = new ZipReader(test)) { + ZipFileEntry foo = zipFile.getEntry("foo"); + // Versions should be increased to minimum required for STORED compression. + assertThat(foo.getVersion()).isEqualTo((short) 0xa); + assertThat(foo.getVersionNeeded()).isEqualTo((short) 0xa); + assertThat(foo.getFlags()).isEqualTo((short) 0); // Data descriptor bit should be cleared. + assertThat(foo.getInternalAttributes()).isEqualTo((short) 4660); + assertThat(foo.getExternalAttributes()).isEqualTo(1450707210); + } + } + + @Test public void testPrefixFile() throws IOException, InterruptedException { + ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8); + + writer.startPrefixFile(); + writer.write("#!/bin/bash\necho 'hello world'\n".getBytes(UTF_8)); + writer.endPrefixFile(); + + byte[] content = "content".getBytes(UTF_8); + crc.update(content); + ZipFileEntry entry = new ZipFileEntry("foo"); + entry.setMethod(Compression.STORED); + entry.setSize(content.length); + entry.setCompressedSize(content.length); + entry.setCrc(crc.getValue()); + entry.setTime(cal.getTimeInMillis()); + + writer.putNextEntry(entry); + writer.write(content); + writer.closeEntry(); + writer.close(); + + byte[] buf = new byte[128]; + try (ZipFile zipFile = new ZipFile(test)) { + ZipEntry foo = zipFile.getEntry("foo"); + assertThat(foo.getMethod()).isEqualTo(ZipEntry.STORED); + assertThat(foo.getSize()).isEqualTo(content.length); + assertThat(foo.getCompressedSize()).isEqualTo(content.length); + assertThat(foo.getCrc()).isEqualTo(crc.getValue()); + assertThat(foo.getTime()).isEqualTo(cal.getTimeInMillis()); + zipFile.getInputStream(foo).read(buf); + assertThat(Bytes.indexOf(buf, content)).isEqualTo(0); + } + + Process pr = new ProcessBuilder("chmod", "750", test.getAbsolutePath()).start(); + pr.waitFor(); + pr = new ProcessBuilder(test.getAbsolutePath()).start(); + pr.getInputStream().read(buf); + pr.waitFor(); + assertThat(Bytes.indexOf(buf, "hello world".getBytes(UTF_8))).isEqualTo(0); + } + + @Test public void testPrefixFileAfterZip() throws IOException { + try (ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8)) { + byte[] content = "content".getBytes(UTF_8); + crc.update(content); + ZipFileEntry entry = new ZipFileEntry("foo"); + entry.setMethod(Compression.STORED); + entry.setSize(content.length); + entry.setCompressedSize(content.length); + entry.setCrc(crc.getValue()); + entry.setTime(cal.getTimeInMillis()); + + writer.putNextEntry(entry); + thrown.expect(ZipException.class); + thrown.expectMessage("Cannot add a prefix file after the zip contents have been started."); + writer.startPrefixFile(); + writer.write("#!/bin/bash\necho 'hello world'\n".getBytes(UTF_8)); + writer.endPrefixFile(); + } + } + + @Test public void testPrefixAfterFinish() throws IOException { + try (ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8)) { + writer.finish(); + thrown.expect(IllegalStateException.class); + writer.startPrefixFile(); + writer.write("#!/bin/bash\necho 'hello world'\n".getBytes(UTF_8)); + writer.endPrefixFile(); + } + } + + @Test public void testPutEntryAfterFinish() throws IOException { + try (ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8)) { + writer.finish(); + thrown.expect(IllegalStateException.class); + writer.putNextEntry(new ZipFileEntry("foo")); + } + } + + @Test public void testCloseEntryAfterFinish() throws IOException { + try (ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8)) { + byte[] content = "content".getBytes(UTF_8); + crc.update(content); + ZipFileEntry entry = new ZipFileEntry("foo"); + entry.setMethod(Compression.STORED); + entry.setSize(content.length); + entry.setCompressedSize(content.length); + entry.setCrc(crc.getValue()); + entry.setTime(cal.getTimeInMillis()); + + writer.putNextEntry(entry); + writer.write(content); + writer.finish(); + thrown.expect(IllegalStateException.class); + writer.closeEntry(); + } + } + + @Test public void testFinishAfterFinish() throws IOException { + try (ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8)) { + writer.finish(); + thrown.expect(IllegalStateException.class); + writer.finish(); + } + } + + @Test public void testWriteAfterFinish() throws IOException { + try (ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8)) { + writer.finish(); + thrown.expect(IllegalStateException.class); + writer.write("content".getBytes(UTF_8)); + } + } +} |