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 */ 41 public class TransferTo { 42 43 public static void main(String[] args) throws IOException { 44 ifOutIsNullThenNpeIsThrown(); 45 ifExceptionInInputNeitherStreamIsClosed(); 46 ifExceptionInOutputNeitherStreamIsClosed(); 47 onReturnNeitherStreamIsClosed(); 48 onReturnInputIsAtEnd(); 49 contents(); 50 } 51 52 private static void ifOutIsNullThenNpeIsThrown() throws IOException { 53 try (InputStream in = input()) { 54 assertThrowsNPE(() -> in.transferTo(null), "out"); 55 } 56 57 try (InputStream in = input((byte) 1)) { 58 assertThrowsNPE(() -> in.transferTo(null), "out"); 59 } 60 61 try (InputStream in = input((byte) 1, (byte) 2)) { 62 assertThrowsNPE(() -> in.transferTo(null), "out"); 63 } 64 65 InputStream in = null; 66 try { 67 InputStream fin = in = new ThrowingInputStream(); 68 // null check should precede everything else: 69 // InputStream shouldn't be touched if OutputStream is null 70 assertThrowsNPE(() -> fin.transferTo(null), "out"); 71 } finally { 72 if (in != null) 73 try { 74 in.close(); 75 } catch (IOException ignored) { } 76 } 77 } 78 79 private static void ifExceptionInInputNeitherStreamIsClosed() 80 throws IOException { 81 transferToThenCheckIfAnyClosed(input(0, new byte[]{1, 2, 3}), output()); 82 transferToThenCheckIfAnyClosed(input(1, new byte[]{1, 2, 3}), output()); 83 transferToThenCheckIfAnyClosed(input(2, new byte[]{1, 2, 3}), output()); 84 } 85 86 private static void ifExceptionInOutputNeitherStreamIsClosed() 87 throws IOException { 88 transferToThenCheckIfAnyClosed(input(new byte[]{1, 2, 3}), output(0)); 89 transferToThenCheckIfAnyClosed(input(new byte[]{1, 2, 3}), output(1)); 90 transferToThenCheckIfAnyClosed(input(new byte[]{1, 2, 3}), output(2)); 91 } 92 93 private static void transferToThenCheckIfAnyClosed(InputStream input, 94 OutputStream output) 95 throws IOException { 96 try (CloseLoggingInputStream in = new CloseLoggingInputStream(input); 97 CloseLoggingOutputStream out = 98 new CloseLoggingOutputStream(output)) { 99 boolean thrown = false; 100 try { 101 in.transferTo(out); 102 } catch (IOException ignored) { 103 thrown = true; 104 } 105 if (!thrown) 106 throw new AssertionError(); 107 108 if (in.wasClosed() || out.wasClosed()) { 109 throw new AssertionError(); 110 } 111 } 112 } 113 114 private static void onReturnNeitherStreamIsClosed() 115 throws IOException { 116 try (CloseLoggingInputStream in = 117 new CloseLoggingInputStream(input(new byte[]{1, 2, 3})); 118 CloseLoggingOutputStream out = 119 new CloseLoggingOutputStream(output())) { 120 121 in.transferTo(out); 122 123 if (in.wasClosed() || out.wasClosed()) { 124 throw new AssertionError(); 125 } 126 } 127 } 128 129 private static void onReturnInputIsAtEnd() throws IOException { 130 try (InputStream in = input(new byte[]{1, 2, 3}); 131 OutputStream out = output()) { 132 133 in.transferTo(out); 134 135 if (in.read() != -1) { 136 throw new AssertionError(); 137 } 138 } 139 } 140 141 private static 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 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 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 170 private static OutputStream output() { 171 return output(-1); 172 } 173 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 187 private static InputStream input(byte... bytes) { 188 return input(-1, bytes); 189 } 190 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 }