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