1/*
2 * Copyright 2009 Google, Inc.  All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/**
25 * @test
26 * @bug 4206909 4813885
27 * @summary Test basic functionality of DeflaterOutputStream/InflaterInputStream and GZIPOutputStream/GZIPInputStream, including flush
28 * @key randomness
29 */
30
31import java.io.*;
32import java.util.*;
33import java.util.zip.*;
34
35public class InflateIn_DeflateOut {
36
37    private static class PairedInputStream extends ByteArrayInputStream {
38        private PairedOutputStream out = null;
39        private Random random;
40
41        public PairedInputStream() {
42            // The ByteArrayInputStream needs to start with a buffer, but we
43            // need to set it to have no data
44            super(new byte[1]);
45            count = 0;
46            pos = 0;
47            random = new Random(new Date().getTime());
48        }
49
50        public void setPairedOutputStream(PairedOutputStream out) {
51            this.out = out;
52        }
53
54        private void maybeFlushPair() {
55            if (random.nextInt(100) < 10) {
56                out.flush();
57            }
58        }
59
60        public int read() {
61            maybeFlushPair();
62            return super.read();
63        }
64
65        public int read(byte b[], int off, int len) {
66            maybeFlushPair();
67            return super.read(b, off, len);
68        }
69
70        public void addBytes(byte[] bytes, int len) {
71            int oldavail = count - pos;
72            int newcount = oldavail + len;
73            byte[] newbuf = new byte[newcount];
74            System.arraycopy(buf, pos, newbuf, 0, oldavail);
75            System.arraycopy(bytes, 0, newbuf, oldavail, len);
76            pos = 0;
77            count = newcount;
78            buf = newbuf;
79        }
80    }
81
82    private static class PairedOutputStream extends ByteArrayOutputStream {
83        private PairedInputStream pairedStream = null;
84
85        public PairedOutputStream(PairedInputStream inputPair) {
86            super();
87            this.pairedStream = inputPair;
88        }
89
90        public void flush() {
91            if (count > 0) {
92                pairedStream.addBytes(buf, count);
93                reset();
94            }
95        }
96
97        public void close() {
98            flush();
99        }
100    }
101
102    private static boolean readFully(InputStream in, byte[] buf, int length)
103            throws IOException {
104        int pos = 0;
105        int n;
106        while ((n = in.read(buf, pos, length - pos)) > 0) {
107            pos += n;
108            if (pos == length) return true;
109        }
110        return false;
111    }
112
113    private static boolean readLineIfAvailable(InputStream in, StringBuilder sb)
114            throws IOException {
115        try {
116            while (in.available() > 0) {
117                int i = in.read();
118                if (i < 0) break;
119                char c = (char) (((byte) i) & 0xff);
120                sb.append(c);
121                if (c == '\n') return true;
122            }
123        } catch (EOFException e) {
124          // empty
125        }
126        return false;
127    }
128
129    /** Check that written, closed and read */
130    private static void WriteCloseRead() throws Throwable {
131        Random random = new Random(new Date().getTime());
132
133        PairedInputStream pis = new PairedInputStream();
134        InflaterInputStream iis = new InflaterInputStream(pis);
135
136        PairedOutputStream pos = new PairedOutputStream(pis);
137        pis.setPairedOutputStream(pos);
138
139        byte[] data = new byte[random.nextInt(1024 * 1024)];
140        byte[] buf = new byte[data.length];
141        random.nextBytes(data);
142
143        try (DeflaterOutputStream dos = new DeflaterOutputStream(pos, true)) {
144            dos.write(data);
145        }
146        check(readFully(iis, buf, buf.length));
147        check(Arrays.equals(data, buf));
148    }
149
150    private static void check(InputStream is, OutputStream os)
151        throws Throwable
152    {
153        Random random = new Random(new Date().getTime());
154       // Large writes
155        for (int x = 0; x < 200 ; x++) {
156            // byte[] data = new byte[random.nextInt(1024 * 1024)];
157            byte[] data = new byte[1024];
158            byte[] buf = new byte[data.length];
159            random.nextBytes(data);
160
161            os.write(data);
162            os.flush();
163            check(readFully(is, buf, buf.length));
164            check(Arrays.equals(data, buf));
165        }
166
167        // Small writes
168        for (int x = 0; x < 2000 ; x++) {
169            byte[] data = new byte[random.nextInt(20) + 10];
170            byte[] buf = new byte[data.length];
171            random.nextBytes(data);
172
173            os.write(data);
174            os.flush();
175            if (!readFully(is, buf, buf.length)) {
176                fail("Didn't read full buffer of " + buf.length);
177            }
178            check(Arrays.equals(data, buf));
179        }
180
181        String quit = "QUIT\r\n";
182
183        // Close it out
184        os.write(quit.getBytes());
185        os.close();
186
187        StringBuilder sb = new StringBuilder();
188        check(readLineIfAvailable(is, sb));
189        equal(sb.toString(), quit);
190    }
191
192    /** Check that written, flushed and read */
193    private static void WriteFlushRead() throws Throwable {
194        PairedInputStream pis = new PairedInputStream();
195        InflaterInputStream iis = new InflaterInputStream(pis);
196
197        PairedOutputStream pos = new PairedOutputStream(pis);
198        pis.setPairedOutputStream(pos);
199        DeflaterOutputStream dos = new DeflaterOutputStream(pos, true);
200
201        check(iis, dos);
202    }
203
204    private static void GZWriteFlushRead() throws Throwable {
205        PairedInputStream pis = new PairedInputStream();
206        PairedOutputStream pos = new PairedOutputStream(pis);
207        pis.setPairedOutputStream(pos);
208
209        GZIPOutputStream gos = new GZIPOutputStream(pos, true);
210        gos.flush();  // flush the head out, so gis can read
211        GZIPInputStream gis = new GZIPInputStream(pis);
212
213        check(gis, gos);
214    }
215
216    private static void checkLOP(InputStream is, OutputStream os)
217        throws Throwable
218    {
219        boolean flushed = false;
220        int count = 0;
221
222        // Do at least a certain number of lines, but too many without a
223        // flush means this test isn't testing anything
224        while ((count < 10 && flushed) || (count < 1000 && !flushed)) {
225            String command = "PING " + count + "\r\n";
226            os.write(command.getBytes());
227
228            StringBuilder buf = new StringBuilder();
229            if (!readLineIfAvailable(is, buf)) {
230                flushed = true;
231                os.flush();
232                check(readLineIfAvailable(is, buf));
233            }
234            equal(buf.toString(), command);
235            count++;
236        }
237        check(flushed);
238    }
239
240    /** Validate that we need to use flush at least once on a line
241     * oriented protocol */
242    private static void LineOrientedProtocol() throws Throwable {
243        PairedInputStream pis = new PairedInputStream();
244        InflaterInputStream iis = new InflaterInputStream(pis);
245
246        PairedOutputStream pos = new PairedOutputStream(pis);
247        pis.setPairedOutputStream(pos);
248        DeflaterOutputStream dos = new DeflaterOutputStream(pos, true);
249
250        checkLOP(iis, dos);
251    }
252
253    private static void GZLineOrientedProtocol() throws Throwable {
254        PairedInputStream pis = new PairedInputStream();
255        PairedOutputStream pos = new PairedOutputStream(pis);
256        pis.setPairedOutputStream(pos);
257
258        GZIPOutputStream gos = new GZIPOutputStream(pos, true);
259        gos.flush();  // flush the head out, so gis can read
260        GZIPInputStream gis = new GZIPInputStream(pis);
261
262        checkLOP(gis, gos);
263    }
264
265    public static void realMain(String[] args) throws Throwable {
266        WriteCloseRead();
267        WriteFlushRead();
268        LineOrientedProtocol();
269        GZWriteFlushRead();
270        GZLineOrientedProtocol();
271    }
272
273    //--------------------- Infrastructure ---------------------------
274    static volatile int passed = 0, failed = 0;
275    static void pass() {passed++;}
276    static void fail() {failed++; Thread.dumpStack();}
277    static void fail(String msg) {System.out.println(msg); fail();}
278    static void unexpected(Throwable t) {failed++; t.printStackTrace();}
279    static void check(boolean cond) {if (cond) pass(); else fail();}
280    static void equal(Object x, Object y) {
281        if (x == null ? y == null : x.equals(y)) pass();
282        else fail(x + " not equal to " + y);}
283    public static void main(String[] args) throws Throwable {
284        try {realMain(args);} catch (Throwable t) {unexpected(t);}
285        System.out.println("\nPassed = " + passed + " failed = " + failed);
286        if (failed > 0) throw new AssertionError("Some tests failed");}
287}
288