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.ByteArrayInputStream; 20import java.io.ByteArrayOutputStream; 21import java.io.EOFException; 22import java.io.InputStream; 23import java.io.IOException; 24import java.io.OutputStream; 25import java.io.PipedInputStream; 26import java.io.PipedOutputStream; 27import java.lang.reflect.Field; 28import java.util.Arrays; 29import java.util.concurrent.Callable; 30import java.util.concurrent.ExecutorService; 31import java.util.concurrent.Executors; 32import java.util.zip.Deflater; 33import java.util.zip.DeflaterOutputStream; 34import java.util.zip.GZIPInputStream; 35import java.util.zip.GZIPOutputStream; 36import java.util.zip.InflaterInputStream; 37import junit.framework.TestCase; 38 39public class DeflaterOutputStreamTest extends TestCase { 40 41 public void testSyncFlushEnabled() throws Exception { 42 InputStream in = createInflaterStream(DeflaterOutputStream.class, true); 43 assertEquals(1, in.read()); 44 assertEquals(2, in.read()); 45 assertEquals(3, in.read()); 46 in.close(); 47 } 48 49 public void testSyncFlushDisabled() throws Exception { 50 InputStream in = createInflaterStream(DeflaterOutputStream.class, false); 51 try { 52 in.read(); 53 fail(); 54 } catch (IOException expected) { 55 } 56 in.close(); 57 } 58 59 /** 60 * Creates an optionally-flushing deflater stream, writes some bytes to it, 61 * and flushes it. Returns an inflater stream that reads this deflater's 62 * output. 63 * 64 * <p>These bytes are written on a separate thread so that when the inflater 65 * stream is read, that read will fail when no bytes are available. Failing 66 * takes 3 seconds, co-ordinated by PipedInputStream's 'broken pipe' 67 * timeout. The 3 second delay is unfortunate but seems to be the easiest 68 * way demonstrate that data is unavailable. Ie. other techniques will cause 69 * the dry read to block indefinitely. 70 */ 71 static InputStream createInflaterStream(final Class<?> c, final boolean flushing) throws Exception { 72 ExecutorService executor = Executors.newSingleThreadExecutor(); 73 final PipedOutputStream pout = new PipedOutputStream(); 74 PipedInputStream pin = new PipedInputStream(pout); 75 76 executor.submit(new Callable<Void>() { 77 public Void call() throws Exception { 78 OutputStream out; 79 if (c == DeflaterOutputStream.class) { 80 out = new DeflaterOutputStream(pout, flushing); 81 } else if (c == GZIPOutputStream.class) { 82 out = new GZIPOutputStream(pout, flushing); 83 } else { 84 throw new AssertionError(); 85 } 86 out.write(1); 87 out.write(2); 88 out.write(3); 89 out.flush(); 90 return null; 91 } 92 }).get(); 93 executor.shutdown(); 94 95 if (c == DeflaterOutputStream.class) { 96 return new InflaterInputStream(pin); 97 } else if (c == GZIPOutputStream.class) { 98 return new GZIPInputStream(pin); 99 } else { 100 throw new AssertionError(); 101 } 102 } 103 104 static class FlushingDeflater extends Deflater { 105 @Override 106 public int deflate(byte[] buf, int offset, int byteCount) { 107 return super.deflate(buf, offset, byteCount, Deflater.SYNC_FLUSH); 108 } 109 } 110 111 /** 112 * Confirm that a DeflaterOutputStream constructed with Deflater 113 * with flushParm == SYNC_FLUSH does not need to to be flushed. 114 * 115 * http://b/4005091 116 */ 117 public void testSyncFlushDeflater() throws Exception { 118 Deflater def = new FlushingDeflater(); 119 final int deflaterBufferSize = 512; 120 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 121 DeflaterOutputStream dos = new DeflaterOutputStream(baos, def, deflaterBufferSize); 122 123 // make output buffer large enough that even if compressed it 124 // won't all fit within the deflaterBufferSize. 125 final int outputBufferSize = 128 * deflaterBufferSize; 126 byte[] output = new byte[outputBufferSize]; 127 for (int i = 0; i < output.length; i++) { 128 output[i] = (byte) i; 129 } 130 dos.write(output); 131 byte[] compressed = baos.toByteArray(); 132 // this main reason for this assert is to make sure that the 133 // compressed byte count is larger than the 134 // deflaterBufferSize. However, when the original bug exists, 135 // it will also fail because the compressed length will be 136 // exactly the length of the deflaterBufferSize. 137 assertTrue("compressed=" + compressed.length 138 + " but deflaterBufferSize=" + deflaterBufferSize, 139 compressed.length > deflaterBufferSize); 140 141 // assert that we returned data matches the input exactly. 142 ByteArrayInputStream bais = new ByteArrayInputStream(compressed); 143 InflaterInputStream iis = new InflaterInputStream(bais); 144 byte[] input = new byte[output.length]; 145 int total = 0; 146 while (true) { 147 int n = iis.read(input, total, input.length - total); 148 if (n == -1) { 149 break; 150 } 151 total += n; 152 if (total == input.length) { 153 try { 154 iis.read(); 155 fail(); 156 } catch (EOFException expected) { 157 break; 158 } 159 } 160 } 161 assertEquals(output.length, total); 162 assertTrue(Arrays.equals(input, output)); 163 164 // ensure Deflater.finish has not been called at any point 165 // during the test, since that would lead to the results being 166 // flushed even without SYNC_FLUSH being used 167 assertFalse(def.finished()); 168 169 // Quieten CloseGuard. 170 def.end(); 171 iis.close(); 172 } 173} 174