1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.io; 18 19 import static org.junit.jupiter.api.Assertions.assertArrayEquals; 20 import static org.junit.jupiter.api.Assertions.assertEquals; 21 import static org.junit.jupiter.api.Assertions.assertNotNull; 22 import static org.junit.jupiter.api.Assertions.assertThrows; 23 24 import java.io.ByteArrayInputStream; 25 import java.io.InputStream; 26 import java.io.InputStreamReader; 27 import java.io.OutputStream; 28 import java.io.OutputStreamWriter; 29 import java.io.Reader; 30 import java.io.Writer; 31 import java.net.URL; 32 import java.nio.charset.StandardCharsets; 33 import java.nio.file.Files; 34 import java.nio.file.Paths; 35 36 import org.apache.commons.io.file.TempFile; 37 import org.apache.commons.io.input.NullInputStream; 38 import org.apache.commons.io.input.NullReader; 39 import org.apache.commons.io.output.ByteArrayOutputStream; 40 import org.apache.commons.io.output.NullOutputStream; 41 import org.apache.commons.io.output.NullWriter; 42 import org.apache.commons.io.test.TestUtils; 43 import org.apache.commons.io.test.ThrowOnCloseInputStream; 44 import org.apache.commons.io.test.ThrowOnFlushAndCloseOutputStream; 45 import org.junit.jupiter.api.Test; 46 47 /** 48 * Tests {@link IOUtils} copy methods. 49 */ 50 public class IOUtilsCopyTest { 51 52 /* 53 * NOTE this is not particularly beautiful code. A better way to check for 54 * flush and close status would be to implement "trojan horse" wrapper 55 * implementations of the various stream classes, which set a flag when 56 * relevant methods are called. (JT) 57 */ 58 59 private static final int FILE_SIZE = 1024 * 4 + 1; 60 61 private final byte[] inData = TestUtils.generateTestData(FILE_SIZE); 62 63 @SuppressWarnings("resource") // 'in' is deliberately not closed 64 @Test testCopy_byteArrayOutputStreamToInputStream()65 public void testCopy_byteArrayOutputStreamToInputStream() throws Exception { 66 final java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); 67 out.write(inData); 68 69 final InputStream in = IOUtils.copy(out); 70 71 final byte[] inData2 = new byte[FILE_SIZE]; 72 final int inSize = in.read(inData2); 73 74 assertEquals(0, in.available(), "Not all bytes were read"); 75 assertEquals(inData.length, inSize, "Sizes differ"); 76 assertArrayEquals(inData, inData2, "Content differs"); 77 } 78 79 @Test testCopy_byteArrayOutputStreamToInputStream_nullOutputStream()80 public void testCopy_byteArrayOutputStreamToInputStream_nullOutputStream() { 81 assertThrows(NullPointerException.class, () -> IOUtils.copy(null)); 82 } 83 84 @SuppressWarnings("resource") // 'in' is deliberately not closed 85 @Test testCopy_inputStreamToOutputStream()86 public void testCopy_inputStreamToOutputStream() throws Exception { 87 InputStream in = new ByteArrayInputStream(inData); 88 in = new ThrowOnCloseInputStream(in); 89 90 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 91 final OutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, false, true); 92 93 final int count = IOUtils.copy(in, out); 94 95 assertEquals(0, in.available(), "Not all bytes were read"); 96 assertEquals(inData.length, baout.size(), "Sizes differ"); 97 assertArrayEquals(inData, baout.toByteArray(), "Content differs"); 98 assertEquals(inData.length, count); 99 } 100 101 /** 102 * Test Copying file > 2GB - see issue# IO-84 103 */ 104 @Test testCopy_inputStreamToOutputStream_IO84()105 public void testCopy_inputStreamToOutputStream_IO84() throws Exception { 106 final long size = (long)Integer.MAX_VALUE + (long)1; 107 final InputStream in = new NullInputStream(size); 108 final OutputStream out = NullOutputStream.INSTANCE; 109 110 // Test copy() method 111 assertEquals(-1, IOUtils.copy(in, out)); 112 113 // reset the input 114 in.close(); 115 116 // Test copyLarge() method 117 assertEquals(size, IOUtils.copyLarge(in, out), "copyLarge()"); 118 } 119 120 @Test testCopy_inputStreamToOutputStream_nullIn()121 public void testCopy_inputStreamToOutputStream_nullIn() { 122 final OutputStream out = new ByteArrayOutputStream(); 123 assertThrows(NullPointerException.class, () -> IOUtils.copy((InputStream) null, out)); 124 } 125 126 @Test testCopy_inputStreamToOutputStream_nullOut()127 public void testCopy_inputStreamToOutputStream_nullOut() { 128 final InputStream in = new ByteArrayInputStream(inData); 129 assertThrows(NullPointerException.class, () -> IOUtils.copy(in, (OutputStream) null)); 130 } 131 132 @Test testCopy_inputStreamToOutputStreamWithBufferSize()133 public void testCopy_inputStreamToOutputStreamWithBufferSize() throws Exception { 134 testCopy_inputStreamToOutputStreamWithBufferSize(1); 135 testCopy_inputStreamToOutputStreamWithBufferSize(2); 136 testCopy_inputStreamToOutputStreamWithBufferSize(4); 137 testCopy_inputStreamToOutputStreamWithBufferSize(8); 138 testCopy_inputStreamToOutputStreamWithBufferSize(16); 139 testCopy_inputStreamToOutputStreamWithBufferSize(32); 140 testCopy_inputStreamToOutputStreamWithBufferSize(64); 141 testCopy_inputStreamToOutputStreamWithBufferSize(128); 142 testCopy_inputStreamToOutputStreamWithBufferSize(256); 143 testCopy_inputStreamToOutputStreamWithBufferSize(512); 144 testCopy_inputStreamToOutputStreamWithBufferSize(1024); 145 testCopy_inputStreamToOutputStreamWithBufferSize(2048); 146 testCopy_inputStreamToOutputStreamWithBufferSize(4096); 147 testCopy_inputStreamToOutputStreamWithBufferSize(8192); 148 testCopy_inputStreamToOutputStreamWithBufferSize(16384); 149 } 150 151 @SuppressWarnings("resource") // 'in' is deliberately not closed testCopy_inputStreamToOutputStreamWithBufferSize(final int bufferSize)152 private void testCopy_inputStreamToOutputStreamWithBufferSize(final int bufferSize) throws Exception { 153 InputStream in = new ByteArrayInputStream(inData); 154 in = new ThrowOnCloseInputStream(in); 155 156 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 157 final OutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, false, true); 158 159 final long count = IOUtils.copy(in, out, bufferSize); 160 161 assertEquals(0, in.available(), "Not all bytes were read"); 162 assertEquals(inData.length, baout.size(), "Sizes differ"); 163 assertArrayEquals(inData, baout.toByteArray(), "Content differs"); 164 assertEquals(inData.length, count); 165 } 166 167 @SuppressWarnings({ "resource", "deprecation" }) // 'in' is deliberately not closed 168 @Test testCopy_inputStreamToWriter()169 public void testCopy_inputStreamToWriter() throws Exception { 170 InputStream in = new ByteArrayInputStream(inData); 171 in = new ThrowOnCloseInputStream(in); 172 173 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 174 final ThrowOnFlushAndCloseOutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, true, true); 175 final Writer writer = new OutputStreamWriter(baout, StandardCharsets.US_ASCII); 176 177 IOUtils.copy(in, writer); // deliberately testing deprecated method 178 out.off(); 179 writer.flush(); 180 181 assertEquals(0, in.available(), "Not all bytes were read"); 182 assertEquals(inData.length, baout.size(), "Sizes differ"); 183 assertArrayEquals(inData, baout.toByteArray(), "Content differs"); 184 } 185 186 @SuppressWarnings("resource") // 'in' is deliberately not closed 187 @Test testCopy_inputStreamToWriter_Encoding()188 public void testCopy_inputStreamToWriter_Encoding() throws Exception { 189 InputStream in = new ByteArrayInputStream(inData); 190 in = new ThrowOnCloseInputStream(in); 191 192 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 193 final ThrowOnFlushAndCloseOutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, true, true); 194 final Writer writer = new OutputStreamWriter(baout, StandardCharsets.US_ASCII); 195 196 IOUtils.copy(in, writer, "UTF8"); 197 out.off(); 198 writer.flush(); 199 200 assertEquals(0, in.available(), "Not all bytes were read"); 201 byte[] bytes = baout.toByteArray(); 202 bytes = new String(bytes, StandardCharsets.UTF_8).getBytes(StandardCharsets.US_ASCII); 203 assertArrayEquals(inData, bytes, "Content differs"); 204 } 205 206 @SuppressWarnings("resource") // 'in' is deliberately not closed 207 @Test testCopy_inputStreamToWriter_Encoding_nullEncoding()208 public void testCopy_inputStreamToWriter_Encoding_nullEncoding() throws Exception { 209 InputStream in = new ByteArrayInputStream(inData); 210 in = new ThrowOnCloseInputStream(in); 211 212 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 213 final ThrowOnFlushAndCloseOutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, true, true); 214 final Writer writer = new OutputStreamWriter(baout, StandardCharsets.US_ASCII); 215 216 IOUtils.copy(in, writer, (String) null); 217 out.off(); 218 writer.flush(); 219 220 assertEquals(0, in.available(), "Not all bytes were read"); 221 assertEquals(inData.length, baout.size(), "Sizes differ"); 222 assertArrayEquals(inData, baout.toByteArray(), "Content differs"); 223 } 224 225 @Test testCopy_inputStreamToWriter_Encoding_nullIn()226 public void testCopy_inputStreamToWriter_Encoding_nullIn() { 227 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 228 final OutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, true, true); 229 final Writer writer = new OutputStreamWriter(out, StandardCharsets.US_ASCII); 230 assertThrows(NullPointerException.class, () -> IOUtils.copy(null, writer, "UTF8")); 231 } 232 233 @Test testCopy_inputStreamToWriter_Encoding_nullOut()234 public void testCopy_inputStreamToWriter_Encoding_nullOut() { 235 final InputStream in = new ByteArrayInputStream(inData); 236 assertThrows(NullPointerException.class, () -> IOUtils.copy(in, null, "UTF8")); 237 } 238 239 @SuppressWarnings("deprecation") // deliberately testing deprecated method 240 @Test testCopy_inputStreamToWriter_nullIn()241 public void testCopy_inputStreamToWriter_nullIn() { 242 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 243 final OutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, true, true); 244 final Writer writer = new OutputStreamWriter(out, StandardCharsets.US_ASCII); 245 assertThrows(NullPointerException.class, () -> IOUtils.copy((InputStream) null, writer)); 246 } 247 248 @SuppressWarnings("deprecation") // deliberately testing deprecated method 249 @Test testCopy_inputStreamToWriter_nullOut()250 public void testCopy_inputStreamToWriter_nullOut() { 251 final InputStream in = new ByteArrayInputStream(inData); 252 assertThrows(NullPointerException.class, () -> IOUtils.copy(in, (Writer) null)); // deliberately testing deprecated method 253 } 254 255 @SuppressWarnings("resource") // 'in' is deliberately not closed 256 @Test testCopy_readerToAppendable()257 public void testCopy_readerToAppendable() throws Exception { 258 InputStream in = new ByteArrayInputStream(inData); 259 in = new ThrowOnCloseInputStream(in); 260 final Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII); 261 262 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 263 final ThrowOnFlushAndCloseOutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, true, true); 264 final Writer writer = new OutputStreamWriter(baout, StandardCharsets.US_ASCII); 265 266 final long count = IOUtils.copy(reader, (Appendable) writer); 267 out.off(); 268 writer.flush(); 269 assertEquals(inData.length, count, "The number of characters returned by copy is wrong"); 270 assertEquals(inData.length, baout.size(), "Sizes differ"); 271 assertArrayEquals(inData, baout.toByteArray(), "Content differs"); 272 } 273 274 @Test testCopy_readerToAppendable_IO84()275 public void testCopy_readerToAppendable_IO84() throws Exception { 276 final long size = (long) Integer.MAX_VALUE + (long) 1; 277 final Reader reader = new NullReader(size); 278 final NullWriter writer = new NullWriter(); 279 280 // Test copy() method 281 assertEquals(size, IOUtils.copy(reader, (Appendable) writer)); 282 283 // reset the input 284 reader.close(); 285 286 // Test copyLarge() method 287 assertEquals(size, IOUtils.copyLarge(reader, writer), "copy()"); 288 } 289 290 @Test testCopy_readerToAppendable_nullIn()291 public void testCopy_readerToAppendable_nullIn() { 292 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 293 final OutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, true, true); 294 final Appendable writer = new OutputStreamWriter(out, StandardCharsets.US_ASCII); 295 assertThrows(NullPointerException.class, () -> IOUtils.copy(null, writer)); 296 } 297 298 @SuppressWarnings("resource") // 'in' is deliberately not closed 299 @Test testCopy_readerToAppendable_nullOut()300 public void testCopy_readerToAppendable_nullOut() { 301 InputStream in = new ByteArrayInputStream(inData); 302 in = new ThrowOnCloseInputStream(in); 303 final Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII); 304 assertThrows(NullPointerException.class, () -> IOUtils.copy(reader, (Appendable) null)); 305 } 306 307 @SuppressWarnings({ "resource", "deprecation" }) // 'in' is deliberately not closed 308 @Test testCopy_readerToOutputStream()309 public void testCopy_readerToOutputStream() throws Exception { 310 InputStream in = new ByteArrayInputStream(inData); 311 in = new ThrowOnCloseInputStream(in); 312 final Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII); 313 314 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 315 final OutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, false, true); 316 317 IOUtils.copy(reader, out); // deliberately testing deprecated method 318 //Note: this method *does* flush. It is equivalent to: 319 // OutputStreamWriter _out = new OutputStreamWriter(fout); 320 // IOUtils.copy( fin, _out, 4096 ); // copy( Reader, Writer, int ); 321 // _out.flush(); 322 // out = fout; 323 324 // Note: rely on the method to flush 325 assertEquals(inData.length, baout.size(), "Sizes differ"); 326 assertArrayEquals(inData, baout.toByteArray(), "Content differs"); 327 } 328 329 @SuppressWarnings("resource") // 'in' is deliberately not closed 330 @Test testCopy_readerToOutputStream_Encoding()331 public void testCopy_readerToOutputStream_Encoding() throws Exception { 332 InputStream in = new ByteArrayInputStream(inData); 333 in = new ThrowOnCloseInputStream(in); 334 final Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII); 335 336 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 337 final OutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, false, true); 338 339 IOUtils.copy(reader, out, "UTF16"); 340 // note: this method *does* flush. 341 // note: we don't flush here; this IOUtils method does it for us 342 343 byte[] bytes = baout.toByteArray(); 344 bytes = new String(bytes, StandardCharsets.UTF_16).getBytes(StandardCharsets.US_ASCII); 345 assertArrayEquals(inData, bytes, "Content differs"); 346 } 347 348 @SuppressWarnings("resource") // 'in' is deliberately not closed 349 @Test testCopy_readerToOutputStream_Encoding_nullEncoding()350 public void testCopy_readerToOutputStream_Encoding_nullEncoding() throws Exception { 351 InputStream in = new ByteArrayInputStream(inData); 352 in = new ThrowOnCloseInputStream(in); 353 final Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII); 354 355 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 356 final OutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, false, true); 357 358 IOUtils.copy(reader, out, (String) null); 359 // note: this method *does* flush. 360 // note: we don't flush here; this IOUtils method does it for us 361 362 assertEquals(inData.length, baout.size(), "Sizes differ"); 363 assertArrayEquals(inData, baout.toByteArray(), "Content differs"); 364 } 365 366 @Test testCopy_readerToOutputStream_Encoding_nullIn()367 public void testCopy_readerToOutputStream_Encoding_nullIn() { 368 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 369 final OutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, true, true); 370 assertThrows(NullPointerException.class, () -> IOUtils.copy(null, out, "UTF16")); 371 } 372 373 @SuppressWarnings("resource") // 'in' is deliberately not closed 374 @Test testCopy_readerToOutputStream_Encoding_nullOut()375 public void testCopy_readerToOutputStream_Encoding_nullOut() { 376 InputStream in = new ByteArrayInputStream(inData); 377 in = new ThrowOnCloseInputStream(in); 378 final Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII); 379 assertThrows(NullPointerException.class, () -> IOUtils.copy(reader, null, "UTF16")); 380 } 381 382 @SuppressWarnings("deprecation") 383 @Test testCopy_readerToOutputStream_nullIn()384 public void testCopy_readerToOutputStream_nullIn() { // deliberately testing deprecated method 385 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 386 final OutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, true, true); 387 assertThrows(NullPointerException.class, () -> IOUtils.copy((Reader) null, out)); 388 } 389 390 @SuppressWarnings({ "resource", "deprecation" }) // 'in' is deliberately not closed 391 @Test testCopy_readerToOutputStream_nullOut()392 public void testCopy_readerToOutputStream_nullOut() { 393 InputStream in = new ByteArrayInputStream(inData); 394 in = new ThrowOnCloseInputStream(in); 395 final Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII); 396 assertThrows(NullPointerException.class, () -> IOUtils.copy(reader, (OutputStream) null)); // deliberately testing deprecated method 397 } 398 399 @SuppressWarnings("resource") // 'in' is deliberately not closed 400 @Test testCopy_readerToWriter()401 public void testCopy_readerToWriter() throws Exception { 402 InputStream in = new ByteArrayInputStream(inData); 403 in = new ThrowOnCloseInputStream(in); 404 final Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII); 405 406 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 407 final ThrowOnFlushAndCloseOutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, true, true); 408 final Writer writer = new OutputStreamWriter(baout, StandardCharsets.US_ASCII); 409 410 final int count = IOUtils.copy(reader, writer); 411 out.off(); 412 writer.flush(); 413 assertEquals(inData.length, count, "The number of characters returned by copy is wrong"); 414 assertEquals(inData.length, baout.size(), "Sizes differ"); 415 assertArrayEquals(inData, baout.toByteArray(), "Content differs"); 416 } 417 418 /** 419 * Tests Copying file > 2GB - see issue# IO-84 420 */ 421 @Test testCopy_readerToWriter_IO84()422 public void testCopy_readerToWriter_IO84() throws Exception { 423 final long size = (long)Integer.MAX_VALUE + (long)1; 424 final Reader reader = new NullReader(size); 425 final Writer writer = new NullWriter(); 426 427 // Test copy() method 428 assertEquals(-1, IOUtils.copy(reader, writer)); 429 430 // reset the input 431 reader.close(); 432 433 // Test copyLarge() method 434 assertEquals(size, IOUtils.copyLarge(reader, writer), "copyLarge()"); 435 } 436 437 @Test testCopy_readerToWriter_nullIn()438 public void testCopy_readerToWriter_nullIn() { 439 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 440 final OutputStream out = new ThrowOnFlushAndCloseOutputStream(baout, true, true); 441 final Writer writer = new OutputStreamWriter(out, StandardCharsets.US_ASCII); 442 assertThrows(NullPointerException.class, () -> IOUtils.copy((Reader) null, writer)); 443 } 444 445 @SuppressWarnings("resource") // 'in' is deliberately not closed 446 @Test testCopy_readerToWriter_nullOut()447 public void testCopy_readerToWriter_nullOut() { 448 InputStream in = new ByteArrayInputStream(inData); 449 in = new ThrowOnCloseInputStream(in); 450 final Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII); 451 assertThrows(NullPointerException.class, () -> IOUtils.copy(reader, (Writer) null)); 452 } 453 454 @Test testCopy_URLToFile()455 public void testCopy_URLToFile() throws Exception { 456 final String name = "/org/apache/commons/io/abitmorethan16k.txt"; 457 final URL in = getClass().getResource(name); 458 assertNotNull(in, name); 459 460 try (TempFile path = TempFile.create("testCopy_URLToFile", ".txt")) { 461 IOUtils.copy(in, path.toFile()); 462 assertArrayEquals(Files.readAllBytes(Paths.get("src/test/resources" + name)), Files.readAllBytes(path.get())); 463 } 464 } 465 466 @Test testCopy_URLToOutputStream()467 public void testCopy_URLToOutputStream() throws Exception { 468 final String name = "/org/apache/commons/io/abitmorethan16k.txt"; 469 final URL in = getClass().getResource(name); 470 assertNotNull(in, name); 471 472 final ByteArrayOutputStream baout = new ByteArrayOutputStream(); 473 IOUtils.copy(in, baout); 474 475 assertArrayEquals(Files.readAllBytes(Paths.get("src/test/resources" + name)), baout.toByteArray()); 476 } 477 478 } 479