1cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson/*
2cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson * Copyright (C) 2010 The Android Open Source Project
3cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson *
4cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
5cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson * you may not use this file except in compliance with the License.
6cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson * You may obtain a copy of the License at
7cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson *
8cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
9cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson *
10cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson * Unless required by applicable law or agreed to in writing, software
11cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
12cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson * See the License for the specific language governing permissions and
14cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson * limitations under the License.
15cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson */
16cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson
174557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonpackage libcore.java.util.zip;
18cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson
19a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstromimport java.io.ByteArrayInputStream;
20a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstromimport java.io.ByteArrayOutputStream;
21a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstromimport java.io.EOFException;
22cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilsonimport java.io.IOException;
2327604018f783bf6354a13870b3e7785edca69b5fPaul Duffinimport java.io.InputStream;
24cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilsonimport java.io.OutputStream;
25cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilsonimport java.io.PipedInputStream;
26cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilsonimport java.io.PipedOutputStream;
278601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstromimport java.util.Arrays;
28cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilsonimport java.util.concurrent.Callable;
29cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilsonimport java.util.concurrent.ExecutorService;
30cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilsonimport java.util.concurrent.Executors;
31a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstromimport java.util.zip.Deflater;
324557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonimport java.util.zip.DeflaterOutputStream;
3335f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughesimport java.util.zip.GZIPInputStream;
3435f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughesimport java.util.zip.GZIPOutputStream;
354557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonimport java.util.zip.InflaterInputStream;
3627604018f783bf6354a13870b3e7785edca69b5fPaul Duffinimport libcore.junit.junit3.TestCaseWithRules;
3727604018f783bf6354a13870b3e7785edca69b5fPaul Duffinimport libcore.junit.util.ResourceLeakageDetector;
3827604018f783bf6354a13870b3e7785edca69b5fPaul Duffinimport org.junit.Rule;
3927604018f783bf6354a13870b3e7785edca69b5fPaul Duffinimport org.junit.rules.TestRule;
40cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson
4127604018f783bf6354a13870b3e7785edca69b5fPaul Duffinpublic class DeflaterOutputStreamTest extends TestCaseWithRules {
4227604018f783bf6354a13870b3e7785edca69b5fPaul Duffin    @Rule
4327604018f783bf6354a13870b3e7785edca69b5fPaul Duffin    public TestRule guardRule = ResourceLeakageDetector.getRule();
44cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson
45cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson    public void testSyncFlushEnabled() throws Exception {
4635f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes        InputStream in = createInflaterStream(DeflaterOutputStream.class, true);
47cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        assertEquals(1, in.read());
48cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        assertEquals(2, in.read());
49cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        assertEquals(3, in.read());
50a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        in.close();
51cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson    }
52cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson
53cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson    public void testSyncFlushDisabled() throws Exception {
5435f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes        InputStream in = createInflaterStream(DeflaterOutputStream.class, false);
55cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        try {
56cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson            in.read();
57cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson            fail();
58cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        } catch (IOException expected) {
59cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        }
60a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        in.close();
61cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson    }
62cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson
63cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson    /**
64cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson     * Creates an optionally-flushing deflater stream, writes some bytes to it,
65cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson     * and flushes it. Returns an inflater stream that reads this deflater's
66cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson     * output.
67cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson     *
68cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson     * <p>These bytes are written on a separate thread so that when the inflater
69cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson     * stream is read, that read will fail when no bytes are available. Failing
70cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson     * takes 3 seconds, co-ordinated by PipedInputStream's 'broken pipe'
71cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson     * timeout. The 3 second delay is unfortunate but seems to be the easiest
72cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson     * way demonstrate that data is unavailable. Ie. other techniques will cause
73cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson     * the dry read to block indefinitely.
74cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson     */
7535f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes    static InputStream createInflaterStream(final Class<?> c, final boolean flushing) throws Exception {
76cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        ExecutorService executor = Executors.newSingleThreadExecutor();
77cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        final PipedOutputStream pout = new PipedOutputStream();
78cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        PipedInputStream pin = new PipedInputStream(pout);
79cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson
80cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        executor.submit(new Callable<Void>() {
81cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson            public Void call() throws Exception {
8235f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes                OutputStream out;
8335f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes                if (c == DeflaterOutputStream.class) {
8435f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes                    out = new DeflaterOutputStream(pout, flushing);
8535f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes                } else if (c == GZIPOutputStream.class) {
8635f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes                    out = new GZIPOutputStream(pout, flushing);
8735f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes                } else {
8835f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes                    throw new AssertionError();
8935f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes                }
90cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson                out.write(1);
91cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson                out.write(2);
92cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson                out.write(3);
93cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson                out.flush();
94cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson                return null;
95cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson            }
96cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        }).get();
97cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson        executor.shutdown();
98cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson
9935f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes        if (c == DeflaterOutputStream.class) {
10035f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes            return new InflaterInputStream(pin);
10135f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes        } else if (c == GZIPOutputStream.class) {
10235f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes            return new GZIPInputStream(pin);
10335f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes        } else {
10435f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes            throw new AssertionError();
10535f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes        }
106cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson    }
107a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom
1083e2415c8a33ad147352176a0e98e43a8bbd5fb11Narayan Kamath    static class FlushingDeflater extends Deflater {
1093e2415c8a33ad147352176a0e98e43a8bbd5fb11Narayan Kamath        @Override
1103e2415c8a33ad147352176a0e98e43a8bbd5fb11Narayan Kamath        public int deflate(byte[] buf, int offset, int byteCount) {
1113e2415c8a33ad147352176a0e98e43a8bbd5fb11Narayan Kamath            return super.deflate(buf, offset, byteCount, Deflater.SYNC_FLUSH);
1123e2415c8a33ad147352176a0e98e43a8bbd5fb11Narayan Kamath        }
1133e2415c8a33ad147352176a0e98e43a8bbd5fb11Narayan Kamath    }
1143e2415c8a33ad147352176a0e98e43a8bbd5fb11Narayan Kamath
115a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom    /**
116a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom     * Confirm that a DeflaterOutputStream constructed with Deflater
117a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom     * with flushParm == SYNC_FLUSH does not need to to be flushed.
118a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom     *
1198601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom     * http://b/4005091
120a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom     */
121a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom    public void testSyncFlushDeflater() throws Exception {
1223e2415c8a33ad147352176a0e98e43a8bbd5fb11Narayan Kamath        Deflater def = new FlushingDeflater();
123a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        final int deflaterBufferSize = 512;
124a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        ByteArrayOutputStream baos = new ByteArrayOutputStream();
125a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        DeflaterOutputStream dos = new DeflaterOutputStream(baos, def, deflaterBufferSize);
126a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom
127a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        // make output buffer large enough that even if compressed it
128a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        // won't all fit within the deflaterBufferSize.
129a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        final int outputBufferSize = 128 * deflaterBufferSize;
130a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        byte[] output = new byte[outputBufferSize];
131a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        for (int i = 0; i < output.length; i++) {
132a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom            output[i] = (byte) i;
133a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        }
134a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        dos.write(output);
135a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        byte[] compressed = baos.toByteArray();
1368601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom        // this main reason for this assert is to make sure that the
1378601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom        // compressed byte count is larger than the
1388601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom        // deflaterBufferSize. However, when the original bug exists,
1398601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom        // it will also fail because the compressed length will be
1408601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom        // exactly the length of the deflaterBufferSize.
141a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        assertTrue("compressed=" + compressed.length
142a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom                   + " but deflaterBufferSize=" + deflaterBufferSize,
143a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom                   compressed.length > deflaterBufferSize);
144a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom
1458601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom        // assert that we returned data matches the input exactly.
146a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        ByteArrayInputStream bais = new ByteArrayInputStream(compressed);
147a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        InflaterInputStream iis = new InflaterInputStream(bais);
148a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        byte[] input = new byte[output.length];
149a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        int total = 0;
150a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        while (true)  {
151a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom            int n = iis.read(input, total, input.length - total);
152a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom            if (n == -1) {
153a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom                break;
154a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom            }
155a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom            total += n;
156a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom            if (total == input.length) {
157a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom                try {
158a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom                    iis.read();
159a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom                    fail();
160a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom                } catch (EOFException expected) {
161a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom                    break;
162a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom                }
163a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom            }
164a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        }
165a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom        assertEquals(output.length, total);
1668601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom        assertTrue(Arrays.equals(input, output));
1678601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom
1688601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom        // ensure Deflater.finish has not been called at any point
1698601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom        // during the test, since that would lead to the results being
1708601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom        // flushed even without SYNC_FLUSH being used
1718601d6b5872167f20f3ab845160ae7f9e0fad94bBrian Carlstrom        assertFalse(def.finished());
17235f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes
17335f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes        // Quieten CloseGuard.
17435f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes        def.end();
17535f9da25ee063a0d0c32f7246b466cff8e57c9eeElliott Hughes        iis.close();
176a321720e459ae0d7fc403d112f2e974102a59d9fBrian Carlstrom    }
177cf900b4862df0e261b3cce2195ff0654124ad7dbJesse Wilson}
178