View Javadoc
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  
18  package org.apache.juneau.commons.utils;
19  
20  import static org.apache.juneau.commons.utils.IoUtils.*;
21  import static org.junit.jupiter.api.Assertions.*;
22  
23  import java.io.*;
24  import java.nio.charset.*;
25  import java.nio.file.*;
26  import java.util.concurrent.atomic.*;
27  import java.util.*;
28  
29  import org.apache.juneau.*;
30  import org.junit.jupiter.api.*;
31  
32  /**
33   * Tests {@link IoUtils}.
34   */
35  class IoUtils_Test extends TestBase {
36  
37  	//====================================================================================================
38  	// Constructor (line 35)
39  	//====================================================================================================
40  	@Test
41  	void a00_constructor() {
42  		// Test line 35: class instantiation
43  		// IoUtils has an implicit public no-arg constructor
44  		var instance = new IoUtils();
45  		assertNotNull(instance);
46  	}
47  
48  	//====================================================================================================
49  	// close(Object...)
50  	//====================================================================================================
51  	@Test
52  	void a001_close() throws IOException {
53  		var is = new TestInputStream("test");
54  		var os = new TestOutputStream();
55  		var r = new TestReader("test");
56  		var w = new TestWriter();
57  
58  		close(is, os, r, w);
59  
60  		assertTrue(is.closed);
61  		assertTrue(os.closed);
62  		assertTrue(r.closed);
63  		assertTrue(w.closed);
64  
65  		// Test with null entries
66  		close((Object)null, is, null, os);
67  		// Should not throw
68  
69  		// Test with empty array
70  		close();
71  
72  		// Test with IOException (line 100)
73  		var throwingIs = new ThrowingTestInputStream("test");
74  		var throwingOs = new ThrowingTestOutputStream();
75  		var throwingR = new ThrowingTestReader();
76  		var throwingW = new ThrowingTestWriter();
77  		assertThrows(IOException.class, () -> close(throwingIs, throwingOs, throwingR, throwingW));
78  	}
79  
80  	//====================================================================================================
81  	// closeQuietly(InputStream)
82  	//====================================================================================================
83  	@Test
84  	void a002_closeQuietly_InputStream() {
85  		var is = new TestInputStream("test");
86  		closeQuietly(is);
87  		assertTrue(is.closed);
88  
89  		// Test with null
90  		closeQuietly((InputStream)null);
91  		// Should not throw
92  	}
93  
94  	//====================================================================================================
95  	// closeQuietly(Object...)
96  	//====================================================================================================
97  	@Test
98  	void a003_closeQuietly_Object() {
99  		var is = new TestInputStream("test");
100 		var os = new TestOutputStream();
101 		var r = new TestReader("test");
102 		var w = new TestWriter();
103 
104 		closeQuietly(is, os, r, w);
105 
106 		assertTrue(is.closed);
107 		assertTrue(os.closed);
108 		assertTrue(r.closed);
109 		assertTrue(w.closed);
110 
111 		// Test with null entries
112 		closeQuietly((Object)null, is, null, os);
113 		// Should not throw
114 
115 		// Test with empty array
116 		closeQuietly();
117 	}
118 
119 	//====================================================================================================
120 	// closeQuietly(OutputStream)
121 	//====================================================================================================
122 	@Test
123 	void a004_closeQuietly_OutputStream() {
124 		var os = new TestOutputStream();
125 		closeQuietly(os);
126 		assertTrue(os.closed);
127 
128 		// Test with null
129 		closeQuietly((OutputStream)null);
130 		// Should not throw
131 	}
132 
133 	//====================================================================================================
134 	// closeQuietly(Reader)
135 	//====================================================================================================
136 	@Test
137 	void a005_closeQuietly_Reader() {
138 		var r = new TestReader("test");
139 		closeQuietly(r);
140 		assertTrue(r.closed);
141 
142 		// Test with null
143 		closeQuietly((Reader)null);
144 		// Should not throw
145 	}
146 
147 	//====================================================================================================
148 	// closeQuietly(Writer)
149 	//====================================================================================================
150 	@Test
151 	void a006_closeQuietly_Writer() {
152 		var w = new TestWriter();
153 		closeQuietly(w);
154 		assertTrue(w.closed);
155 
156 		// Test with null
157 		closeQuietly((Writer)null);
158 		// Should not throw
159 	}
160 
161 	//====================================================================================================
162 	// count(InputStream)
163 	//====================================================================================================
164 	@Test
165 	void a007_count_InputStream() throws IOException {
166 		var data = "Hello World";
167 		var is = new ByteArrayInputStream(data.getBytes());
168 		var count = count(is);
169 		assertEquals(data.length(), count);
170 		assertTrue(is.available() == 0 || is.read() == -1); // Stream should be closed/consumed
171 
172 		// Test with null
173 		assertEquals(0, count((InputStream)null));
174 	}
175 
176 	//====================================================================================================
177 	// count(Reader)
178 	//====================================================================================================
179 	@Test
180 	void a008_count_Reader() throws IOException {
181 		var data = "Hello World";
182 		var r = new StringReader(data);
183 		var count = count(r);
184 		assertEquals(data.length(), count);
185 
186 		// Test with null
187 		assertEquals(0, count((Reader)null));
188 	}
189 
190 	//====================================================================================================
191 	// flush(Object...)
192 	//====================================================================================================
193 	@Test
194 	void a009_flush() throws IOException {
195 		var os = new ByteArrayOutputStream();
196 		var w = new StringWriter();
197 
198 		os.write("test".getBytes());
199 		w.write("test");
200 
201 		flush(os, w);
202 
203 		// Test with null entries
204 		flush((Object)null, os, null, w);
205 		// Should not throw
206 
207 		// Test with empty array
208 		flush();
209 
210 		// Test with IOException (line 245)
211 		var throwingOs = new ThrowingTestOutputStream();
212 		var throwingW = new ThrowingTestWriter();
213 		assertThrows(IOException.class, () -> flush(throwingOs, throwingW));
214 	}
215 
216 	//====================================================================================================
217 	// loadSystemResourceAsString(String, String...)
218 	//====================================================================================================
219 	@Test
220 	void a010_loadSystemResourceAsString() throws Exception {
221 		assertNotNull(loadSystemResourceAsString("test1.txt", "."));
222 		assertNull(loadSystemResourceAsString("test2.txt", "."));
223 		assertNull(loadSystemResourceAsString("test3.txt", "sub"));
224 		assertNull(loadSystemResourceAsString("test3.txt", "sub2"));
225 		assertNotNull(loadSystemResourceAsString("test3.txt", "."));
226 		assertNotNull(loadSystemResourceAsString("test4.txt", ".", "sub"));
227 		assertNotNull(loadSystemResourceAsString("test4.txt", "sub"));
228 
229 		// Test system resource path (line 280)
230 		// Try to load a resource that exists in the system classloader
231 		loadSystemResourceAsString("java/lang/String.class", "."); // Exercise the code path
232 		// This may or may not return null depending on whether the resource is accessible
233 		// The important thing is that line 280 is executed
234 	}
235 
236 	//====================================================================================================
237 	// pipe(byte[], OutputStream, int)
238 	//====================================================================================================
239 	@Test
240 	void a011_pipe_byteArray_OutputStream_int() throws IOException {
241 		var data = "Hello World".getBytes();
242 		var os = new ByteArrayOutputStream();
243 		var count = pipe(data, os, -1);
244 		assertEquals(data.length, count);
245 		assertArrayEquals(data, os.toByteArray());
246 
247 		// Test with maxBytes
248 		os = new ByteArrayOutputStream();
249 		count = pipe(data, os, 5);
250 		assertEquals(5, count);
251 		assertEquals(5, os.toByteArray().length);
252 
253 		// Test with null
254 		assertEquals(0, pipe((byte[])null, os, -1));
255 		assertEquals(0, pipe(data, null, -1));
256 	}
257 
258 	//====================================================================================================
259 	// pipe(InputStream, OutputStream)
260 	//====================================================================================================
261 	@Test
262 	void a012_pipe_InputStream_OutputStream() throws IOException {
263 		var data = "Hello World";
264 		var is = new ByteArrayInputStream(data.getBytes());
265 		var os = new ByteArrayOutputStream();
266 		var count = pipe(is, os);
267 		assertEquals(data.length(), count);
268 		assertEquals(data, os.toString());
269 		assertTrue(is.available() == 0 || is.read() == -1); // Stream should be closed
270 
271 		// Test with null
272 		assertEquals(0, pipe((InputStream)null, os));
273 		assertEquals(0, pipe(is, (OutputStream)null));
274 	}
275 
276 	//====================================================================================================
277 	// pipe(InputStream, OutputStream, Consumer<IOException>)
278 	//====================================================================================================
279 	@Test
280 	void a013_pipe_InputStream_OutputStream_Consumer() {
281 		var data = "Hello World";
282 		var is = new ByteArrayInputStream(data.getBytes());
283 		var os = new ByteArrayOutputStream();
284 		var exceptionCaught = new AtomicBoolean(false);
285 		var count = pipe(is, os, e -> exceptionCaught.set(true));
286 		assertEquals(data.length(), count);
287 		assertEquals(data, os.toString());
288 		assertFalse(exceptionCaught.get());
289 
290 		// Test with IOException (line 355)
291 		var throwingIs = new ThrowingTestInputStream(data);
292 		exceptionCaught.set(false);
293 		var exception = new AtomicReference<IOException>();
294 		count = pipe(throwingIs, os, e -> {
295 			exceptionCaught.set(true);
296 			exception.set(e);
297 		});
298 		assertTrue(exceptionCaught.get());
299 		assertNotNull(exception.get());
300 		assertEquals(-1, count);
301 	}
302 
303 	//====================================================================================================
304 	// pipe(InputStream, OutputStream, long)
305 	//====================================================================================================
306 	@Test
307 	void a014_pipe_InputStream_OutputStream_long() throws IOException {
308 		var data = "Hello World";
309 		var is = new ByteArrayInputStream(data.getBytes());
310 		var os = new ByteArrayOutputStream();
311 		var count = pipe(is, os, -1);
312 		assertEquals(data.length(), count);
313 		assertEquals(data, os.toString());
314 
315 		// Test with maxBytes
316 		is = new ByteArrayInputStream(data.getBytes());
317 		os = new ByteArrayOutputStream();
318 		count = pipe(is, os, 5);
319 		assertEquals(5, count);
320 		assertEquals(5, os.toString().length());
321 
322 		// Test with null
323 		assertEquals(0, pipe((InputStream)null, os, -1));
324 		assertEquals(0, pipe(is, null, -1));
325 
326 		// Test with end of stream (read returns -1) (line 395)
327 		var emptyIs = new ByteArrayInputStream(new byte[0]);
328 		os = new ByteArrayOutputStream();
329 		count = pipe(emptyIs, os, 100);
330 		assertEquals(0, count);
331 		assertEquals(0, os.size());
332 	}
333 
334 	//====================================================================================================
335 	// pipe(InputStream, Writer)
336 	//====================================================================================================
337 	@Test
338 	void a015_pipe_InputStream_Writer() throws IOException {
339 		var data = "Hello World";
340 		var is = new ByteArrayInputStream(data.getBytes());
341 		var w = new StringWriter();
342 		var count = pipe(is, w);
343 		assertEquals(data.length(), count);
344 		assertEquals(data, w.toString());
345 
346 		// Test with null
347 		assertEquals(0, pipe((InputStream)null, w));
348 		assertEquals(0, pipe(is, (Writer)null));
349 	}
350 
351 	//====================================================================================================
352 	// pipe(InputStream, Writer, Consumer<IOException>)
353 	//====================================================================================================
354 	@Test
355 	void a016_pipe_InputStream_Writer_Consumer() {
356 		var data = "Hello World";
357 		var is = new ByteArrayInputStream(data.getBytes());
358 		var w = new StringWriter();
359 		var exceptionCaught = new AtomicBoolean(false);
360 		var count = pipe(is, w, e -> exceptionCaught.set(true));
361 		assertEquals(data.length(), count);
362 		assertEquals(data, w.toString());
363 		assertFalse(exceptionCaught.get());
364 
365 		// Test with null (line 444)
366 		assertEquals(0, pipe((InputStream)null, w, e -> exceptionCaught.set(true)));
367 		assertEquals(0, pipe(is, (Writer)null, e -> exceptionCaught.set(true)));
368 
369 		// Test with IOException (line 447)
370 		var throwingIs = new ThrowingTestInputStream(data);
371 		exceptionCaught.set(false);
372 		var exception = new AtomicReference<IOException>();
373 		count = pipe(throwingIs, w, e -> {
374 			exceptionCaught.set(true);
375 			exception.set(e);
376 		});
377 		assertTrue(exceptionCaught.get());
378 		assertNotNull(exception.get());
379 		assertEquals(-2, count);
380 	}
381 
382 	//====================================================================================================
383 	// pipe(Reader, File)
384 	//====================================================================================================
385 	@Test
386 	void a017_pipe_Reader_File() throws IOException {
387 		var data = "Hello World";
388 		var r = new StringReader(data);
389 		var file = File.createTempFile("test", ".txt");
390 		try {
391 			var count = pipe(r, file);
392 			assertEquals(data.length(), count);
393 			var content = read(file);
394 			assertEquals(data, content);
395 		} finally {
396 			file.delete();
397 		}
398 
399 		// Test with null
400 		assertEquals(0, pipe((Reader)null, file));
401 		assertEquals(0, pipe(r, (File)null));
402 	}
403 
404 	//====================================================================================================
405 	// pipe(Reader, OutputStream)
406 	//====================================================================================================
407 	@Test
408 	void a018_pipe_Reader_OutputStream() throws IOException {
409 		var data = "Hello World";
410 		var r = new StringReader(data);
411 		var os = new ByteArrayOutputStream();
412 		var count = pipe(r, os);
413 		assertEquals(data.length(), count);
414 		assertEquals(data, os.toString());
415 
416 		// Test with null
417 		assertEquals(0, pipe((Reader)null, os));
418 		assertEquals(0, pipe(r, (OutputStream)null));
419 	}
420 
421 	//====================================================================================================
422 	// pipe(Reader, OutputStream, Consumer<IOException>)
423 	//====================================================================================================
424 	@Test
425 	void a019_pipe_Reader_OutputStream_Consumer() {
426 		var data = "Hello World";
427 		var r = new StringReader(data);
428 		var os = new ByteArrayOutputStream();
429 		var exceptionCaught = new AtomicBoolean(false);
430 		var count = pipe(r, os, e -> exceptionCaught.set(true));
431 		assertEquals(data.length(), count);
432 		assertEquals(data, os.toString());
433 		assertFalse(exceptionCaught.get());
434 
435 		// Test with IOException (line 523)
436 		var throwingR = new StringReader(data) {
437 			@Override
438 			public int read(char[] cbuf, int off, int len) throws IOException {
439 				throw new IOException("Test exception");
440 			}
441 		};
442 		exceptionCaught.set(false);
443 		var exception = new AtomicReference<IOException>();
444 		count = pipe(throwingR, os, e -> {
445 			exceptionCaught.set(true);
446 			exception.set(e);
447 		});
448 		assertTrue(exceptionCaught.get());
449 		assertNotNull(exception.get());
450 		assertEquals(-1, count);
451 	}
452 
453 	//====================================================================================================
454 	// pipe(Reader, Writer)
455 	//====================================================================================================
456 	@Test
457 	void a020_pipe_Reader_Writer() throws IOException {
458 		var data = "foobar";
459 		var in = new TestReader(data);
460 		var out = new TestWriter();
461 
462 		var count = pipe(in, out);
463 		assertTrue(in.closed);
464 		assertFalse(out.closed);
465 		assertEquals(data, out.toString());
466 		assertEquals(data.length(), count);
467 
468 		// Test with null
469 		assertEquals(0, pipe((Reader)null, out));
470 		assertEquals(0, pipe(in, (Writer)null));
471 	}
472 
473 	//====================================================================================================
474 	// pipe(Reader, Writer, Consumer<IOException>)
475 	//====================================================================================================
476 	@Test
477 	void a021_pipe_Reader_Writer_Consumer() {
478 		var data = "Hello World";
479 		var r = new StringReader(data);
480 		var w = new StringWriter();
481 		var exceptionCaught = new AtomicBoolean(false);
482 		var count = pipe(r, w, e -> exceptionCaught.set(true));
483 		assertEquals(data.length(), count);
484 		assertEquals(data, w.toString());
485 		assertFalse(exceptionCaught.get());
486 
487 		// Test with IOException (line 578)
488 		var throwingR = new StringReader(data) {
489 			@Override
490 			public int read(char[] cbuf, int off, int len) throws IOException {
491 				throw new IOException("Test exception");
492 			}
493 		};
494 		exceptionCaught.set(false);
495 		var exception = new AtomicReference<IOException>();
496 		count = pipe(throwingR, w, e -> {
497 			exceptionCaught.set(true);
498 			exception.set(e);
499 		});
500 		assertTrue(exceptionCaught.get());
501 		assertNotNull(exception.get());
502 		assertEquals(-1, count);
503 	}
504 
505 	//====================================================================================================
506 	// pipeLines(Reader, Writer)
507 	//====================================================================================================
508 	@Test
509 	void a022_pipeLines() throws IOException {
510 		var data = "Line 1\nLine 2\nLine 3";
511 		var r = new StringReader(data);
512 		var w = new StringWriter();
513 		var count = pipeLines(r, w);
514 		assertEquals(data.length() + 1, count); // +1 for final newline
515 		assertTrue(w.toString().contains("Line 1"));
516 		assertTrue(w.toString().contains("Line 2"));
517 		assertTrue(w.toString().contains("Line 3"));
518 
519 		// Test with null
520 		assertEquals(0, pipeLines((Reader)null, w));
521 		assertEquals(0, pipeLines(r, null));
522 	}
523 
524 	//====================================================================================================
525 	// read(byte[])
526 	//====================================================================================================
527 	@Test
528 	void a023_read_byteArray() {
529 		var data = "Hello World";
530 		var bytes = data.getBytes();
531 		var result = read(bytes);
532 		assertEquals(data, result);
533 
534 		// Test with null
535 		assertNull(read((byte[])null));
536 	}
537 
538 	//====================================================================================================
539 	// read(byte[], Charset)
540 	//====================================================================================================
541 	@Test
542 	void a024_read_byteArray_Charset() {
543 		var data = "Hello World";
544 		var bytes = data.getBytes(StandardCharsets.UTF_8);
545 		var result = read(bytes, StandardCharsets.UTF_8);
546 		assertEquals(data, result);
547 
548 		// Test with different charset
549 		bytes = data.getBytes(StandardCharsets.ISO_8859_1);
550 		result = read(bytes, StandardCharsets.ISO_8859_1);
551 		assertEquals(data, result);
552 
553 		// Test with null
554 		assertNull(read((byte[])null, StandardCharsets.UTF_8));
555 	}
556 
557 	//====================================================================================================
558 	// read(File)
559 	//====================================================================================================
560 	@Test
561 	void a025_read_File() throws IOException {
562 		var p = new Properties();
563 		p.load(new StringReader(read(Paths.get("src/test/resources/files/Test3.properties").toFile())));
564 		assertEquals("files/Test3.properties", p.get("file"));
565 
566 		// Test with null
567 		assertNull(read((File)null));
568 
569 		// Test with non-existent file
570 		var nonExistent = new File("nonexistent.txt");
571 		assertNull(read(nonExistent));
572 	}
573 
574 	//====================================================================================================
575 	// read(InputStream)
576 	//====================================================================================================
577 	@Test
578 	void a026_read_InputStream() throws IOException {
579 		var data = "Hello World";
580 		var is = new ByteArrayInputStream(data.getBytes());
581 		var result = read(is);
582 		assertEquals(data, result);
583 
584 		// Test with null
585 		assertNull(read((InputStream)null));
586 	}
587 
588 	//====================================================================================================
589 	// read(InputStream, Charset)
590 	//====================================================================================================
591 	@Test
592 	void a027_read_InputStream_Charset() throws IOException {
593 		var data = "Hello World";
594 		var is = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
595 		var result = read(is, StandardCharsets.UTF_8);
596 		assertEquals(data, result);
597 
598 		// Test with null
599 		assertNull(read((InputStream)null, StandardCharsets.UTF_8));
600 	}
601 
602 	//====================================================================================================
603 	// read(InputStream, Charset, Consumer<IOException>)
604 	//====================================================================================================
605 	@Test
606 	void a028_read_InputStream_Charset_Consumer() {
607 		var data = "Hello World";
608 		var is = new ByteArrayInputStream(data.getBytes());
609 		var exceptionCaught = new AtomicBoolean(false);
610 		var result = read(is, StandardCharsets.UTF_8, e -> exceptionCaught.set(true));
611 		assertEquals(data, result);
612 		assertFalse(exceptionCaught.get());
613 
614 		// Test with null
615 		assertNull(read((InputStream)null, StandardCharsets.UTF_8, e -> exceptionCaught.set(true)));
616 
617 		// Test with IOException (line 727)
618 		var throwingIs = new ThrowingTestInputStream(data);
619 		exceptionCaught.set(false);
620 		var exception = new AtomicReference<IOException>();
621 		result = read(throwingIs, StandardCharsets.UTF_8, e -> {
622 			exceptionCaught.set(true);
623 			exception.set(e);
624 		});
625 		assertTrue(exceptionCaught.get());
626 		assertNotNull(exception.get());
627 		assertNull(result);
628 	}
629 
630 	//====================================================================================================
631 	// read(InputStream, Consumer<IOException>)
632 	//====================================================================================================
633 	@Test
634 	void a029_read_InputStream_Consumer() {
635 		var data = "Hello World";
636 		var is = new ByteArrayInputStream(data.getBytes());
637 		var exceptionCaught = new AtomicBoolean(false);
638 		var result = read(is, e -> exceptionCaught.set(true));
639 		assertEquals(data, result);
640 		assertFalse(exceptionCaught.get());
641 
642 		// Test with null
643 		assertNull(read((InputStream)null, e -> exceptionCaught.set(true)));
644 	}
645 
646 	//====================================================================================================
647 	// read(InputStream, int)
648 	//====================================================================================================
649 	@Test
650 	void a030_read_InputStream_int() throws IOException {
651 		var data = "Hello World";
652 		var is = new ByteArrayInputStream(data.getBytes());
653 		// Note: maxBytes is used for buffer sizing, not limiting the read
654 		var result = read(is, 5);
655 		assertEquals(data, result); // Reads all bytes
656 
657 		// Test with -1 (read all)
658 		is = new ByteArrayInputStream(data.getBytes());
659 		result = read(is, -1);
660 		assertEquals(data, result);
661 
662 		// Test with null
663 		assertNull(read((InputStream)null, 10));
664 	}
665 
666 	//====================================================================================================
667 	// read(InputStream, int, Charset)
668 	//====================================================================================================
669 	@Test
670 	void a031_read_InputStream_int_Charset() throws IOException {
671 		var data = "Hello World";
672 		var is = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
673 		// Note: maxBytes is used for buffer sizing, not limiting the read
674 		var result = read(is, 5, StandardCharsets.UTF_8);
675 		assertEquals(data, result); // Reads all bytes
676 
677 		// Test with null
678 		assertNull(read((InputStream)null, 10, StandardCharsets.UTF_8));
679 	}
680 
681 	//====================================================================================================
682 	// read(InputStream, long)
683 	//====================================================================================================
684 	@Test
685 	void a032_read_InputStream_long() throws IOException {
686 		var data = "Hello World";
687 		var is = new ByteArrayInputStream(data.getBytes());
688 		// Note: maxBytes is used for buffer sizing, not limiting the read
689 		var result = read(is, 5L);
690 		assertEquals(data, result); // Reads all bytes
691 
692 		// Test with null
693 		assertNull(read((InputStream)null, 10L));
694 	}
695 
696 	//====================================================================================================
697 	// read(Object)
698 	//====================================================================================================
699 	@Test
700 	void a033_read_Object() throws IOException {
701 		var data = "Hello World";
702 
703 		// Test with Reader (line 827)
704 		var r = new StringReader(data);
705 		Object readerObj = r; // Explicitly cast to Object to ensure read(Object) is called
706 		assertEquals(data, read(readerObj));
707 
708 		// Test with InputStream (line 829)
709 		var is = new ByteArrayInputStream(data.getBytes());
710 		Object inputStreamObj = is; // Explicitly cast to Object to ensure read(Object) is called
711 		assertEquals(data, read(inputStreamObj));
712 
713 		// Test with File (line 831)
714 		var file = File.createTempFile("test", ".txt");
715 		try {
716 			try (var w = new FileWriter(file)) {
717 				w.write(data);
718 			}
719 			Object fileObj = file; // Explicitly cast to Object to ensure read(Object) is called
720 			assertEquals(data, read(fileObj));
721 		} finally {
722 			file.delete();
723 		}
724 
725 		// Test with byte[] (line 833)
726 		var bytes = data.getBytes();
727 		Object bytesObj = bytes; // Explicitly cast to Object to ensure read(Object) is called
728 		assertEquals(data, read(bytesObj));
729 
730 		// Test with null
731 		assertNull(read((Object)null));
732 
733 		// Test with invalid type
734 		assertThrows(IllegalArgumentException.class, () -> read(new Object()));
735 	}
736 
737 	//====================================================================================================
738 	// read(Path)
739 	//====================================================================================================
740 	@Test
741 	void a034_read_Path() throws IOException {
742 		var p = new Properties();
743 		p.load(new StringReader(read(Paths.get("src/test/resources/files/Test3.properties"))));
744 		assertEquals("files/Test3.properties", p.get("file"));
745 
746 		// Test with null
747 		assertNull(read((Path)null));
748 
749 		// Test with non-existent path
750 		var nonExistent = Paths.get("nonexistent.txt");
751 		assertNull(read(nonExistent));
752 	}
753 
754 	//====================================================================================================
755 	// read(Reader)
756 	//====================================================================================================
757 	@Test
758 	void a035_read_Reader() throws IOException {
759 		var data = "Hello World";
760 		var r = new StringReader(data);
761 		var result = read(r);
762 		assertEquals(data, result);
763 
764 		// Test with null
765 		assertNull(read((Reader)null));
766 	}
767 
768 	//====================================================================================================
769 	// read(Reader, Consumer<IOException>)
770 	//====================================================================================================
771 	@Test
772 	void a036_read_Reader_Consumer() {
773 		var data = "Hello World";
774 		var r = new StringReader(data);
775 		var exceptionCaught = new AtomicBoolean(false);
776 		var result = read(r, e -> exceptionCaught.set(true));
777 		assertEquals(data, result);
778 		assertFalse(exceptionCaught.get());
779 
780 		// Test with null
781 		assertNull(read((Reader)null, e -> exceptionCaught.set(true)));
782 
783 		// Test with IOException (line 900)
784 		var throwingR = new ThrowingTestReader();
785 		exceptionCaught.set(false);
786 		var exception = new AtomicReference<IOException>();
787 		result = read(throwingR, e -> {
788 			exceptionCaught.set(true);
789 			exception.set(e);
790 		});
791 		assertTrue(exceptionCaught.get());
792 		assertNotNull(exception.get());
793 		assertNull(result);
794 	}
795 
796 	//====================================================================================================
797 	// read(Reader, long)
798 	//====================================================================================================
799 	@Test
800 	void a037_read_Reader_long() throws IOException {
801 		var data = "Hello World";
802 		var r = new StringReader(data);
803 		var result = read(r, data.length());
804 		assertEquals(data, result);
805 
806 		// Test with -1 (unknown length)
807 		r = new StringReader(data);
808 		result = read(r, -1);
809 		assertEquals(data, result);
810 
811 		// Test with null
812 		assertNull(read((Reader)null, 10L));
813 	}
814 
815 	//====================================================================================================
816 	// readBytes(File)
817 	//====================================================================================================
818 	@Test
819 	void a038_readBytes_File() throws IOException {
820 		var data = "Hello World";
821 		var file = File.createTempFile("test", ".txt");
822 		try {
823 			try (var w = new FileWriter(file)) {
824 				w.write(data);
825 			}
826 			var bytes = readBytes(file);
827 			assertArrayEquals(data.getBytes(), bytes);
828 		} finally {
829 			file.delete();
830 		}
831 
832 		// Test with null
833 		var bytes = readBytes((File)null);
834 		assertArrayEquals(new byte[0], bytes);
835 
836 		// Test with non-existent file
837 		var nonExistent = new File("nonexistent.txt");
838 		bytes = readBytes(nonExistent);
839 		assertArrayEquals(new byte[0], bytes);
840 	}
841 
842 	//====================================================================================================
843 	// readBytes(File, int)
844 	//====================================================================================================
845 	@Test
846 	void a039_readBytes_File_int() throws IOException {
847 		var data = "Hello World";
848 		var file = File.createTempFile("test", ".txt");
849 		try {
850 			try (var w = new FileWriter(file)) {
851 				w.write(data);
852 			}
853 			// Note: maxBytes is used for buffer sizing, not limiting the read
854 			var bytes = readBytes(file, 5);
855 			assertArrayEquals(data.getBytes(), bytes); // Reads all bytes
856 		} finally {
857 			file.delete();
858 		}
859 
860 		// Test with null
861 		var bytes = readBytes((File)null, 10);
862 		assertArrayEquals(new byte[0], bytes);
863 	}
864 
865 	//====================================================================================================
866 	// readBytes(InputStream)
867 	//====================================================================================================
868 	@Test
869 	void a040_readBytes_InputStream() throws IOException {
870 		var data = "Hello World";
871 		var is = new ByteArrayInputStream(data.getBytes());
872 		var bytes = readBytes(is);
873 		assertArrayEquals(data.getBytes(), bytes);
874 
875 		// Test with null
876 		var bytes2 = readBytes((InputStream)null);
877 		assertArrayEquals(new byte[0], bytes2);
878 	}
879 
880 	//====================================================================================================
881 	// readBytes(InputStream, int)
882 	//====================================================================================================
883 	@Test
884 	void a041_readBytes_InputStream_int() throws IOException {
885 		var data = "Hello World";
886 		var is = new ByteArrayInputStream(data.getBytes());
887 		// Note: maxBytes is used for buffer sizing, not limiting the read
888 		var bytes = readBytes(is, 5);
889 		assertArrayEquals(data.getBytes(), bytes); // Reads all bytes
890 
891 		// Test with -1 (read all)
892 		is = new ByteArrayInputStream(data.getBytes());
893 		bytes = readBytes(is, -1);
894 		assertArrayEquals(data.getBytes(), bytes);
895 
896 		// Test with null
897 		var bytes2 = readBytes((InputStream)null, 10);
898 		assertArrayEquals(new byte[0], bytes2);
899 	}
900 
901 	//====================================================================================================
902 	// readBytes(Reader)
903 	//====================================================================================================
904 	@Test
905 	void a042_readBytes_Reader() throws IOException {
906 		var data = "Hello World";
907 		var r = new StringReader(data);
908 		var bytes = readBytes(r);
909 		assertArrayEquals(data.getBytes(), bytes);
910 
911 		// Test with null
912 		var bytes2 = readBytes((Reader)null);
913 		assertArrayEquals(new byte[0], bytes2);
914 	}
915 
916 	//====================================================================================================
917 	// toBufferedReader(Reader)
918 	//====================================================================================================
919 	@Test
920 	void a043_toBufferedReader() {
921 		var r = new StringReader("test");
922 		var br = toBufferedReader(r);
923 		assertSame(r, br); // StringReader should be returned as-is
924 
925 		// Test with BufferedReader
926 		var br2 = new BufferedReader(new StringReader("test"));
927 		var br3 = toBufferedReader(br2);
928 		assertSame(br2, br3);
929 
930 		// Test with other Reader
931 		var fr = new StringReader("test");
932 		var br4 = toBufferedReader(fr);
933 		assertSame(fr, br4); // StringReader should be returned as-is
934 
935 		// Test with FileReader (not StringReader or BufferedReader)
936 		// We can't easily test this without creating a file, but we can test the logic
937 		// Test with null
938 		assertNull(toBufferedReader(null));
939 	}
940 
941 	//====================================================================================================
942 	// EMPTY_INPUT_STREAM and EMPTY_READER tests
943 	//====================================================================================================
944 	@Test
945 	void a044_emptyInputStream() throws IOException {
946 		// Test line 44: EMPTY_INPUT_STREAM.read()
947 		assertEquals(-1, EMPTY_INPUT_STREAM.read());
948 	}
949 
950 	@Test
951 	void a045_emptyReader() throws IOException {
952 		// Test line 65: EMPTY_READER.close()
953 		EMPTY_READER.close(); // Should not throw
954 
955 		// Test line 69: EMPTY_READER.read()
956 		assertEquals(-1, EMPTY_READER.read());
957 
958 		// Test line 74: EMPTY_READER.read(char[], int, int)
959 		var buf = new char[10];
960 		assertEquals(-1, EMPTY_READER.read(buf, 0, 10));
961 	}
962 
963 	//====================================================================================================
964 	// Test helper classes
965 	//====================================================================================================
966 	public static class TestReader extends StringReader {
967 		boolean closed;
968 
969 		public TestReader(String s) {
970 			super(s);
971 		}
972 
973 		@Override /* Reader */
974 		public void close() {
975 			closed = true;
976 		}
977 	}
978 
979 	public static class TestWriter extends StringWriter {
980 		boolean closed;
981 
982 		public TestWriter() { /* no-op */ }
983 
984 		@Override /* Writer */
985 		public void close() {
986 			closed = true;
987 		}
988 	}
989 
990 	public static class TestInputStream extends ByteArrayInputStream {
991 		boolean closed;
992 
993 		public TestInputStream(String s) {
994 			super(s.getBytes());
995 		}
996 
997 		@Override /* InputStream */
998 		public void close() throws IOException {
999 			super.close();
1000 			closed = true;
1001 		}
1002 	}
1003 
1004 	public static class TestOutputStream extends ByteArrayOutputStream {
1005 		boolean closed;
1006 
1007 		public TestOutputStream() { /* no-op */ }
1008 
1009 		@Override /* OutputStream */
1010 		public void close() throws IOException {
1011 			super.close();
1012 			closed = true;
1013 		}
1014 
1015 		@Override /* Overridden from Object */
1016 		public synchronized String toString() {
1017 			return new String(this.toByteArray(), UTF8);
1018 		}
1019 	}
1020 
1021 	public static class ThrowingTestInputStream extends InputStream {
1022 		private final byte[] data;
1023 		private int pos = 0;
1024 
1025 		public ThrowingTestInputStream(String s) {
1026 			this.data = s.getBytes();
1027 		}
1028 
1029 		@Override
1030 		public int read() throws IOException {
1031 			if (pos >= data.length)
1032 				return -1;
1033 			return data[pos++] & 0xFF;
1034 		}
1035 
1036 		@Override
1037 		public int read(byte[] b, int off, int len) throws IOException {
1038 			throw new IOException("Test exception");
1039 		}
1040 	}
1041 
1042 	public static class ThrowingTestOutputStream extends OutputStream {
1043 		private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
1044 
1045 		@Override
1046 		public void write(int b) throws IOException {
1047 			baos.write(b);
1048 		}
1049 
1050 		@Override
1051 		public void flush() throws IOException {
1052 			throw new IOException("Test exception");
1053 		}
1054 
1055 		@Override
1056 		public void close() throws IOException {
1057 			throw new IOException("Test exception");
1058 		}
1059 	}
1060 
1061 	public static class ThrowingTestReader extends Reader {
1062 		public ThrowingTestReader() {
1063 		}
1064 
1065 		@Override
1066 		public int read(char[] cbuf, int off, int len) throws IOException {
1067 			throw new IOException("Test exception");
1068 		}
1069 
1070 		@Override
1071 		public void close() throws IOException {
1072 			throw new IOException("Test exception");
1073 		}
1074 	}
1075 
1076 	public static class ThrowingTestWriter extends Writer {
1077 		private final StringWriter sw = new StringWriter();
1078 
1079 		@Override
1080 		public void write(char[] cbuf, int off, int len) throws IOException {
1081 			sw.write(cbuf, off, len);
1082 		}
1083 
1084 		@Override
1085 		public void flush() throws IOException {
1086 			throw new IOException("Test exception");
1087 		}
1088 
1089 		@Override
1090 		public void close() throws IOException {
1091 			throw new IOException("Test exception");
1092 		}
1093 	}
1094 }
1095