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