• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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