/* * 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.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.lang.reflect.Field; import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.util.zip.InflaterInputStream; import junit.framework.TestCase; public class DeflaterOutputStreamTest extends TestCase { public void testSyncFlushEnabled() throws Exception { InputStream in = createInflaterStream(DeflaterOutputStream.class, true); assertEquals(1, in.read()); assertEquals(2, in.read()); assertEquals(3, in.read()); in.close(); } public void testSyncFlushDisabled() throws Exception { InputStream in = createInflaterStream(DeflaterOutputStream.class, false); try { in.read(); fail(); } catch (IOException expected) { } in.close(); } /** * Creates an optionally-flushing deflater stream, writes some bytes to it, * and flushes it. Returns an inflater stream that reads this deflater's * output. * * <p>These bytes are written on a separate thread so that when the inflater * stream is read, that read will fail when no bytes are available. Failing * takes 3 seconds, co-ordinated by PipedInputStream's 'broken pipe' * timeout. The 3 second delay is unfortunate but seems to be the easiest * way demonstrate that data is unavailable. Ie. other techniques will cause * the dry read to block indefinitely. */ static InputStream createInflaterStream(final Class<?> c, final boolean flushing) throws Exception { ExecutorService executor = Executors.newSingleThreadExecutor(); final PipedOutputStream pout = new PipedOutputStream(); PipedInputStream pin = new PipedInputStream(pout); executor.submit(new Callable<Void>() { public Void call() throws Exception { OutputStream out; if (c == DeflaterOutputStream.class) { out = new DeflaterOutputStream(pout, flushing); } else if (c == GZIPOutputStream.class) { out = new GZIPOutputStream(pout, flushing); } else { throw new AssertionError(); } out.write(1); out.write(2); out.write(3); out.flush(); return null; } }).get(); executor.shutdown(); if (c == DeflaterOutputStream.class) { return new InflaterInputStream(pin); } else if (c == GZIPOutputStream.class) { return new GZIPInputStream(pin); } else { throw new AssertionError(); } } /** * Confirm that a DeflaterOutputStream constructed with Deflater * with flushParm == SYNC_FLUSH does not need to to be flushed. * * http://b/4005091 */ public void testSyncFlushDeflater() throws Exception { Deflater def = new Deflater(); Field f = def.getClass().getDeclaredField("flushParm"); f.setAccessible(true); f.setInt(def, Deflater.SYNC_FLUSH); final int deflaterBufferSize = 512; ByteArrayOutputStream baos = new ByteArrayOutputStream(); DeflaterOutputStream dos = new DeflaterOutputStream(baos, def, deflaterBufferSize); // make output buffer large enough that even if compressed it // won't all fit within the deflaterBufferSize. final int outputBufferSize = 128 * deflaterBufferSize; byte[] output = new byte[outputBufferSize]; for (int i = 0; i < output.length; i++) { output[i] = (byte) i; } dos.write(output); byte[] compressed = baos.toByteArray(); // this main reason for this assert is to make sure that the // compressed byte count is larger than the // deflaterBufferSize. However, when the original bug exists, // it will also fail because the compressed length will be // exactly the length of the deflaterBufferSize. assertTrue("compressed=" + compressed.length + " but deflaterBufferSize=" + deflaterBufferSize, compressed.length > deflaterBufferSize); // assert that we returned data matches the input exactly. ByteArrayInputStream bais = new ByteArrayInputStream(compressed); InflaterInputStream iis = new InflaterInputStream(bais); byte[] input = new byte[output.length]; int total = 0; while (true) { int n = iis.read(input, total, input.length - total); if (n == -1) { break; } total += n; if (total == input.length) { try { iis.read(); fail(); } catch (EOFException expected) { break; } } } assertEquals(output.length, total); assertTrue(Arrays.equals(input, output)); // ensure Deflater.finish has not been called at any point // during the test, since that would lead to the results being // flushed even without SYNC_FLUSH being used assertFalse(def.finished()); // Quieten CloseGuard. def.end(); iis.close(); } }