blob: fa7b8c76661888faa87affab737561073cd5bcea [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* 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 libcore.java.util.zip;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Random;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import junit.framework.TestCase;
public final class ZipFileTest extends TestCase {
/**
* Exercise Inflater's ability to refill the zlib's input buffer. As of this
* writing, this buffer's max size is 64KiB compressed bytes. We'll write a
* full megabyte of uncompressed data, which should be sufficient to exhaust
* the buffer. http://b/issue?id=2734751
*/
public void testInflatingFilesRequiringZipRefill() throws IOException {
int originalSize = 1024 * 1024;
byte[] readBuffer = new byte[8192];
ZipFile zipFile = new ZipFile(createZipFile(1, originalSize));
for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
ZipEntry zipEntry = e.nextElement();
assertTrue("This test needs >64 KiB of compressed data to exercise Inflater",
zipEntry.getCompressedSize() > (64 * 1024));
InputStream is = zipFile.getInputStream(zipEntry);
while (is.read(readBuffer, 0, readBuffer.length) != -1) {}
is.close();
}
zipFile.close();
}
public void testInflatingStreamsRequiringZipRefill() throws IOException {
int originalSize = 1024 * 1024;
byte[] readBuffer = new byte[8192];
ZipInputStream in = new ZipInputStream(new FileInputStream(createZipFile(1, originalSize)));
while (in.getNextEntry() != null) {
while (in.read(readBuffer, 0, readBuffer.length) != -1) {}
}
in.close();
}
public void testZipFileWithLotsOfEntries() throws IOException {
int expectedEntryCount = 64*1024 - 1;
File f = createZipFile(expectedEntryCount, 0);
ZipFile zipFile = new ZipFile(f);
int entryCount = 0;
for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
ZipEntry zipEntry = e.nextElement();
++entryCount;
}
assertEquals(expectedEntryCount, entryCount);
zipFile.close();
}
// http://code.google.com/p/android/issues/detail?id=36187
public void testZipFileLargerThan2GiB() throws IOException {
if (false) { // TODO: this test requires too much time and too much disk space!
File f = createZipFile(1024, 3*1024*1024);
ZipFile zipFile = new ZipFile(f);
int entryCount = 0;
for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
ZipEntry zipEntry = e.nextElement();
++entryCount;
}
assertEquals(1024, entryCount);
zipFile.close();
}
}
public void testZip64Support() throws IOException {
try {
createZipFile(64*1024, 0);
fail(); // Make this test more like testHugeZipFile when we have Zip64 support.
} catch (ZipException expected) {
}
}
/**
* Compresses the given number of files, each of the given size, into a .zip archive.
*/
private File createZipFile(int entryCount, int entrySize) throws IOException {
File result = createTemporaryZipFile();
byte[] writeBuffer = new byte[8192];
Random random = new Random();
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(result)));
for (int entry = 0; entry < entryCount; ++entry) {
ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
out.putNextEntry(ze);
for (int i = 0; i < entrySize; i += writeBuffer.length) {
random.nextBytes(writeBuffer);
int byteCount = Math.min(writeBuffer.length, entrySize - i);
out.write(writeBuffer, 0, byteCount);
}
out.closeEntry();
}
out.close();
return result;
}
private File createTemporaryZipFile() throws IOException {
File result = File.createTempFile("ZipFileTest", "zip");
result.deleteOnExit();
return result;
}
private ZipOutputStream createZipOutputStream(File f) throws IOException {
return new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
}
public void testSTORED() throws IOException {
ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
CRC32 crc = new CRC32();
// Missing CRC, size, and compressed size => failure.
try {
ZipEntry ze = new ZipEntry("a");
ze.setMethod(ZipEntry.STORED);
out.putNextEntry(ze);
fail();
} catch (ZipException expected) {
}
// Missing CRC and compressed size => failure.
try {
ZipEntry ze = new ZipEntry("a");
ze.setMethod(ZipEntry.STORED);
ze.setSize(0);
out.putNextEntry(ze);
fail();
} catch (ZipException expected) {
}
// Missing CRC and size => failure.
try {
ZipEntry ze = new ZipEntry("a");
ze.setMethod(ZipEntry.STORED);
ze.setSize(0);
ze.setCompressedSize(0);
out.putNextEntry(ze);
fail();
} catch (ZipException expected) {
}
// Missing size and compressed size => failure.
try {
ZipEntry ze = new ZipEntry("a");
ze.setMethod(ZipEntry.STORED);
ze.setCrc(crc.getValue());
out.putNextEntry(ze);
fail();
} catch (ZipException expected) {
}
// Missing size is copied from compressed size.
{
ZipEntry ze = new ZipEntry("okay1");
ze.setMethod(ZipEntry.STORED);
ze.setCrc(crc.getValue());
assertEquals(-1, ze.getSize());
assertEquals(-1, ze.getCompressedSize());
ze.setCompressedSize(0);
assertEquals(-1, ze.getSize());
assertEquals(0, ze.getCompressedSize());
out.putNextEntry(ze);
assertEquals(0, ze.getSize());
assertEquals(0, ze.getCompressedSize());
}
// Missing compressed size is copied from size.
{
ZipEntry ze = new ZipEntry("okay2");
ze.setMethod(ZipEntry.STORED);
ze.setCrc(crc.getValue());
assertEquals(-1, ze.getSize());
assertEquals(-1, ze.getCompressedSize());
ze.setSize(0);
assertEquals(0, ze.getSize());
assertEquals(-1, ze.getCompressedSize());
out.putNextEntry(ze);
assertEquals(0, ze.getSize());
assertEquals(0, ze.getCompressedSize());
}
// Mismatched size and compressed size => failure.
try {
ZipEntry ze = new ZipEntry("a");
ze.setMethod(ZipEntry.STORED);
ze.setCrc(crc.getValue());
ze.setCompressedSize(1);
ze.setSize(0);
out.putNextEntry(ze);
fail();
} catch (ZipException expected) {
}
// Everything present => success.
ZipEntry ze = new ZipEntry("okay");
ze.setMethod(ZipEntry.STORED);
ze.setCrc(crc.getValue());
ze.setSize(0);
ze.setCompressedSize(0);
out.putNextEntry(ze);
out.close();
}
private String makeString(int count, String ch) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; ++i) {
sb.append(ch);
}
return sb.toString();
}
public void testComment() throws Exception {
String expectedFileComment = "1 \u0666 2";
String expectedEntryComment = "a \u0666 b";
File file = createTemporaryZipFile();
ZipOutputStream out = createZipOutputStream(file);
// Is file comment length checking done on bytes or characters? (Should be bytes.)
out.setComment(null);
out.setComment(makeString(0xffff, "a"));
try {
out.setComment(makeString(0xffff + 1, "a"));
fail();
} catch (IllegalArgumentException expected) {
}
try {
out.setComment(makeString(0xffff, "\u0666"));
fail();
} catch (IllegalArgumentException expected) {
}
ZipEntry ze = new ZipEntry("a");
// Is entry comment length checking done on bytes or characters? (Should be bytes.)
ze.setComment(null);
ze.setComment(makeString(0xffff, "a"));
try {
ze.setComment(makeString(0xffff + 1, "a"));
fail();
} catch (IllegalArgumentException expected) {
}
try {
ze.setComment(makeString(0xffff, "\u0666"));
fail();
} catch (IllegalArgumentException expected) {
}
ze.setComment(expectedEntryComment);
out.putNextEntry(ze);
out.closeEntry();
out.setComment(expectedFileComment);
out.close();
ZipFile zipFile = new ZipFile(file);
// TODO: there's currently no API for reading the file comment --- strings(1) the file?
assertEquals(expectedEntryComment, zipFile.getEntry("a").getComment());
zipFile.close();
}
public void testNameLengthChecks() throws IOException {
// Is entry name length checking done on bytes or characters?
// Really it should be bytes, but the RI only checks characters at construction time.
// Android does the same, because it's cheap...
try {
new ZipEntry((String) null);
fail();
} catch (NullPointerException expected) {
}
new ZipEntry(makeString(0xffff, "a"));
try {
new ZipEntry(makeString(0xffff + 1, "a"));
fail();
} catch (IllegalArgumentException expected) {
}
// ...but Android won't let you create a zip file with a truncated name.
ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
ZipEntry ze = new ZipEntry(makeString(0xffff, "\u0666"));
try {
out.putNextEntry(ze);
fail(); // The RI fails this test; it just checks the character count at construction time.
} catch (IllegalArgumentException expected) {
}
out.closeEntry();
out.putNextEntry(new ZipEntry("okay")); // ZipOutputStream.close throws if you add nothing!
out.close();
}
public void testCrc() throws IOException {
ZipEntry ze = new ZipEntry("test");
ze.setMethod(ZipEntry.STORED);
ze.setSize(4);
// setCrc takes a long, not an int, so -1 isn't a valid CRC32 (because it's 64 bits).
try {
ze.setCrc(-1);
} catch (IllegalArgumentException expected) {
}
// You can set the CRC32 to 0xffffffff if you're slightly more careful though...
ze.setCrc(0xffffffffL);
assertEquals(0xffffffffL, ze.getCrc());
// And it actually works, even though we use -1L to mean "no CRC set"...
ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
out.putNextEntry(ze);
out.write(-1);
out.write(-1);
out.write(-1);
out.write(-1);
out.closeEntry();
out.close();
}
}