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 }