1/*
2 * Copyright (c) 2016, Oracle and/or its affiliates. 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 8155808
27 * @run main SkipTest
28 * @summary verify skip method of Process Input Stream
29 */
30
31import java.io.BufferedOutputStream;
32import java.io.File;
33import java.io.IOException;
34import java.io.InputStream;
35import java.io.OutputStream;
36
37public class SkipTest {
38    private static final String javaExe =
39            System.getProperty("java.home") +
40                    File.separator + "bin" + File.separator + "java";
41
42    private static final String classpath =
43            System.getProperty("java.class.path");
44
45    static final int BLOCK_SIZE = 10000;
46
47
48    public static void main(String[] args) throws Throwable {
49
50        // Start a Process to generate the test data to stdout and stderr
51        ProcessBuilder pb = new ProcessBuilder(javaExe, "-classpath", classpath, "SkipTest$GenerateData");
52        System.out.printf("cmd: %s%n", pb.command());
53        Process process = pb.start();
54
55        /*
56         * Verify the data expected is received mixing reads and skip.
57         */
58        try (InputStream in = process.getInputStream()) {
59
60            // Note: Process.getInputStream() actually returns a BufferedInputStream whose
61            // skip() method works perfectly, partially obscuring the bug. Only when the
62            // BufferedInputStream's buffer is drained, and it passes the skip() call to
63            // the underlying native stream, does the problem occur.
64
65            long n = in.skip(-1);
66            if (n != 0) {
67                throw new AssertionError("skip(-1) should return 0");
68            }
69            n = in.skip(0);
70            if (n != 0) {
71                throw new AssertionError("skip(0) should return 0");
72            }
73
74            // Now iterate all the data blocks
75            int header;
76            for (int expectedHeader = 'A'; (header = in.read()) != -1; expectedHeader++) {
77                // The header byte should be simple 'A' to 'Z'.
78                // When the bug hits, we will get lowercase letters instead.
79                if (header != expectedHeader) {
80                    throw new AssertionError("header char wrong, expected: " +
81                            expectedHeader + ", actual: " + header);
82                }
83
84                // Handle the data bytes.
85                // If the correct number of bytes are not skipped,
86                // then subsequent reads become out-of-sync;
87                int remaining = BLOCK_SIZE;
88                do {
89                    remaining -= in.skip(remaining);
90                } while (remaining != 0);
91            }
92            n = in.skip(1);
93            if (n != 0) {
94                throw new AssertionError("skip(1) at eof should return 0");
95            }
96        }
97
98        /**
99         * Do the same for the standard error stream.
100         */
101        try (InputStream in = process.getErrorStream()) {
102            long n = in.skip(-1);
103            if (n != 0) {
104                throw new AssertionError("skip(-1) should return 0");
105            }
106            n = in.skip(0);
107            if (n != 0) {
108                throw new AssertionError("skip(0) should return 0");
109            }
110
111            // Now iterate all the data blocks
112            int header;
113            for (int expectedHeader = 'A'; (header = in.read()) != -1; expectedHeader++) {
114                // The header byte should be simple 'A' to 'Z'.
115                // When the bug hits, we will get lowercase letters instead.
116                if (header != expectedHeader) {
117                    throw new AssertionError("header char wrong, expected: " +
118                            expectedHeader + ", actual: " + header);
119                }
120
121                // Handle the data bytes.
122                // If the correct number of bytes are not skipped,
123                // then subsequent reads become out-of-sync;
124                int remaining = BLOCK_SIZE;
125                do {
126                    remaining -= in.skip(remaining);
127                } while (remaining != 0);
128            }
129            n = in.skip(1);
130            if (n != 0) {
131                throw new AssertionError("skip(1) at eof should return 0");
132            }
133        }
134    }
135
136    /**
137     * Alternate main to generate the test data to standard output
138     * and standard error.
139     */
140    static class GenerateData {
141
142        public static void main(String[] args) {
143            // Generate test data containing a series of data blocks of length BLOCK_SIZE,
144            // each with a one-byte header. For example's sake, the "header" is a capital letter,
145            // and the "data" is the lowercase version of that letter repeated BLOCK_SIZE times:
146            try (OutputStream out = new BufferedOutputStream(System.out)) {
147                for (int header = 'A'; header <= 'Z'; header++) {
148                    out.write(header);
149
150                    int data = Character.toLowerCase(header);
151                    for (int i = 0; i < BLOCK_SIZE; i++) {
152                        out.write(data);
153                    }
154                }
155            } catch (IOException ioe) {
156                ioe.printStackTrace();
157                System.exit(1);
158            }
159            // Generate the same data to the error output
160            try (OutputStream err = new BufferedOutputStream(System.err)) {
161                for (int header = 'A'; header <= 'Z'; header++) {
162                    err.write(header);
163
164                    int data = Character.toLowerCase(header);
165                    for (int i = 0; i < BLOCK_SIZE; i++) {
166                        err.write(data);
167                    }
168                }
169            } catch (IOException ioe) {
170                ioe.printStackTrace();
171                System.exit(1);
172            }
173        }
174    }
175}
176