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