Add Java 6's new java.util.zip API.
This passes all but four of the harmony tests. The others fail because our
Deflater works differently to theirs; we already fail the corresponding
Deflater test.
The only jtreg test for ZipError neither passes nor fails: it appears that
this can only be thrown on Windows.
Bug: 2497395
Change-Id: I79687495da42507dda692c890ec939bdbc9be2b3
diff --git a/libcore/archive/src/main/java/java/util/zip/DeflaterInputStream.java b/libcore/archive/src/main/java/java/util/zip/DeflaterInputStream.java
new file mode 100644
index 0000000..13d65e4
--- /dev/null
+++ b/libcore/archive/src/main/java/java/util/zip/DeflaterInputStream.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 java.util.zip;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * An {@code InputStream} filter to compress data. Callers read
+ * compressed data in the "deflate" format from the uncompressed
+ * underlying stream.
+ * @since 1.6
+ * @hide
+ */
+public class DeflaterInputStream extends FilterInputStream {
+ private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+ protected final Deflater deflater;
+ protected final byte[] buf;
+
+ private boolean closed = false;
+ private boolean available = true;
+
+ /**
+ * Constructs a {@code DeflaterInputStream} with a new {@code Deflater} and an
+ * implementation-defined default internal buffer size. {@code in} is a source of
+ * uncompressed data, and this stream will be a source of compressed data.
+ *
+ * @param in the source {@code InputStream}
+ */
+ public DeflaterInputStream(InputStream in) {
+ this(in, new Deflater(), DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Constructs a {@code DeflaterInputStream} with the given {@code Deflater} and an
+ * implementation-defined default internal buffer size. {@code in} is a source of
+ * uncompressed data, and this stream will be a source of compressed data.
+ *
+ * @param in the source {@code InputStream}
+ * @param deflater the {@code Deflater} to be used for compression
+ */
+ public DeflaterInputStream(InputStream in, Deflater deflater) {
+ this(in, deflater, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Constructs a {@code DeflaterInputStream} with the given {@code Deflater} and
+ * given internal buffer size. {@code in} is a source of
+ * uncompressed data, and this stream will be a source of compressed data.
+ *
+ * @param in the source {@code InputStream}
+ * @param deflater the {@code Deflater} to be used for compression
+ * @param bufferSize the length in bytes of the internal buffer
+ */
+ public DeflaterInputStream(InputStream in, Deflater deflater, int bufferSize) {
+ super(in);
+ if (in == null || deflater == null) {
+ throw new NullPointerException();
+ }
+ if (bufferSize <= 0) {
+ throw new IllegalArgumentException();
+ }
+ this.deflater = deflater;
+ this.buf = new byte[bufferSize];
+ }
+
+ /**
+ * Closes the underlying input stream and discards any remaining uncompressed
+ * data.
+ */
+ @Override
+ public void close() throws IOException {
+ closed = true;
+ deflater.end();
+ in.close();
+ }
+
+ /**
+ * Reads a byte from the compressed input stream. The result will be a byte of compressed
+ * data corresponding to an uncompressed byte or bytes read from the underlying stream.
+ *
+ * @return the byte or -1 if the end of the stream has been reached.
+ */
+ @Override
+ public int read() throws IOException {
+ byte[] result = new byte[1];
+ if (read(result, 0, 1) == -1) {
+ return -1;
+ }
+ return result[0] & 0xff;
+ }
+
+ /**
+ * Reads compressed data into a byte buffer. The result will be bytes of compressed
+ * data corresponding to an uncompressed byte or bytes read from the underlying stream.
+ *
+ * @param b
+ * the byte buffer that compressed data will be read into.
+ * @param off
+ * the offset in the byte buffer where compressed data will start
+ * to be read into.
+ * @param len
+ * the length of the compressed data that is expected to read.
+ * @return the number of bytes read or -1 if the end of the compressed input
+ * stream has been reached.
+ */
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ checkClosed();
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ if (off < 0 || len < 0 || len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (len == 0) {
+ return 0;
+ }
+
+ if (!available) {
+ return -1;
+ }
+
+ int count = 0;
+ while (count < len && !deflater.finished()) {
+ if (deflater.needsInput()) {
+ // read data from input stream
+ int byteCount = in.read(buf);
+ if (byteCount == -1) {
+ deflater.finish();
+ } else {
+ deflater.setInput(buf, 0, byteCount);
+ }
+ }
+ int byteCount = deflater.deflate(buf, 0, Math.min(buf.length, len - count));
+ if (byteCount == -1) {
+ break;
+ }
+ System.arraycopy(buf, 0, b, off + count, byteCount);
+ count += byteCount;
+ }
+ if (count == 0) {
+ count = -1;
+ available = false;
+ }
+ return count;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>Note: if {@code n > Integer.MAX_VALUE}, this stream will only attempt to
+ * skip {@code Integer.MAX_VALUE} bytes.
+ */
+ @Override
+ public long skip(long n) throws IOException {
+ if (n < 0) {
+ throw new IllegalArgumentException();
+ }
+ if (n > Integer.MAX_VALUE) {
+ n = Integer.MAX_VALUE;
+ }
+ checkClosed();
+
+ int remaining = (int) n;
+ byte[] tmp = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)];
+ while (remaining > 0) {
+ int count = read(tmp, 0, Math.min(remaining, tmp.length));
+ if (count == -1) {
+ break;
+ }
+ remaining -= count;
+ }
+ return n - remaining;
+ }
+
+ /**
+ * Returns 0 when when this stream has exhausted its input; and 1 otherwise.
+ * A result of 1 does not guarantee that further bytes can be returned,
+ * with or without blocking.
+ *
+ * <p>Although consistent with the RI, this behavior is inconsistent with
+ * {@link InputStream#available()}, and violates the <a
+ * href="http://en.wikipedia.org/wiki/Liskov_substitution_principle">Liskov
+ * Substitution Principle</a>. This method should not be used.
+ *
+ * @return 0 if no further bytes are available. Otherwise returns 1,
+ * which suggests (but does not guarantee) that additional bytes are
+ * available.
+ * @throws IOException if this stream is closed or an error occurs
+ */
+ @Override
+ public int available() throws IOException {
+ checkClosed();
+ return available ? 1 : 0;
+ }
+
+ /**
+ * Returns false because {@code DeflaterInputStream} does not support
+ * {@code mark}/{@code reset}.
+ */
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+
+ /**
+ * This operation is not supported and does nothing.
+ */
+ @Override
+ public void mark(int limit) {
+ }
+
+ /**
+ * This operation is not supported and throws {@code IOException}.
+ */
+ @Override
+ public void reset() throws IOException {
+ throw new IOException();
+ }
+
+ private void checkClosed() throws IOException {
+ if (closed) {
+ throw new IOException("Stream is closed");
+ }
+ }
+}
diff --git a/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java b/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
index dd0da9b..d7f412e 100644
--- a/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
@@ -159,7 +159,7 @@
@Override
public int read(byte[] buffer, int off, int nbytes) throws IOException {
if (closed) {
- throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
+ throw new IOException("Stream is closed");
}
if (eos) {
return -1;
diff --git a/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java b/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
index 8cd8cf2..fcaf8c15 100644
--- a/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
@@ -151,12 +151,8 @@
*/
@Override
public int read(byte[] buffer, int off, int nbytes) throws IOException {
- /* archive.1E=Stream is closed */
- if (closed) {
- throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
- }
-
- if (null == buffer) {
+ checkClosed();
+ if (buffer == null) {
throw new NullPointerException();
}
@@ -216,9 +212,7 @@
* if an {@code IOException} occurs.
*/
protected void fill() throws IOException {
- if (closed) {
- throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
- }
+ checkClosed();
// BEGIN android-only
if (nativeEndBufSize > 0) {
ZipFile.RAFStream is = (ZipFile.RAFStream)in;
@@ -283,10 +277,7 @@
*/
@Override
public int available() throws IOException {
- if (closed) {
- // archive.1E=Stream is closed
- throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
- }
+ checkClosed();
if (eof) {
return 0;
}
@@ -345,4 +336,9 @@
return false;
}
+ private void checkClosed() throws IOException {
+ if (closed) {
+ throw new IOException("Stream is closed");
+ }
+ }
}
diff --git a/libcore/archive/src/main/java/java/util/zip/InflaterOutputStream.java b/libcore/archive/src/main/java/java/util/zip/InflaterOutputStream.java
new file mode 100644
index 0000000..1329393
--- /dev/null
+++ b/libcore/archive/src/main/java/java/util/zip/InflaterOutputStream.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 java.util.zip;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An {@code OutputStream} filter to decompress data. Callers write
+ * compressed data in the "deflate" format, and uncompressed data is
+ * written to the underlying stream.
+ * @since 1.6
+ * @hide
+ */
+public class InflaterOutputStream extends FilterOutputStream {
+ private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+ protected final Inflater inflater;
+ protected final byte[] buf;
+
+ private boolean closed = false;
+
+ /**
+ * Constructs an {@code InflaterOutputStream} with a new {@code Inflater} and an
+ * implementation-defined default internal buffer size. {@code out} is a destination
+ * for uncompressed data, and compressed data will be written to this stream.
+ *
+ * @param out the destination {@code OutputStream}
+ */
+ public InflaterOutputStream(OutputStream out) {
+ this(out, new Inflater());
+ }
+
+ /**
+ * Constructs an {@code InflaterOutputStream} with the given {@code Inflater} and an
+ * implementation-defined default internal buffer size. {@code out} is a destination
+ * for uncompressed data, and compressed data will be written to this stream.
+ *
+ * @param out the destination {@code OutputStream}
+ * @param inflater the {@code Inflater} to be used for decompression
+ */
+ public InflaterOutputStream(OutputStream out, Inflater inflater) {
+ this(out, inflater, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Constructs an {@code InflaterOutputStream} with the given {@code Inflater} and
+ * given internal buffer size. {@code out} is a destination
+ * for uncompressed data, and compressed data will be written to this stream.
+ *
+ * @param out the destination {@code OutputStream}
+ * @param inflater the {@code Inflater} to be used for decompression
+ * @param bufferSize the length in bytes of the internal buffer
+ */
+ public InflaterOutputStream(OutputStream out, Inflater inflater, int bufferSize) {
+ super(out);
+ if (out == null || inflater == null) {
+ throw new NullPointerException();
+ }
+ if (bufferSize <= 0) {
+ throw new IllegalArgumentException();
+ }
+ this.inflater = inflater;
+ this.buf = new byte[bufferSize];
+ }
+
+ /**
+ * Writes remaining data into the output stream and closes the underlying
+ * output stream.
+ */
+ @Override
+ public void close() throws IOException {
+ if (!closed) {
+ finish();
+ inflater.end();
+ out.close();
+ closed = true;
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ finish();
+ out.flush();
+ }
+
+ /**
+ * Finishes writing current uncompressed data into the InflaterOutputStream
+ * without closing it.
+ *
+ * @throws IOException if an I/O error occurs, or the stream has been closed
+ */
+ public void finish() throws IOException {
+ checkClosed();
+ write();
+ }
+
+ /**
+ * Writes a byte to the decompressing output stream. {@code b} should be a byte of
+ * compressed input. The corresponding uncompressed data will be written to the underlying
+ * stream.
+ *
+ * @param b the byte
+ * @throws IOException if an I/O error occurs, or the stream has been closed
+ * @throws ZipException if a zip exception occurs.
+ */
+ @Override
+ public void write(int b) throws IOException, ZipException {
+ write(new byte[] { (byte) b }, 0, 1);
+ }
+
+ /**
+ * Writes bytes to the decompressing output stream. {@code b} should be an array of
+ * compressed input. The corresponding uncompressed data will be written to the underlying
+ * stream.
+ *
+ * @param b the byte array
+ * @param off the start offset in the byte array
+ * @param len the number of the bytes to take from the byte array
+ * @throws IOException if an I/O error occurs, or the stream has been closed
+ * @throws ZipException if a zip exception occurs.
+ * @throws NullPointerException if {@code b == null}.
+ * @throws IndexOutOfBoundsException if {@code off < 0 || len < 0 || off + len > b.length}
+ */
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException, ZipException {
+ checkClosed();
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ if (off < 0 || len < 0 || len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ inflater.setInput(b, off, len);
+ write();
+ }
+
+ private void write() throws IOException, ZipException {
+ try {
+ int inflated;
+ while ((inflated = inflater.inflate(buf)) > 0) {
+ out.write(buf, 0, inflated);
+ }
+ } catch (DataFormatException e) {
+ throw new ZipException();
+ }
+ }
+
+ private void checkClosed() throws IOException {
+ if (closed) {
+ throw new IOException();
+ }
+ }
+}
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipError.java b/libcore/archive/src/main/java/java/util/zip/ZipError.java
new file mode 100644
index 0000000..a244d80
--- /dev/null
+++ b/libcore/archive/src/main/java/java/util/zip/ZipError.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 java.util.zip;
+
+/**
+ * Thrown when an unrecoverable ZIP error has occurred.
+ * @since 1.6
+ * @hide
+ */
+public class ZipError extends InternalError {
+ private static final long serialVersionUID = 853973422266861979L;
+
+ /**
+ * Constructs a ZipError with the given detail message.
+ */
+ public ZipError(String s) {
+ super(s);
+ }
+}
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipFile.java b/libcore/archive/src/main/java/java/util/zip/ZipFile.java
index 6513622..a2ca35a 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipFile.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipFile.java
@@ -181,7 +181,7 @@
private void checkNotClosed() {
if (mRaf == null) {
- throw new IllegalStateException("Zip File closed.");
+ throw new IllegalStateException("Zip file closed");
}
}
@@ -458,6 +458,12 @@
@Override
public int available() throws IOException {
+ if (closed) {
+ // Our superclass will throw an exception, but there's a jtreg test that
+ // explicitly checks that the InputStream returned from ZipFile.getInputStream
+ // returns 0 even when closed.
+ return 0;
+ }
return super.available() == 0 ? 0 : (int) (entry.getSize() - bytesRead);
}
}
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java b/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
index 0d55a10..14141e9 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
@@ -103,15 +103,13 @@
* if an {@code IOException} occurs.
*/
public void closeEntry() throws IOException {
- if (closed) {
- throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
- }
+ checkClosed();
if (currentEntry == null) {
return;
}
if (currentEntry instanceof java.util.jar.JarEntry) {
Attributes temp = ((JarEntry) currentEntry).getAttributes();
- if (temp != null && temp.containsKey("hidden")) { //$NON-NLS-1$
+ if (temp != null && temp.containsKey("hidden")) {
return;
}
}
@@ -301,9 +299,7 @@
*/
@Override
public int read(byte[] buffer, int start, int length) throws IOException {
- if (closed) {
- throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
- }
+ checkClosed();
if (inf.finished() || currentEntry == null) {
return -1;
}
@@ -385,9 +381,7 @@
@Override
public int available() throws IOException {
- if (closed) {
- throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
- }
+ checkClosed();
// The InflaterInputStream contract says we must only return 0 or 1.
return (currentEntry == null || inRead < currentEntry.size) ? 1 : 0;
}
@@ -415,4 +409,10 @@
l |= ((long) (buffer[off + 3] & 0xFF)) << 24;
return l;
}
+
+ private void checkClosed() throws IOException {
+ if (closed) {
+ throw new IOException("Stream is closed");
+ }
+ }
}
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java b/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
index e53061a..3a9bd7e 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
@@ -109,9 +109,7 @@
* If an error occurs closing the entry.
*/
public void closeEntry() throws IOException {
- if (cDir == null) {
- throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
- }
+ checkClosed();
if (currentEntry == null) {
return;
}
@@ -196,8 +194,9 @@
*/
@Override
public void finish() throws IOException {
+ // TODO: is there a bug here? why not checkClosed?
if (out == null) {
- throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
+ throw new IOException("Stream is closed");
}
if (cDir == null) {
return;
@@ -261,10 +260,7 @@
throw new ZipException(Messages.getString("archive.21")); //$NON-NLS-1$
}
}
- /* [MSG "archive.1E", "Stream is closed"] */
- if (cDir == null) {
- throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
- }
+ checkClosed();
if (entries.contains(ze.name)) {
/* [MSG "archive.29", "Entry already exists: {0}"] */
throw new ZipException(Messages.getString("archive.29", ze.name)); //$NON-NLS-1$
@@ -450,4 +446,10 @@
}
return result;
}
+
+ private void checkClosed() throws IOException {
+ if (cDir == null) {
+ throw new IOException("Stream is closed");
+ }
+ }
}
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/AllTests.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/AllTests.java
index 562b396..bf27661 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/AllTests.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/AllTests.java
@@ -25,19 +25,19 @@
*/
public class AllTests {
public static Test suite() {
- TestSuite suite = new TestSuite(
- "Suite org.apache.harmony.archive.tests.java.util.zip");
- // $JUnit-BEGIN$
+ TestSuite suite = new TestSuite("Suite org.apache.harmony.archive.tests.java.util.zip");
suite.addTestSuite(Adler32Test.class);
suite.addTestSuite(CheckedInputStreamTest.class);
suite.addTestSuite(CheckedOutputStreamTest.class);
suite.addTestSuite(CRC32Test.class);
suite.addTestSuite(DataFormatExceptionTest.class);
+ suite.addTestSuite(DeflaterInputStreamTest.class);
suite.addTestSuite(DeflaterOutputStreamTest.class);
suite.addTestSuite(DeflaterTest.class);
suite.addTestSuite(GZIPInputStreamTest.class);
suite.addTestSuite(GZIPOutputStreamTest.class);
suite.addTestSuite(InflaterInputStreamTest.class);
+ suite.addTestSuite(InflaterOutputStreamTest.class);
suite.addTestSuite(InflaterTest.class);
suite.addTestSuite(ZipEntryTest.class);
suite.addTestSuite(ZipExceptionTest.class);
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/DeflaterInputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/DeflaterInputStreamTest.java
new file mode 100644
index 0000000..9ed0f67
--- /dev/null
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/DeflaterInputStreamTest.java
@@ -0,0 +1,378 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.harmony.archive.tests.java.util.zip;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterInputStream;
+
+import junit.framework.TestCase;
+
+public class DeflaterInputStreamTest extends TestCase {
+
+ String testStr = "Hi,this is a test";
+
+ InputStream is;
+
+ /**
+ * @tests DeflaterInputStream#available()
+ */
+ public void testAvailable() throws IOException {
+ byte[] buf = new byte[1024];
+ DeflaterInputStream dis = new DeflaterInputStream(is);
+ assertEquals(1, dis.available());
+ assertEquals(120, dis.read());
+ assertEquals(1, dis.available());
+ assertEquals(22, dis.read(buf, 0, 1024));
+ assertEquals(1, dis.available());
+ assertEquals(-1, dis.read());
+ assertEquals(0, dis.available());
+ dis.close();
+ try {
+ dis.available();
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ }
+
+ /**
+ * @tests DeflaterInputStream#close()
+ */
+ public void testClose() throws IOException {
+ byte[] buf = new byte[1024];
+ DeflaterInputStream dis = new DeflaterInputStream(is);
+ assertEquals(1, dis.available());
+ dis.close();
+ try {
+ dis.available();
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ dis.read(buf, 0, 1024);
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ // can close after close
+ dis.close();
+ }
+
+ /**
+ * @tests DeflaterInputStream#mark()
+ */
+ public void testMark() throws IOException {
+ // mark do nothing
+ DeflaterInputStream dis = new DeflaterInputStream(is);
+ dis.mark(-1);
+ dis.mark(0);
+ dis.mark(1);
+ dis.close();
+ dis.mark(1);
+ }
+
+ /**
+ * @tests DeflaterInputStream#markSupported()
+ */
+ public void testMarkSupported() throws IOException {
+ DeflaterInputStream dis = new DeflaterInputStream(is);
+ assertFalse(dis.markSupported());
+ dis.close();
+ assertFalse(dis.markSupported());
+ }
+
+ /**
+ * @tests DeflaterInputStream#read()
+ */
+ public void testRead() throws IOException {
+ DeflaterInputStream dis = new DeflaterInputStream(is);
+ assertEquals(1, dis.available());
+ assertEquals(120, dis.read());
+ assertEquals(1, dis.available());
+ assertEquals(156, dis.read());
+ assertEquals(1, dis.available());
+ assertEquals(243, dis.read());
+ assertEquals(1, dis.available());
+ dis.close();
+ try {
+ dis.read();
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ }
+
+ /**
+ * @tests DeflaterInputStream#read(byte[],int,int)
+ */
+ public void testReadByteArrayIntInt() throws IOException {
+ byte[] buf1 = new byte[256];
+ byte[] buf2 = new byte[256];
+ DeflaterInputStream dis = new DeflaterInputStream(is);
+ assertEquals(23, dis.read(buf1, 0, 256));
+ dis = new DeflaterInputStream(is);
+ assertEquals(8, dis.read(buf2, 0, 256));
+ is = new ByteArrayInputStream(testStr.getBytes("UTF-8"));
+ dis = new DeflaterInputStream(is);
+ assertEquals(1, dis.available());
+ assertEquals(120, dis.read());
+ assertEquals(1, dis.available());
+ assertEquals(22, dis.read(buf2, 0, 256));
+ assertEquals(1, dis.available());
+ assertEquals(-1, dis.read());
+ assertEquals(0, dis.available());
+ try {
+ dis.read(buf1, 0, 512);
+ fail("should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ try {
+ dis.read(null, 0, 0);
+ fail("should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ dis.read(null, -1, 0);
+ fail("should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ dis.read(null, -1, -1);
+ fail("should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ dis.read(buf1, -1, -1);
+ fail("should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ try {
+ dis.read(buf1, 0, -1);
+ fail("should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ dis.close();
+ try {
+ dis.read(buf1, 0, 512);
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ dis.read(buf1, 0, 1);
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ dis.read(null, 0, 0);
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ dis.read(null, -1, 0);
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ dis.read(null, -1, -1);
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ dis.read(buf1, -1, -1);
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ dis.read(buf1, 0, -1);
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ }
+
+ /**
+ * @tests DeflaterInputStream#reset()
+ */
+ public void testReset() throws IOException {
+ DeflaterInputStream dis = new DeflaterInputStream(is);
+ try {
+ dis.reset();
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ dis.close();
+ try {
+ dis.reset();
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ }
+
+ /**
+ * @tests DeflaterInputStream#skip()
+ */
+ public void testSkip() throws IOException {
+ byte[] buf = new byte[1024];
+ DeflaterInputStream dis = new DeflaterInputStream(is);
+ assertEquals(1, dis.available());
+ dis.skip(1);
+ assertEquals(1, dis.available());
+ assertEquals(22, dis.read(buf, 0, 1024));
+ assertEquals(1, dis.available());
+ dis.skip(1);
+ assertEquals(0, dis.available());
+ is = new ByteArrayInputStream(testStr.getBytes("UTF-8"));
+ dis = new DeflaterInputStream(is);
+ assertEquals(1, dis.available());
+ dis.skip(56);
+ assertEquals(0, dis.available());
+ assertEquals(-1, dis.read(buf, 0, 1024));
+ try {
+ dis.skip(-1);
+ fail("should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ assertEquals(0, dis.available());
+ // can still skip
+ dis.skip(1);
+ dis.close();
+ try {
+ dis.skip(1);
+ fail("should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ dis.skip(-1);
+ fail("should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ is = new ByteArrayInputStream(testStr.getBytes("UTF-8"));
+ dis = new DeflaterInputStream(is);
+ assertEquals(23, dis.skip(Long.MAX_VALUE));
+ assertEquals(0, dis.available());
+ }
+
+ /**
+ * @tests DeflaterInputStream#DeflaterInputStream(InputStream)
+ */
+ public void testDeflaterInputStreamInputStream() {
+ // ok
+ new DeflaterInputStream(is);
+ // fail
+ try {
+ new DeflaterInputStream(null);
+ fail("should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ /**
+ * @tests DeflaterInputStream#DeflaterInputStream(InputStream,Deflater)
+ */
+ public void testDeflaterInputStreamInputStreamDeflater() {
+ // ok
+ new DeflaterInputStream(is, new Deflater());
+ // fail
+ try {
+ new DeflaterInputStream(is, null);
+ fail("should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ new DeflaterInputStream(null, new Deflater());
+ fail("should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ /**
+ * @tests DeflaterInputStream#DeflaterInputStream(InputStream,Deflater,int)
+ */
+ public void testDeflaterInputStreamInputStreamDeflaterInt() {
+ // ok
+ new DeflaterInputStream(is, new Deflater(), 1024);
+ // fail
+ try {
+ new DeflaterInputStream(is, null, 1024);
+ fail("should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ new DeflaterInputStream(null, new Deflater(), 1024);
+ fail("should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ new DeflaterInputStream(is, new Deflater(), -1);
+ fail("should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ try {
+ new DeflaterInputStream(null, new Deflater(), -1);
+ fail("should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ new DeflaterInputStream(is, null, -1);
+ fail("should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ is = new ByteArrayInputStream(testStr.getBytes("UTF-8"));
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ is.close();
+ super.tearDown();
+ }
+}
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterOutputStreamTest.java
new file mode 100644
index 0000000..9627613
--- /dev/null
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterOutputStreamTest.java
@@ -0,0 +1,392 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.harmony.archive.tests.java.util.zip;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterOutputStream;
+import java.util.zip.ZipException;
+
+import junit.framework.TestCase;
+
+public class InflaterOutputStreamTest extends TestCase {
+
+ private ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ private byte[] compressedBytes = new byte[100];
+
+ private String testString = "Hello world";
+
+ /**
+ * @tests java.util.zip.InflaterOutputStream#InflaterOutputStream(java.io.OutputStream)
+ */
+ public void test_ConstructorLjava_io_OutputStream() throws IOException {
+ new InflaterOutputStream(os);
+
+ try {
+ new InflaterOutputStream(null);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ /**
+ * @tests java.util.zip.InflaterOutputStream#InflaterOutputStream(java.io.OutputStream,Inflater)
+ */
+ public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_Inflater() {
+ new InflaterOutputStream(os, new Inflater());
+
+ try {
+ new InflaterOutputStream(null, new Inflater());
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ try {
+ new InflaterOutputStream(os, null);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ /**
+ * @tests java.util.zip.InflaterOutputStream#InflaterOutputStream(java.io.OutputStream,Inflater,int)
+ */
+ public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_InflaterI() {
+ new InflaterOutputStream(os, new Inflater(), 20);
+
+ try {
+ new InflaterOutputStream(null, null, 10);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ try {
+ new InflaterOutputStream(null, new Inflater(), -1);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ try {
+ new InflaterOutputStream(os, null, -1);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ try {
+ new InflaterOutputStream(null, null, -1);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ try {
+ new InflaterOutputStream(os, new Inflater(), 0);
+ fail("Should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ new InflaterOutputStream(os, new Inflater(), -10000);
+ fail("Should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ /**
+ * @tests java.util.zip.InflaterOutputStream#close()
+ */
+ public void test_close() throws IOException {
+ InflaterOutputStream ios = new InflaterOutputStream(os);
+ ios.close();
+ // multiple close
+ ios.close();
+ }
+
+ /**
+ * @tests java.util.zip.InflaterOutputStream#flush()
+ */
+ public void test_flush() throws IOException {
+ InflaterOutputStream ios = new InflaterOutputStream(os);
+ ios.close();
+ try {
+ ios.flush();
+ fail("Should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+
+ ios = new InflaterOutputStream(os);
+ ios.flush();
+ ios.flush();
+ }
+
+ /**
+ * @tests java.util.zip.InflaterOutputStream#finish()
+ */
+ public void test_finish() throws IOException {
+ InflaterOutputStream ios = new InflaterOutputStream(os);
+ ios.close();
+ try {
+ ios.finish();
+ fail("Should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+
+ ios = new InflaterOutputStream(os);
+ ios.finish();
+ ios.finish();
+ ios.flush();
+ ios.flush();
+ ios.finish();
+
+ byte[] bytes1 = {10,20,30,40,50};
+ Deflater defaultDeflater = new Deflater(Deflater.BEST_SPEED);
+ defaultDeflater.setInput(bytes1);
+ defaultDeflater.finish();
+ int length1 = defaultDeflater.deflate(compressedBytes);
+
+ byte[] bytes2 = {100,90,80,70,60};
+ Deflater bestDeflater = new Deflater(Deflater.BEST_COMPRESSION );
+ bestDeflater.setInput(bytes2);
+ bestDeflater.finish();
+ int length2 = bestDeflater.deflate(compressedBytes,length1,compressedBytes.length-length1);
+
+ ios = new InflaterOutputStream(os);
+ for (int i = 0; i < length1; i++) {
+ ios.write(compressedBytes[i]);
+ }
+ ios.finish();
+ ios.close();
+
+ byte[] result = os.toByteArray();
+ for(int i =0;i<bytes1.length; i++){
+ assertEquals(bytes1[i],result[i]);
+ }
+
+ ios = new InflaterOutputStream(os);
+ for (int i = length1; i < length2*2; i++) {
+ ios.write(compressedBytes[i]);
+ }
+ ios.finish();
+ ios.close();
+
+ result = os.toByteArray();
+ for(int i =0;i<bytes2.length; i++){
+ assertEquals(bytes2[i],result[bytes1.length+i]);
+ }
+
+ }
+
+ /**
+ * @tests java.util.zip.InflaterOutputStream#write(int)
+ */
+ public void test_write_I() throws IOException {
+ int length = compressToBytes(testString);
+
+ // uncompress the data stored in the compressedBytes
+ InflaterOutputStream ios = new InflaterOutputStream(os);
+ for (int i = 0; i < length; i++) {
+ ios.write(compressedBytes[i]);
+ }
+
+ String result = new String(os.toByteArray());
+ assertEquals(testString, result);
+ }
+
+ /**
+ * @tests java.util.zip.InflaterOutputStream#write(int)
+ */
+ public void test_write_I_Illegal() throws IOException {
+
+ // write after close
+ InflaterOutputStream ios = new InflaterOutputStream(os);
+ ios.close();
+ try {
+ ios.write(-1);
+ fail("Should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ }
+
+ /**
+ * @tests java.util.zip.InflaterOutputStream#write(byte[],int,int)
+ */
+ public void test_write_$BII() throws IOException {
+ int length = compressToBytes(testString);
+
+ // uncompress the data stored in the compressedBytes
+ InflaterOutputStream ios = new InflaterOutputStream(os);
+ ios.write(compressedBytes, 0, length);
+
+ String result = new String(os.toByteArray());
+ assertEquals(testString, result);
+ }
+
+ /**
+ * @tests java.util.zip.InflaterOutputStream#write(byte[],int,int)
+ */
+ public void test_write_$BII_Illegal() throws IOException {
+ // write error compression (ZIP) format
+ InflaterOutputStream ios = new InflaterOutputStream(os);
+ byte[] bytes = { 0, 1, 2, 3 };
+ try {
+ ios.write(bytes, 0, 4);
+ fail("Should throw ZipException");
+ } catch (ZipException e) {
+ // expected
+ }
+ try {
+ ios.flush();
+ fail("Should throw ZipException");
+ } catch (ZipException e) {
+ // expected
+ }
+
+ // write after close
+ ios = new InflaterOutputStream(os);
+ ios.close();
+ try {
+ ios.write(bytes, 0, 4);
+ fail("Should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ ios.write(bytes, -1, 4);
+ fail("Should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ ios.write(bytes, -1, -4);
+ fail("Should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ ios.write(bytes, 0, 400);
+ fail("Should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ ios.write(null, -1, 4);
+ fail("Should throw IOException");
+ } catch (IOException e) {
+ // expected
+ }
+
+ ios = new InflaterOutputStream(os);
+ try {
+ ios.write(null, 0, 4);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ ios.write(null, -1, 4);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ ios.write(null, 0, -4);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ ios.write(null, 0, 1000);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ ios.write(bytes, -1, 4);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ try {
+ ios.write(bytes, 0, -4);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ try {
+ ios.write(bytes, 0, 100);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ try {
+ ios.write(bytes, -100, 100);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ ios = new InflaterOutputStream(os);
+ ios.finish();
+
+ try {
+ ios.write(bytes, -1,-100);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ try {
+ ios.write(null, -1,-100);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ ios = new InflaterOutputStream(os);
+ ios.flush();
+ try {
+ ios.write(bytes, 0, 4);
+ fail("Should throw ZipException");
+ } catch (ZipException e) {
+ // expected
+ }
+ }
+
+ // Compress the test string into compressedBytes
+ private int compressToBytes(String string) {
+ byte[] input = string.getBytes();
+ Deflater deflater = new Deflater();
+ deflater.setInput(input);
+ deflater.finish();
+ return deflater.deflate(compressedBytes);
+ }
+
+}