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.ByteArrayOutputStream;
20import java.util.zip.Adler32;
21import java.util.zip.Deflater;
22import java.util.zip.Inflater;
23import libcore.junit.junit3.TestCaseWithRules;
24import libcore.junit.util.ResourceLeakageDetector;
25import org.junit.Rule;
26import org.junit.rules.TestRule;
27
28public class InflaterTest extends TestCaseWithRules {
29    @Rule
30    public TestRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule();
31
32    public void testDefaultDictionary() throws Exception {
33        assertRoundTrip(null);
34    }
35
36    public void testPresetCustomDictionary() throws Exception {
37        assertRoundTrip("dictionary".getBytes("UTF-8"));
38    }
39
40    private static void assertRoundTrip(byte[] dictionary) throws Exception {
41        // Construct a nice long input byte sequence.
42        String expected = makeString();
43        byte[] expectedBytes = expected.getBytes("UTF-8");
44
45        // Compress the bytes, using the passed-in dictionary (or no dictionary).
46        byte[] deflatedBytes = deflate(expectedBytes, dictionary);
47
48        // Get ready to decompress deflatedBytes back to the original bytes ...
49        Inflater inflater = new Inflater();
50        // We'll only supply the input a little bit at a time, so that zlib has to ask for more.
51        final int CHUNK_SIZE = 16;
52        int offset = 0;
53        inflater.setInput(deflatedBytes, offset, CHUNK_SIZE);
54        offset += CHUNK_SIZE;
55        // If we used a dictionary to compress, check that we're asked for that same dictionary.
56        if (dictionary != null) {
57            // 1. there's no data available immediately...
58            assertEquals(0, inflater.inflate(new byte[8]));
59            // 2. ...because you need a dictionary.
60            assertTrue(inflater.needsDictionary());
61            // 3. ...and that dictionary has the same Adler32 as the dictionary we used.
62            assertEquals(adler32(dictionary), inflater.getAdler());
63            inflater.setDictionary(dictionary);
64        }
65        // Do the actual decompression, now the dictionary's set up appropriately...
66        // We use a tiny output buffer to ensure that we call inflate multiple times, and
67        // a tiny input buffer to ensure that zlib has to ask for more input.
68        ByteArrayOutputStream inflatedBytes = new ByteArrayOutputStream();
69        byte[] buf = new byte[8];
70        while (inflatedBytes.size() != expectedBytes.length) {
71            if (inflater.needsInput()) {
72                int nextChunkByteCount = Math.min(CHUNK_SIZE, deflatedBytes.length - offset);
73                inflater.setInput(deflatedBytes, offset, nextChunkByteCount);
74                offset += nextChunkByteCount;
75            } else {
76                int inflatedByteCount = inflater.inflate(buf);
77                if (inflatedByteCount > 0) {
78                    inflatedBytes.write(buf, 0, inflatedByteCount);
79                }
80            }
81        }
82        inflater.end();
83
84        assertEquals(expected, new String(inflatedBytes.toByteArray(), "UTF-8"));
85    }
86
87    private static String makeString() {
88        StringBuilder sb = new StringBuilder();
89        for (int i = 0; i < 1024; ++i) {
90            // This is arbitrary but convenient in that it gives our string
91            // an easily-recognizable beginning and end.
92            sb.append(i + 1024);
93        }
94        return sb.toString();
95    }
96
97    /**
98     * http://code.google.com/p/android/issues/detail?id=11755
99     */
100    public void testEmptyFileAndEmptyBuffer() throws Exception {
101        byte[] emptyInput = deflate(new byte[0], null);
102        Inflater inflater = new Inflater();
103        inflater.setInput(emptyInput);
104        assertFalse(inflater.finished());
105        assertEquals(0, inflater.inflate(new byte[0], 0, 0));
106        assertTrue(inflater.finished());
107        inflater.end();
108    }
109
110    private static byte[] deflate(byte[] input, byte[] dictionary) {
111        Deflater deflater = new Deflater();
112        ByteArrayOutputStream deflatedBytes = new ByteArrayOutputStream();
113        try {
114            if (dictionary != null) {
115                deflater.setDictionary(dictionary);
116            }
117            deflater.setInput(input);
118            deflater.finish();
119            byte[] buf = new byte[8];
120            while (!deflater.finished()) {
121                int byteCount = deflater.deflate(buf);
122                deflatedBytes.write(buf, 0, byteCount);
123            }
124        } finally {
125            deflater.end();
126        }
127        return deflatedBytes.toByteArray();
128    }
129
130    private static int adler32(byte[] bytes) {
131        Adler32 adler32 = new Adler32();
132        adler32.update(bytes);
133        return (int) adler32.getValue();
134    }
135
136    public void testInflaterCounts() throws Exception {
137        Inflater inflater = new Inflater();
138        byte[] decompressed = new byte[32];
139        byte[] compressed = deflate(new byte[] { 1, 2, 3 }, null);
140        assertEquals(11, compressed.length);
141
142        // Feed in bytes [0, 5) to the first iteration.
143        inflater.setInput(compressed, 0, 5);
144        inflater.inflate(decompressed, 0, decompressed.length);
145        assertEquals(5, inflater.getBytesRead());
146        assertEquals(5, inflater.getTotalIn());
147        assertEquals(2, inflater.getBytesWritten());
148        assertEquals(2, inflater.getTotalOut());
149
150        // Feed in bytes [5, 11) to the second iteration.
151        assertEquals(true, inflater.needsInput());
152        inflater.setInput(compressed, 5, 6);
153        assertEquals(1, inflater.inflate(decompressed, 0, decompressed.length));
154        assertEquals(11, inflater.getBytesRead());
155        assertEquals(11, inflater.getTotalIn());
156        assertEquals(3, inflater.getBytesWritten());
157        assertEquals(3, inflater.getTotalOut());
158
159        inflater.reset();
160        assertEquals(0, inflater.getBytesRead());
161        assertEquals(0, inflater.getTotalIn());
162        assertEquals(0, inflater.getBytesWritten());
163        assertEquals(0, inflater.getTotalOut());
164        inflater.end();
165    }
166}
167