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
17package libcore.java.util.zip;
18
19import java.io.BufferedOutputStream;
20import java.io.File;
21import java.io.FileInputStream;
22import java.io.FileOutputStream;
23import java.io.IOException;
24import java.util.Arrays;
25import java.util.List;
26import java.util.jar.JarEntry;
27import java.util.zip.ZipEntry;
28import java.util.zip.ZipException;
29import java.util.zip.ZipFile;
30import java.util.zip.ZipInputStream;
31import java.util.zip.ZipOutputStream;
32
33public class ZipEntryTest extends junit.framework.TestCase {
34  private static File createTemporaryZipFile() throws IOException {
35    File result = File.createTempFile("ZipFileTest", "zip");
36    result.deleteOnExit();
37    return result;
38  }
39
40  private static ZipOutputStream createZipOutputStream(File f) throws IOException {
41    return new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
42  }
43
44  private static String makeString(int count, String s) {
45    StringBuilder sb = new StringBuilder();
46    for (int i = 0; i < count; ++i) {
47      sb.append(s);
48    }
49    return sb.toString();
50  }
51
52  // http://code.google.com/p/android/issues/detail?id=4690
53  public void test_utf8FileNames() throws Exception {
54    // Create a zip file containing non-ASCII filenames.
55    File f = File.createTempFile("your", "mum");
56    List<String> filenames = Arrays.asList("us-ascii",
57                                           "\u043c\u0430\u0440\u0442\u0430", // russian
58                                           "\u1f00\u03c0\u1f78", // greek
59                                           "\u30b3\u30f3\u30cb\u30c1\u30cf"); // japanese
60    ZipOutputStream out = new ZipOutputStream(new FileOutputStream(f));
61    for (String filename : filenames) {
62      out.putNextEntry(new ZipEntry(filename));
63      out.closeEntry(); // Empty files are fine.
64    }
65    out.close();
66    // Read it back, and check we find all those names.
67    // This failed when we were mangling the encoding.
68    ZipFile zipFile = new ZipFile(f);
69    for (String filename : filenames) {
70      assertNotNull(filename, zipFile.getEntry(filename));
71    }
72    // Check that ZipInputStream works too.
73    ZipInputStream in = new ZipInputStream(new FileInputStream(f));
74    ZipEntry entry;
75    int entryCount = 0;
76    while ((entry = in.getNextEntry()) != null) {
77      assertTrue(entry.getName(), filenames.contains(entry.getName()));
78      ++entryCount;
79    }
80    assertEquals(filenames.size(), entryCount);
81    in.close();
82  }
83
84  // http://b/2099615
85  public void testClone() {
86    byte[] extra = { 5, 7, 9 };
87    JarEntry jarEntry = new JarEntry("foo");
88    jarEntry.setExtra(extra);
89    assertSame("Expected no defensive copy of extra", extra, jarEntry.getExtra());
90
91    ZipEntry clone = (ZipEntry) jarEntry.clone();
92    assertEquals(JarEntry.class, clone.getClass());
93    assertNotSame(extra, clone.getExtra());
94  }
95
96  public void testTooLongName() throws Exception {
97    String tooLongName = makeString(65536, "z");
98    try {
99      new ZipEntry(tooLongName);
100      fail();
101    } catch (IllegalArgumentException expected) {
102    }
103  }
104
105  public void testMaxLengthName() throws Exception {
106    String maxLengthName = makeString(65535, "z");
107
108    File f = createTemporaryZipFile();
109    ZipOutputStream out = createZipOutputStream(f);
110    out.putNextEntry(new ZipEntry(maxLengthName));
111    out.closeEntry();
112    out.close();
113
114    // Read it back, and check that we see the entry.
115    ZipFile zipFile = new ZipFile(f);
116    assertNotNull(zipFile.getEntry(maxLengthName));
117    zipFile.close();
118  }
119
120  public void testTooLongExtra() throws Exception {
121    byte[] tooLongExtra = new byte[65536];
122    ZipEntry ze = new ZipEntry("x");
123    try {
124      ze.setExtra(tooLongExtra);
125      fail();
126    } catch (IllegalArgumentException expected) {
127    }
128  }
129
130  public void testMaxLengthExtra() throws Exception {
131    byte[] maxLengthExtra = new byte[65535];
132
133    File f = createTemporaryZipFile();
134    ZipOutputStream out = createZipOutputStream(f);
135    ZipEntry ze = new ZipEntry("x");
136    ze.setSize(0);
137    ze.setExtra(maxLengthExtra);
138    out.putNextEntry(ze);
139    out.closeEntry();
140    out.close();
141
142    // Read it back, and check that we see the entry.
143    ZipFile zipFile = new ZipFile(f);
144    assertEquals(maxLengthExtra.length, zipFile.getEntry("x").getExtra().length);
145    zipFile.close();
146  }
147
148
149  // TODO: This test does not compile because we need to add a ZipOutputStream constructor
150  // that forces zip64. This also needs followup changes in ZipInputStream et al. to assume zip64
151  // if the header says so, and to not depend purely on the entry length.
152  //
153  // public void testMaxLengthExtra_zip64() throws Exception {
154  //   // Not quite the max length (65535), but large enough that there's no space
155  //   // for the zip64 extended info header.
156  //   byte[] maxLengthExtra = new byte[65530];
157  //
158  //   File f = createTemporaryZipFile();
159  //   ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)),
160  //           true /* forceZip64 */);
161  //   ZipEntry ze = new ZipEntry("x");
162  //
163  //   ze.setExtra(maxLengthExtra);
164  //   try {
165  //     out.putNextEntry(ze);
166  //     fail();
167  //   } catch (ZipException expected) {
168  //   }
169  // }
170
171
172  public void testTooLongComment() throws Exception {
173    String tooLongComment = makeString(65536, "z");
174    ZipEntry ze = new ZipEntry("x");
175    try {
176      ze.setComment(tooLongComment);
177      fail();
178    } catch (IllegalArgumentException expected) {
179    }
180  }
181
182  public void testMaxLengthComment() throws Exception {
183    String maxLengthComment = makeString(65535, "z");
184
185    File f = createTemporaryZipFile();
186    ZipOutputStream out = createZipOutputStream(f);
187    ZipEntry ze = new ZipEntry("x");
188    ze.setComment(maxLengthComment);
189    out.putNextEntry(ze);
190    out.closeEntry();
191    out.close();
192
193    // Read it back, and check that we see the entry.
194    ZipFile zipFile = new ZipFile(f);
195    assertEquals(maxLengthComment, zipFile.getEntry("x").getComment());
196    zipFile.close();
197  }
198
199  public void testCommentAndExtraInSameOrder() throws Exception {
200    String comment = makeString(17, "z");
201    byte[] extra = makeString(11, "a").getBytes();
202
203    File f = createTemporaryZipFile();
204    ZipOutputStream out = createZipOutputStream(f);
205
206    // Regular (non zip64) format.
207    ZipEntry ze = new ZipEntry("x");
208    ze.setSize(0);
209    ze.setExtra(extra);
210    ze.setComment(comment);
211    out.putNextEntry(ze);
212    out.closeEntry();
213
214    // An entry without a length is assumed to be zip64.
215    ze = new ZipEntry("y");
216    ze.setExtra(extra);
217    ze.setComment(comment);
218    out.putNextEntry(ze);
219    out.closeEntry();
220    out.close();
221
222    // Read it back and make sure comments and extra are in the right order
223    ZipFile zipFile = new ZipFile(f);
224    try {
225      assertEquals(comment, zipFile.getEntry("x").getComment());
226      assertTrue(Arrays.equals(extra, zipFile.getEntry("x").getExtra()));
227
228      assertEquals(comment, zipFile.getEntry("y").getComment());
229      assertTrue(Arrays.equals(extra, zipFile.getEntry("y").getExtra()));
230    } finally {
231      zipFile.close();
232    }
233  }
234}
235