1 /*
2  * Copyright (c) 2014, 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 package test.java.io.InputStream;
24 
25 import java.io.ByteArrayOutputStream;
26 import java.io.FilterInputStream;
27 import java.io.FilterOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.util.Arrays;
32 import java.util.Random;
33 import org.testng.annotations.Test;
34 
35 import static java.lang.String.format;
36 
37 /*
38  * @test
39  * @bug 8066867
40  * @summary tests whether java.io.InputStream.transferTo conforms to its
41  *          contract defined in the javadoc
42  * @key randomness
43  */
44 public class TransferTo {
45 
46     @Test
ifOutIsNullThenNpeIsThrown()47     public void ifOutIsNullThenNpeIsThrown() throws IOException {
48         try (InputStream in = input()) {
49             assertThrowsNPE(() -> in.transferTo(null), "out");
50         }
51 
52         try (InputStream in = input((byte) 1)) {
53             assertThrowsNPE(() -> in.transferTo(null), "out");
54         }
55 
56         try (InputStream in = input((byte) 1, (byte) 2)) {
57             assertThrowsNPE(() -> in.transferTo(null), "out");
58         }
59 
60         InputStream in = null;
61         try {
62             InputStream fin = in = new ThrowingInputStream();
63             // null check should precede everything else:
64             // InputStream shouldn't be touched if OutputStream is null
65             assertThrowsNPE(() -> fin.transferTo(null), "out");
66         } finally {
67             if (in != null)
68                 try {
69                     in.close();
70                 } catch (IOException ignored) { }
71         }
72     }
73 
74     @Test
ifExceptionInInputNeitherStreamIsClosed()75     public void ifExceptionInInputNeitherStreamIsClosed()
76             throws IOException {
77         transferToThenCheckIfAnyClosed(input(0, new byte[]{1, 2, 3}), output());
78         transferToThenCheckIfAnyClosed(input(1, new byte[]{1, 2, 3}), output());
79         transferToThenCheckIfAnyClosed(input(2, new byte[]{1, 2, 3}), output());
80     }
81 
82     @Test
ifExceptionInOutputNeitherStreamIsClosed()83     public void ifExceptionInOutputNeitherStreamIsClosed()
84             throws IOException {
85         transferToThenCheckIfAnyClosed(input(new byte[]{1, 2, 3}), output(0));
86         transferToThenCheckIfAnyClosed(input(new byte[]{1, 2, 3}), output(1));
87         transferToThenCheckIfAnyClosed(input(new byte[]{1, 2, 3}), output(2));
88     }
89 
transferToThenCheckIfAnyClosed(InputStream input, OutputStream output)90     private static void transferToThenCheckIfAnyClosed(InputStream input,
91             OutputStream output)
92             throws IOException {
93         try (CloseLoggingInputStream in = new CloseLoggingInputStream(input);
94              CloseLoggingOutputStream out =
95                      new CloseLoggingOutputStream(output)) {
96             boolean thrown = false;
97             try {
98                 in.transferTo(out);
99             } catch (IOException ignored) {
100                 thrown = true;
101             }
102             if (!thrown)
103                 throw new AssertionError();
104 
105             if (in.wasClosed() || out.wasClosed()) {
106                 throw new AssertionError();
107             }
108         }
109     }
110 
111     @Test
onReturnNeitherStreamIsClosed()112     public void onReturnNeitherStreamIsClosed()
113             throws IOException {
114         try (CloseLoggingInputStream in =
115                      new CloseLoggingInputStream(input(new byte[]{1, 2, 3}));
116              CloseLoggingOutputStream out =
117                      new CloseLoggingOutputStream(output())) {
118 
119             in.transferTo(out);
120 
121             if (in.wasClosed() || out.wasClosed()) {
122                 throw new AssertionError();
123             }
124         }
125     }
126 
127     @Test
onReturnInputIsAtEnd()128     public void onReturnInputIsAtEnd() throws IOException {
129         try (InputStream in = input(new byte[]{1, 2, 3});
130              OutputStream out = output()) {
131 
132             in.transferTo(out);
133 
134             if (in.read() != -1) {
135                 throw new AssertionError();
136             }
137         }
138     }
139 
140     @Test
contents()141     public void contents() throws IOException {
142         checkTransferredContents(new byte[0]);
143         checkTransferredContents(createRandomBytes(1024, 4096));
144         // to span through several batches
145         checkTransferredContents(createRandomBytes(16384, 16384));
146     }
147 
checkTransferredContents(byte[] bytes)148     private static void checkTransferredContents(byte[] bytes)
149             throws IOException {
150         try (InputStream in = input(bytes);
151              ByteArrayOutputStream out = new ByteArrayOutputStream()) {
152             in.transferTo(out);
153 
154             byte[] outBytes = out.toByteArray();
155             if (!Arrays.equals(bytes, outBytes)) {
156                 throw new AssertionError(
157                         format("bytes.length=%s, outBytes.length=%s",
158                                 bytes.length, outBytes.length));
159             }
160         }
161     }
162 
createRandomBytes(int min, int maxRandomAdditive)163     private static byte[] createRandomBytes(int min, int maxRandomAdditive) {
164         Random rnd = new Random();
165         byte[] bytes = new byte[min + rnd.nextInt(maxRandomAdditive)];
166         rnd.nextBytes(bytes);
167         return bytes;
168     }
169 
output()170     private static OutputStream output() {
171         return output(-1);
172     }
173 
output(int exceptionPosition)174     private static OutputStream output(int exceptionPosition) {
175         return new OutputStream() {
176 
177             int pos;
178 
179             @Override
180             public void write(int b) throws IOException {
181                 if (pos++ == exceptionPosition)
182                     throw new IOException();
183             }
184         };
185     }
186 
input(byte... bytes)187     private static InputStream input(byte... bytes) {
188         return input(-1, bytes);
189     }
190 
input(int exceptionPosition, byte... bytes)191     private static InputStream input(int exceptionPosition, byte... bytes) {
192         return new InputStream() {
193 
194             int pos;
195 
196             @Override
197             public int read() throws IOException {
198                 if (pos == exceptionPosition) {
199                     // because of the pesky IOException swallowing in
200                     // java.io.InputStream.read(byte[], int, int)
201                     // pos++;
202                     throw new IOException();
203                 }
204 
205                 if (pos >= bytes.length)
206                     return -1;
207                 return bytes[pos++] & 0xff;
208             }
209         };
210     }
211 
212     private static class ThrowingInputStream extends InputStream {
213 
214         boolean closed;
215 
216         @Override
217         public int read(byte[] b) throws IOException {
218             throw new IOException();
219         }
220 
221         @Override
222         public int read(byte[] b, int off, int len) throws IOException {
223             throw new IOException();
224         }
225 
226         @Override
227         public long skip(long n) throws IOException {
228             throw new IOException();
229         }
230 
231         @Override
232         public int available() throws IOException {
233             throw new IOException();
234         }
235 
236         @Override
237         public void close() throws IOException {
238             if (!closed) {
239                 closed = true;
240                 throw new IOException();
241             }
242         }
243 
244         @Override
245         public void reset() throws IOException {
246             throw new IOException();
247         }
248 
249         @Override
250         public int read() throws IOException {
251             throw new IOException();
252         }
253     }
254 
255     private static class CloseLoggingInputStream extends FilterInputStream {
256 
257         boolean closed;
258 
259         CloseLoggingInputStream(InputStream in) {
260             super(in);
261         }
262 
263         @Override
264         public void close() throws IOException {
265             closed = true;
266             super.close();
267         }
268 
269         boolean wasClosed() {
270             return closed;
271         }
272     }
273 
274     private static class CloseLoggingOutputStream extends FilterOutputStream {
275 
276         boolean closed;
277 
278         CloseLoggingOutputStream(OutputStream out) {
279             super(out);
280         }
281 
282         @Override
283         public void close() throws IOException {
284             closed = true;
285             super.close();
286         }
287 
288         boolean wasClosed() {
289             return closed;
290         }
291     }
292 
293     public interface Thrower {
294         public void run() throws Throwable;
295     }
296 
297     public static void assertThrowsNPE(Thrower thrower, String message) {
298         assertThrows(thrower, NullPointerException.class, message);
299     }
300 
301     public static <T extends Throwable> void assertThrows(Thrower thrower,
302             Class<T> throwable,
303             String message) {
304         Throwable thrown;
305         try {
306             thrower.run();
307             thrown = null;
308         } catch (Throwable caught) {
309             thrown = caught;
310         }
311 
312         if (!throwable.isInstance(thrown)) {
313             String caught = thrown == null ?
314                     "nothing" : thrown.getClass().getCanonicalName();
315             throw new AssertionError(
316                     format("Expected to catch %s, but caught %s",
317                             throwable, caught), thrown);
318         }
319 
320         if (thrown != null && !message.equals(thrown.getMessage())) {
321             throw new AssertionError(
322                     format("Expected exception message to be '%s', but it's '%s'",
323                             message, thrown.getMessage()));
324         }
325     }
326 }