• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package org.apache.harmony.tests.java.util.zip;
18 
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.lang.reflect.Field;
25 import java.nio.file.attribute.FileTime;
26 import java.time.Duration;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.zip.CRC32;
30 import java.util.zip.ZipEntry;
31 import java.util.zip.ZipException;
32 import java.util.zip.ZipInputStream;
33 import java.util.zip.ZipOutputStream;
34 import libcore.junit.junit3.TestCaseWithRules;
35 import libcore.junit.util.ResourceLeakageDetector.DisableResourceLeakageDetection;
36 import libcore.junit.util.ResourceLeakageDetector;
37 import libcore.test.annotation.NonCts;
38 import libcore.test.reasons.NonCtsReasons;
39 
40 import org.junit.Rule;
41 import org.junit.rules.TestRule;
42 
43 public class ZipOutputStreamTest extends TestCaseWithRules {
44     @Rule
45     public TestRule guardRule = ResourceLeakageDetector.getRule();
46 
47     ZipOutputStream zos;
48 
49     ByteArrayOutputStream bos;
50 
51     ZipInputStream zis;
52 
53     static final String data = "HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorld";
54 
55     /**
56      * java.util.zip.ZipOutputStream#close()
57      */
test_close()58     public void test_close() throws Exception {
59         zos.putNextEntry(new ZipEntry("XX"));
60         zos.closeEntry();
61         zos.close();
62 
63         // Regression for HARMONY-97
64         ZipOutputStream zos = new ZipOutputStream(new ByteArrayOutputStream());
65         zos.putNextEntry(new ZipEntry("myFile"));
66         zos.close();
67         zos.close(); // Should be a no-op
68     }
69 
70     /**
71      * java.util.zip.ZipOutputStream#closeEntry()
72      */
test_closeEntry()73     public void test_closeEntry() throws IOException {
74         ZipEntry ze = new ZipEntry("testEntry");
75         ze.setTime(System.currentTimeMillis());
76         zos.putNextEntry(ze);
77         zos.write("Hello World".getBytes("UTF-8"));
78         zos.closeEntry();
79         assertTrue("closeEntry failed to update required fields",
80                 ze.getSize() == 11 && ze.getCompressedSize() == 13);
81 
82     }
83 
84     /**
85      * java.util.zip.ZipOutputStream#finish()
86      */
test_finish()87     public void test_finish() throws Exception {
88         ZipEntry ze = new ZipEntry("test");
89         zos.putNextEntry(ze);
90         zos.write("Hello World".getBytes());
91         zos.finish();
92         assertEquals("Finish failed to closeCurrentEntry", 11, ze.getSize());
93 
94         ZipOutputStream zos = new ZipOutputStream(new ByteArrayOutputStream());
95         zos.putNextEntry(new ZipEntry("myFile"));
96         zos.finish();
97         zos.close();
98         try {
99             zos.finish();
100             fail("Assert 0: Expected IOException");
101         } catch (IOException e) {
102             // Expected
103         }
104     }
105 
106     /**
107      * java.util.zip.ZipOutputStream#putNextEntry(java.util.zip.ZipEntry)
108      */
test_putNextEntryLjava_util_zip_ZipEntry()109     public void test_putNextEntryLjava_util_zip_ZipEntry() throws IOException {
110         ZipEntry ze = new ZipEntry("testEntry");
111         ze.setTime(System.currentTimeMillis());
112         zos.putNextEntry(ze);
113         zos.write("Hello World".getBytes());
114         zos.closeEntry();
115         zos.close();
116         zis = new ZipInputStream(new ByteArrayInputStream(bos.toByteArray()));
117         ZipEntry ze2 = zis.getNextEntry();
118         zis.closeEntry();
119         assertEquals("Failed to write correct entry", ze.getName(), ze2.getName());
120         assertEquals("Failed to write correct entry", ze.getCrc(), ze2.getCrc());
121         try {
122             zos.putNextEntry(ze);
123             fail("Entry with incorrect setting failed to throw exception");
124         } catch (IOException e) {
125             // expected
126         }
127     }
128 
129     /**
130      * java.util.zip.ZipOutputStream#setComment(java.lang.String)
131      */
132     @DisableResourceLeakageDetection(
133             why = "InflaterOutputStream.close() does not work properly if finish() throws an"
134                     + " exception; finish() throws an exception if the output is invalid; this is"
135                     + " an issue with the ZipOutputStream created in setUp()",
136             bug = "31797037")
test_setCommentLjava_lang_String()137     public void test_setCommentLjava_lang_String() {
138         // There is no way to get the comment back, so no way to determine if
139         // the comment is set correct
140         zos.setComment("test setComment");
141 
142         try {
143             zos.setComment(new String(new byte[0xFFFF + 1]));
144             fail("Comment over 0xFFFF in length should throw exception");
145         } catch (IllegalArgumentException e) {
146             // Passed
147         }
148     }
149 
150     /**
151      * java.util.zip.ZipOutputStream#setLevel(int)
152      */
test_setLevelI()153     public void test_setLevelI() throws IOException {
154         ZipEntry ze = new ZipEntry("test");
155         zos.putNextEntry(ze);
156         zos.write(data.getBytes());
157         zos.closeEntry();
158         long csize = ze.getCompressedSize();
159         zos.setLevel(9); // Max Compression
160         zos.putNextEntry(ze = new ZipEntry("test2"));
161         zos.write(data.getBytes());
162         zos.closeEntry();
163         assertTrue("setLevel failed", csize <= ze.getCompressedSize());
164     }
165 
166     /**
167      * java.util.zip.ZipOutputStream#setMethod(int)
168      */
test_setMethodI()169     public void test_setMethodI() throws IOException {
170         ZipEntry ze = new ZipEntry("test");
171         zos.setMethod(ZipOutputStream.STORED);
172         CRC32 tempCrc = new CRC32();
173         tempCrc.update(data.getBytes());
174         ze.setCrc(tempCrc.getValue());
175         ze.setSize(new String(data).length());
176         zos.putNextEntry(ze);
177         zos.write(data.getBytes());
178         zos.closeEntry();
179         long csize = ze.getCompressedSize();
180         zos.setMethod(ZipOutputStream.DEFLATED);
181         zos.putNextEntry(ze = new ZipEntry("test2"));
182         zos.write(data.getBytes());
183         zos.closeEntry();
184         assertTrue("setLevel failed", csize >= ze.getCompressedSize());
185     }
186 
187     /**
188      * java.util.zip.ZipOutputStream#write(byte[], int, int)
189      */
190     @DisableResourceLeakageDetection(
191             why = "InflaterOutputStream.close() does not work properly if finish() throws an"
192                     + " exception; finish() throws an exception if the output is invalid.",
193             bug = "31797037")
test_write$BII()194     public void test_write$BII() throws IOException {
195         ZipEntry ze = new ZipEntry("test");
196         zos.putNextEntry(ze);
197         zos.write(data.getBytes());
198         zos.closeEntry();
199         zos.close();
200         zos = null;
201         zis = new ZipInputStream(new ByteArrayInputStream(bos.toByteArray()));
202         zis.getNextEntry();
203         byte[] b = new byte[data.length()];
204         int r = 0;
205         int count = 0;
206         while (count != b.length && (r = zis.read(b, count, b.length)) != -1) {
207             count += r;
208         }
209         zis.closeEntry();
210         assertEquals("Write failed to write correct bytes", new String(b), data);
211 
212         File f = File.createTempFile("testZip", "tst");
213         f.deleteOnExit();
214         FileOutputStream stream = new FileOutputStream(f);
215         ZipOutputStream zip = new ZipOutputStream(stream);
216         zip.setMethod(ZipEntry.STORED);
217 
218         try {
219             zip.putNextEntry(new ZipEntry("Second"));
220             fail("Not set an entry. Should have thrown ZipException.");
221         } catch (ZipException e) {
222             // expected -- We have not set an entry
223         }
224 
225         try {
226             // We try to write data without entry
227             zip.write(new byte[2]);
228             fail("Writing data without an entry. Should have thrown IOException");
229         } catch (IOException e) {
230             // expected
231         }
232 
233         try {
234             // Try to write without an entry and with nonsense offset and
235             // length
236             zip.write(new byte[2], 0, 12);
237             fail("Writing data without an entry. Should have thrown IndexOutOfBoundsException");
238         } catch (IndexOutOfBoundsException e) {
239             // expected
240         }
241 
242         // Regression for HARMONY-4405
243         try {
244             zip.write(null, 0, -2);
245             fail();
246         } catch (NullPointerException expected) {
247         } catch (IndexOutOfBoundsException expected) {
248         }
249         try {
250             zip.write(null, 0, 2);
251             fail();
252         } catch (NullPointerException expected) {
253         }
254         try {
255             zip.write(new byte[2], 0, -2);
256             fail();
257         } catch (IndexOutOfBoundsException expected) {
258         }
259 
260         // Close stream because ZIP is invalid
261         stream.close();
262     }
263 
264     /**
265      * java.util.zip.ZipOutputStream#write(byte[], int, int)
266      */
267     @DisableResourceLeakageDetection(
268             why = "InflaterOutputStream.close() does not work properly if finish() throws an"
269                     + " exception; finish() throws an exception if the output is invalid; this is"
270                     + " an issue with the ZipOutputStream created in setUp()",
271             bug = "31797037")
test_write$BII_2()272     public void test_write$BII_2() throws IOException {
273         // Regression for HARMONY-577
274         File f1 = File.createTempFile("testZip1", "tst");
275         f1.deleteOnExit();
276         FileOutputStream stream1 = new FileOutputStream(f1);
277         ZipOutputStream zip1 = new ZipOutputStream(stream1);
278         zip1.putNextEntry(new ZipEntry("one"));
279         zip1.setMethod(ZipOutputStream.STORED);
280         zip1.setMethod(ZipEntry.STORED);
281 
282         zip1.write(new byte[2]);
283 
284         try {
285             zip1.putNextEntry(new ZipEntry("Second"));
286             fail("ZipException expected");
287         } catch (ZipException e) {
288             // expected - We have not set an entry
289         }
290 
291         try {
292             zip1.write(new byte[2]); // try to write data without entry
293             fail("expected IOE there");
294         } catch (IOException e2) {
295             // expected
296         }
297 
298         zip1.close();
299     }
300     /**
301      * Test standard and info-zip-extended timestamp rounding
302      */
test_timeSerializationRounding()303     public void test_timeSerializationRounding() throws Exception {
304         List<ZipEntry> entries = new ArrayList<>();
305         ZipEntry zipEntry;
306 
307         entries.add(zipEntry = new ZipEntry("test1"));
308         final long someTimestamp = 1479139143200L;
309         zipEntry.setTime(someTimestamp);
310 
311         entries.add(zipEntry = new ZipEntry("test2"));
312         zipEntry.setLastModifiedTime(FileTime.fromMillis(someTimestamp));
313 
314         for (ZipEntry entry : entries) {
315             zos.putNextEntry(entry);
316             zos.write(data.getBytes());
317             zos.closeEntry();
318         }
319         zos.close();
320 
321         try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
322            // getTime should be rounded down to a multiple of 2s
323             ZipEntry readEntry = zis.getNextEntry();
324             assertEquals((someTimestamp / 2000) * 2000,
325                          readEntry.getTime());
326 
327             // With extended timestamp getTime&getLastModifiedTime should berounded down to a
328             // multiple of 1s
329             readEntry = zis.getNextEntry();
330             assertEquals((someTimestamp / 1000) * 1000,
331                          readEntry.getLastModifiedTime().toMillis());
332             assertEquals((someTimestamp / 1000) * 1000,
333                          readEntry.getTime());
334         }
335     }
336 
337     /**
338      * Test info-zip extended timestamp support
339      */
340     @NonCts(bug = 310050493, reason = NonCtsReasons.NON_BREAKING_BEHAVIOR_FIX)
test_exttSupport()341     public void test_exttSupport() throws Exception {
342         List<ZipEntry> entries = new ArrayList<>();
343 
344         ZipEntry zipEntry;
345 
346         // There's no practical way to access ONLY mtime
347         Field mtimeField = ZipEntry.class.getDeclaredField("mtime");
348         mtimeField.setAccessible(true);
349 
350         // Serialized DOS timestamp resolution is 2s. Serialized extended
351         // timestamp resolution is 1s. If we won't use rounded values then
352         // asserting time equality would be more complicated (resolution of
353         // getTime depends weather we use extended timestamp).
354         //
355         // We have to call setTime on all entries. If it's not set then
356         // ZipOutputStream will call setTime(System.currentTimeMillis()) on it.
357         // I will use this as a excuse to test whether setting particular time
358         // values (~< 1980 ~> 2099) triggers use of the extended last-modified
359         // timestamp.
360         final long timestampWithinDostimeBound = ZipEntry.UPPER_DOSTIME_BOUND;
361         assertEquals(0, timestampWithinDostimeBound % 1000);
362         final long timestampBeyondDostimeBound = ZipEntry.UPPER_DOSTIME_BOUND + 2000;
363         assertEquals(0, timestampBeyondDostimeBound % 1000);
364 
365         // This will set both dos timestamp and last-modified timestamp (because < 1980)
366         entries.add(zipEntry = new ZipEntry("test_setTime"));
367         zipEntry.setTime(0);
368         assertNotNull(mtimeField.get(zipEntry));
369 
370         // Explicitly set info-zip last-modified extended timestamp
371         entries.add(zipEntry = new ZipEntry("test_setLastModifiedTime"));
372         zipEntry.setLastModifiedTime(FileTime.fromMillis(1000));
373 
374         // Set creation time and (since we have to call setTime on ZipEntry, otherwise
375         // ZipOutputStream will call setTime(System.currentTimeMillis()) and the getTime()
376         // assert will fail due to low serialization resolution) test that calling
377         // setTime with value <= ZipEntry.UPPER_DOSTIME_BOUND won't set the info-zip
378         // last-modified extended timestamp.
379         entries.add(zipEntry = new ZipEntry("test_setCreationTime"));
380         zipEntry.setCreationTime(FileTime.fromMillis(1000));
381         zipEntry.setTime(timestampWithinDostimeBound);
382         assertNull(mtimeField.get(zipEntry));
383 
384         // Set last access time and test that calling setTime with value >
385         // ZipEntry.UPPER_DOSTIME_BOUND will set the info-zip last-modified extended
386         // timestamp. ZipEntry.UPPER_DOSTIME_BOUND is lower than actual DOS time upper
387         // bound, so adding 3 years to make sure that it is really out of upper bound.
388         entries.add(zipEntry = new ZipEntry("test_setLastAccessTime"));
389         zipEntry.setLastAccessTime(FileTime.fromMillis(3000));
390         zipEntry.setTime(timestampBeyondDostimeBound + Duration.ofDays(3 * 365).toMillis());
391         assertNotNull(mtimeField.get(zipEntry));
392 
393         for (ZipEntry entry : entries) {
394             zos.putNextEntry(entry);
395             zos.write(data.getBytes());
396             zos.closeEntry();
397         }
398         zos.close();
399 
400         try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
401             for (ZipEntry entry : entries) {
402                 ZipEntry readEntry = zis.getNextEntry();
403                 assertEquals(entry.getName(), readEntry.getName());
404                 assertEquals(entry.getName(), entry.getTime(), readEntry.getTime());
405                 assertEquals(entry.getName(), entry.getLastModifiedTime(), readEntry.getLastModifiedTime());
406                 assertEquals(entry.getLastAccessTime(), readEntry.getLastAccessTime());
407                 assertEquals(entry.getCreationTime(), readEntry.getCreationTime());
408             }
409         }
410     }
411 
412 
413     @Override
setUp()414     protected void setUp() throws Exception {
415         super.setUp();
416         zos = new ZipOutputStream(bos = new ByteArrayOutputStream());
417     }
418 
419     @Override
tearDown()420     protected void tearDown() throws Exception {
421         try {
422             // Close the ZipInputStream first as that does not fail.
423             if (zis != null) {
424                 zis.close();
425             }
426             if (zos != null) {
427                 // This will throw a ZipException if nothing is written to the ZipOutputStream.
428                 zos.close();
429             }
430         } catch (Exception e) {
431         }
432         super.tearDown();
433     }
434 }
435