1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package libcore.java.util.zip;
18 
19 import libcore.io.Streams;
20 import tests.support.resource.Support_Resources;
21 
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.util.Arrays;
27 import java.util.Random;
28 import java.util.zip.ZipEntry;
29 import java.util.zip.ZipInputStream;
30 import java.util.zip.ZipOutputStream;
31 import libcore.junit.junit3.TestCaseWithRules;
32 import libcore.junit.util.ResourceLeakageDetector;
33 import org.junit.Rule;
34 import org.junit.rules.TestRule;
35 
36 public final class ZipInputStreamTest extends TestCaseWithRules {
37     @Rule
38     public TestRule guardRule = ResourceLeakageDetector.getRule();
39 
testShortMessage()40     public void testShortMessage() throws IOException {
41         byte[] data = "Hello World".getBytes("UTF-8");
42         byte[] zipped = ZipOutputStreamTest.zip("short", data);
43         assertEquals(Arrays.toString(data), Arrays.toString(unzip("short", zipped)));
44     }
45 
testLongMessage()46     public void testLongMessage() throws IOException {
47         byte[] data = new byte[1024 * 1024];
48         new Random().nextBytes(data);
49         assertTrue(Arrays.equals(data, unzip("r", ZipOutputStreamTest.zip("r", data))));
50     }
51 
unzip(String name, byte[] bytes)52     public static byte[] unzip(String name, byte[] bytes) throws IOException {
53         ZipInputStream in = new ZipInputStream(new ByteArrayInputStream(bytes));
54         ByteArrayOutputStream out = new ByteArrayOutputStream();
55 
56         ZipEntry entry = in.getNextEntry();
57         assertEquals(name, entry.getName());
58 
59         byte[] buffer = new byte[1024];
60         int count;
61         while ((count = in.read(buffer)) != -1) {
62             out.write(buffer, 0, count);
63         }
64 
65         assertNull(in.getNextEntry()); // There's only one entry in the Zip files we create.
66 
67         in.close();
68         return out.toByteArray();
69     }
70 
71     /**
72      * Reference implementation allows reading of empty zip using a {@link ZipInputStream}.
73      */
testReadEmpty()74     public void testReadEmpty() throws IOException {
75         InputStream emptyZipIn = Support_Resources.getStream("java/util/zip/EmptyArchive.zip");
76         ZipInputStream in = new ZipInputStream(emptyZipIn);
77         try {
78             ZipEntry entry = in.getNextEntry();
79             assertNull("An empty zip has no entries", entry);
80         } finally {
81             in.close();
82         }
83     }
84 
85     // NOTE: Using octal because it's easiest to use "hexdump -b" to dump file contents.
86     private static final byte[] INCOMPLETE_ZIP = new byte[] {
87             0120, 0113, 0003, 0004, 0024, 0000, 0010, 0010, 0010, 0000, 0002, 0035, (byte) 0330,
88             0106, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0013,
89             0000, 0000, 0000, 0146, 0157, 0157, 0057, 0142, 0141, 0162, 0056, 0160, 0156, 0147 };
90 
91     // http://b//21846904
testReadOnIncompleteStream()92     public void testReadOnIncompleteStream() throws Exception {
93         ZipInputStream zi = new ZipInputStream(new ByteArrayInputStream(INCOMPLETE_ZIP));
94         ZipEntry ze = zi.getNextEntry();
95 
96         // read() and closeEntry() must throw IOExceptions to indicate that
97         // the stream is corrupt. The bug above reported that they would loop
98         // forever.
99         try {
100             zi.read(new byte[1024], 0, 1024);
101             fail();
102         } catch (IOException expected) {
103         }
104 
105         try {
106             zi.closeEntry();
107             fail();
108         } catch (IOException expected) {
109         }
110 
111         zi.close();
112     }
113 
testAvailable()114     public void testAvailable() throws Exception {
115         // NOTE: We don't care about the contents of any of these entries as long as they're
116         // not empty.
117         ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(
118                 zip(new String[] { "foo", "bar", "baz" }, new byte[] { 0, 0, 0, 1, 1, 1 })));
119 
120         assertEquals(1, zis.available());
121         zis.getNextEntry();
122         assertEquals(1, zis.available());
123         zis.closeEntry();
124         // On Android M and below, this call would return "1". That seems a bit odd given that the
125         // contract for available states that we should return 1 if there are any bytes left to read
126         // from the "current" entry.
127         assertEquals(0, zis.available());
128 
129         // There shouldn't be any bytes left to read if the entry is fully consumed...
130         zis.getNextEntry();
131         Streams.readFullyNoClose(zis);
132         assertEquals(0, zis.available());
133 
134         // ... or if the entry is fully skipped over.
135         zis.getNextEntry();
136         zis.skip(Long.MAX_VALUE);
137         assertEquals(0, zis.available());
138 
139         // There are no entries left in the file, so there whould be nothing left to read.
140         assertNull(zis.getNextEntry());
141         assertEquals(0, zis.available());
142 
143         zis.close();
144     }
145 
146     private static final byte[] ZIP_WITH_DATA_DESCRIPTOR = new byte[] {
147 (byte) 80, 75, 3, 4, 10, 0, 8, 0, 0, 0, -51, 90, -121, 80, -20, 62, -84, -103, 2, 0, 0, 0, 2, 0, 0, 0, 8, 0, 28, 0, 116, 101, 115, 116, 46, 116, 120, 116, 85, 84, 9, 0, 3, 97, 84, -116, 94, 102, 84, -116, 94, 117, 120, 11, 0, 1, 4, -119, 66, 0, 0, 4, 83, 95, 1, 0, 72, 10, 80, 75, 7, 8, -20, 62, -84, -103, 2, 0, 0, 0, 2, 0, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, -51, 90, -121, 80, -20, 62, -84, -103, 2, 0, 0, 0, 2, 0, 0, 0, 8, 0, 24, 0, 0, 0, 0, 0, 1, 0, 0, 0, -92, -127, 0, 0, 0, 0, 116, 101, 115, 116, 46, 116, 120, 116, 85, 84, 5, 0, 3, 97, 84, -116, 94, 117, 120, 11, 0, 1, 4, -119, 66, 0, 0, 4, 83, 95, 1, 0, 80, 75, 5, 6, 0, 0, 0, 0, 1, 0, 1, 0, 78, 0, 0, 0, 84, 0, 0, 0, 0, 0 };
148 
testDataDescriptorOnStoredEntry()149     public void testDataDescriptorOnStoredEntry() throws Exception {
150         ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(
151                 ZIP_WITH_DATA_DESCRIPTOR));
152 
153         ZipEntry entry = zis.getNextEntry();
154         assertEquals("test.txt", entry.getName());
155 
156         zis.close();
157     }
158 
zip(String[] names, byte[] bytes)159     private static byte[] zip(String[] names, byte[] bytes) throws IOException {
160         ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
161         ZipOutputStream zippedOut = new ZipOutputStream(bytesOut);
162 
163         for (String name : names) {
164             ZipEntry entry = new ZipEntry(name);
165             zippedOut.putNextEntry(entry);
166             zippedOut.write(bytes);
167             zippedOut.closeEntry();
168         }
169 
170         zippedOut.close();
171         return bytesOut.toByteArray();
172     }
173 }
174