xref: /aosp_15_r20/external/apache-commons-io/src/test/java/org/apache/commons/io/function/IOStreamTest.java (revision 0c4d7b72e49a04598d65c566f44504b95342d75a)
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.commons.io.function;
19 
20 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
22 import static org.junit.jupiter.api.Assertions.assertEquals;
23 import static org.junit.jupiter.api.Assertions.assertFalse;
24 import static org.junit.jupiter.api.Assertions.assertNotNull;
25 import static org.junit.jupiter.api.Assertions.assertNull;
26 import static org.junit.jupiter.api.Assertions.assertThrows;
27 import static org.junit.jupiter.api.Assertions.assertTrue;
28 
29 import java.io.IOException;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.concurrent.atomic.AtomicInteger;
33 import java.util.concurrent.atomic.AtomicReference;
34 import java.util.stream.Collectors;
35 import java.util.stream.DoubleStream;
36 import java.util.stream.IntStream;
37 import java.util.stream.LongStream;
38 import java.util.stream.Stream;
39 
40 import org.apache.commons.lang3.JavaVersion;
41 import org.apache.commons.lang3.SystemUtils;
42 import org.junit.jupiter.api.Test;
43 
44 /**
45  * Tests {@link IOStream}.
46  */
47 public class IOStreamTest {
48 
49     private static final boolean AT_LEAST_JAVA_11 = SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_11);
50     private static final boolean AT_LEAST_JAVA_17 = SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_17);
51 
compareAndSetIO(final AtomicReference<String> ref, final String expected, final String update)52     private void compareAndSetIO(final AtomicReference<String> ref, final String expected, final String update) throws IOException {
53         TestUtils.compareAndSetThrowsIO(ref, expected, update);
54     }
55 
compareAndSetRE(final AtomicReference<String> ref, final String expected, final String update)56     private void compareAndSetRE(final AtomicReference<String> ref, final String expected, final String update) {
57         TestUtils.compareAndSetThrowsRE(ref, expected, update);
58     }
59 
ioExceptionOnNull(final Object test)60     private void ioExceptionOnNull(final Object test) throws IOException {
61         if (test == null) {
62             throw new IOException("Unexpected");
63         }
64     }
65 
66     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
67     @Test
testAdapt()68     public void testAdapt() {
69         assertEquals(0, IOStream.adapt((Stream<?>) null).count());
70         assertEquals(0, IOStream.adapt(Stream.empty()).count());
71         assertEquals(1, IOStream.adapt(Stream.of("A")).count());
72     }
73 
74     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
75     @Test
testAllMatch()76     public void testAllMatch() throws IOException {
77         assertThrows(IOException.class, () -> IOStream.of("A", "B").allMatch(TestConstants.THROWING_IO_PREDICATE));
78         assertTrue(IOStream.of("A", "B").allMatch(IOPredicate.alwaysTrue()));
79         assertFalse(IOStream.of("A", "B").allMatch(IOPredicate.alwaysFalse()));
80     }
81 
82     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
83     @Test
testAnyMatch()84     public void testAnyMatch() throws IOException {
85         assertThrows(IOException.class, () -> IOStream.of("A", "B").anyMatch(TestConstants.THROWING_IO_PREDICATE));
86         assertTrue(IOStream.of("A", "B").anyMatch(IOPredicate.alwaysTrue()));
87         assertFalse(IOStream.of("A", "B").anyMatch(IOPredicate.alwaysFalse()));
88     }
89 
90     @Test
testClose()91     public void testClose() {
92         IOStream.of("A", "B").close();
93     }
94 
95     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
96     @Test
testCollectCollectorOfQsuperTAR()97     public void testCollectCollectorOfQsuperTAR() {
98         // TODO IOCollector?
99         IOStream.of("A", "B").collect(Collectors.toList());
100     }
101 
102     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
103     @Test
testCollectSupplierOfRBiConsumerOfRQsuperTBiConsumerOfRR()104     public void testCollectSupplierOfRBiConsumerOfRQsuperTBiConsumerOfRR() throws IOException {
105         // TODO Need an IOCollector?
106         IOStream.of("A", "B").collect(() -> "A", (t, u) -> {
107         }, (t, u) -> {
108         });
109         assertEquals("AB", Stream.of("A", "B").collect(StringBuilder::new, StringBuilder::append, StringBuilder::append).toString());
110         assertEquals("AB", IOStream.of("A", "B").collect(StringBuilder::new, StringBuilder::append, StringBuilder::append).toString());
111         // Exceptions
112         assertThrows(IOException.class, () -> IOStream.of("A", "B").collect(TestUtils.throwingIOSupplier(), (t, u) -> {
113         }, (t, u) -> {
114         }));
115         assertThrows(IOException.class, () -> IOStream.of("A", "B").collect(() -> "A", TestUtils.throwingIOBiConsumer(), (t, u) -> {
116         }));
117     }
118 
119     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
120     @Test
testCount()121     public void testCount() {
122         assertEquals(0, IOStream.of().count());
123         assertEquals(1, IOStream.of("A").count());
124         assertEquals(2, IOStream.of("A", "B").count());
125         assertEquals(3, IOStream.of("A", "B", "C").count());
126         assertEquals(3, IOStream.of("A", "A", "A").count());
127     }
128 
129     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
130     @Test
testDistinct()131     public void testDistinct() {
132         assertEquals(0, IOStream.of().distinct().count());
133         assertEquals(1, IOStream.of("A").distinct().count());
134         assertEquals(2, IOStream.of("A", "B").distinct().count());
135         assertEquals(3, IOStream.of("A", "B", "C").distinct().count());
136         assertEquals(1, IOStream.of("A", "A", "A").distinct().count());
137     }
138 
139     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
140     @Test
testEmpty()141     public void testEmpty() throws IOException {
142         assertEquals(0, Stream.empty().count());
143         assertEquals(0, IOStream.empty().count());
144         IOStream.empty().forEach(TestUtils.throwingIOConsumer());
145     }
146 
147     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
148     @Test
testFilter()149     public void testFilter() throws IOException {
150         IOStream.of("A").filter(TestConstants.THROWING_IO_PREDICATE);
151         // compile vs type
152         assertThrows(IOException.class, () -> IOStream.of("A").filter(TestConstants.THROWING_IO_PREDICATE).count());
153         // compile vs inline lambda
154         assertThrows(IOException.class, () -> IOStream.of("A").filter(e -> {
155             throw new IOException("Failure");
156         }).count());
157     }
158 
159     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
160     @Test
testFindAny()161     public void testFindAny() throws IOException {
162         // compile vs type
163         assertThrows(IOException.class, () -> IOStream.of("A").filter(TestConstants.THROWING_IO_PREDICATE).findAny());
164         // compile vs inline lambda
165         assertThrows(IOException.class, () -> IOStream.of("A").filter(e -> {
166             throw new IOException("Failure");
167         }).findAny());
168 
169         assertTrue(IOStream.of("A", "B").filter(IOPredicate.alwaysTrue()).findAny().isPresent());
170         assertFalse(IOStream.of("A", "B").filter(IOPredicate.alwaysFalse()).findAny().isPresent());
171     }
172 
173     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
174     @Test
testFindFirst()175     public void testFindFirst() throws IOException {
176         // compile vs type
177         assertThrows(IOException.class, () -> IOStream.of("A").filter(TestConstants.THROWING_IO_PREDICATE).findFirst());
178         // compile vs inline lambda
179         assertThrows(IOException.class, () -> IOStream.of("A").filter(e -> {
180             throw new IOException("Failure");
181         }).findAny());
182 
183         assertTrue(IOStream.of("A", "B").filter(IOPredicate.alwaysTrue()).findFirst().isPresent());
184         assertFalse(IOStream.of("A", "B").filter(IOPredicate.alwaysFalse()).findFirst().isPresent());
185     }
186 
187     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
188     @Test
testFlatMap()189     public void testFlatMap() throws IOException {
190         assertEquals(Arrays.asList("A", "B", "C", "D"),
191                 IOStream.of(IOStream.of("A", "B"), IOStream.of("C", "D")).flatMap(IOFunction.identity()).collect(Collectors.toList()));
192     }
193 
194     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
195     @Test
testFlatMapToDouble()196     public void testFlatMapToDouble() throws IOException {
197         assertEquals('A' + 'B', IOStream.of("A", "B").flatMapToDouble(e -> DoubleStream.of(e.charAt(0))).sum());
198     }
199 
200     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
201     @Test
testFlatMapToInt()202     public void testFlatMapToInt() throws IOException {
203         assertEquals('A' + 'B', IOStream.of("A", "B").flatMapToInt(e -> IntStream.of(e.charAt(0))).sum());
204     }
205 
206     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
207     @Test
testFlatMapToLong()208     public void testFlatMapToLong() throws IOException {
209         assertEquals('A' + 'B', IOStream.of("A", "B").flatMapToLong(e -> LongStream.of(e.charAt(0))).sum());
210     }
211 
212     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
213     @Test
testForaAllIOConsumer()214     public void testForaAllIOConsumer() throws IOException {
215         // compile vs type
216         assertThrows(IOException.class, () -> IOStream.of("A").forAll(TestUtils.throwingIOConsumer()));
217         // compile vs inline
218         assertThrows(IOException.class, () -> IOStream.of("A").forAll(e -> {
219             throw new IOException("Failure");
220         }));
221         assertThrows(IOException.class, () -> IOStream.of("A", "B").forAll(TestUtils.throwingIOConsumer()));
222         final StringBuilder sb = new StringBuilder();
223         IOStream.of("A", "B").forAll(sb::append);
224         assertEquals("AB", sb.toString());
225     }
226 
227     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
228     @Test
testForaAllIOConsumerBiFunction()229     public void testForaAllIOConsumerBiFunction() throws IOException {
230         // compile vs type
231         assertThrows(IOException.class, () -> IOStream.of("A").forAll(TestUtils.throwingIOConsumer(), (i, e) -> e));
232         // compile vs inline
233         assertThrows(IOException.class, () -> IOStream.of("A").forAll(e -> {
234             throw new IOException("Failure");
235         }, (i, e) -> e));
236         assertThrows(IOException.class, () -> IOStream.of("A", "B").forAll(TestUtils.throwingIOConsumer(), (i, e) -> e));
237         final StringBuilder sb = new StringBuilder();
238         IOStream.of("A", "B").forAll(sb::append, (i, e) -> e);
239         assertEquals("AB", sb.toString());
240     }
241 
242     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
243     @Test
testForaAllIOConsumerBiFunctionNull()244     public void testForaAllIOConsumerBiFunctionNull() throws IOException {
245         // compile vs type
246         assertDoesNotThrow(() -> IOStream.of("A").forAll(TestUtils.throwingIOConsumer(), null));
247         // compile vs inline
248         assertDoesNotThrow(() -> IOStream.of("A").forAll(e -> {
249             throw new IOException("Failure");
250         }, null));
251         assertDoesNotThrow(() -> IOStream.of("A", "B").forAll(TestUtils.throwingIOConsumer(), null));
252         final StringBuilder sb = new StringBuilder();
253         IOStream.of("A", "B").forAll(sb::append, null);
254         assertEquals("AB", sb.toString());
255     }
256 
257     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
258     @Test
testForEachIOConsumerOfQsuperT()259     public void testForEachIOConsumerOfQsuperT() throws IOException {
260         // compile vs type
261         assertThrows(IOException.class, () -> IOStream.of("A").forEach(TestUtils.throwingIOConsumer()));
262         // compile vs inline
263         assertThrows(IOException.class, () -> IOStream.of("A").forEach(e -> {
264             throw new IOException("Failure");
265         }));
266         assertThrows(IOException.class, () -> IOStream.of("A", "B").forEach(TestUtils.throwingIOConsumer()));
267         final StringBuilder sb = new StringBuilder();
268         IOStream.of("A", "B").forEachOrdered(sb::append);
269         assertEquals("AB", sb.toString());
270     }
271 
272     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
273     @Test
testForEachOrdered()274     public void testForEachOrdered() throws IOException {
275         // compile vs type
276         assertThrows(IOException.class, () -> IOStream.of("A").forEach(TestUtils.throwingIOConsumer()));
277         // compile vs inline
278         assertThrows(IOException.class, () -> IOStream.of("A").forEach(e -> {
279             throw new IOException("Failure");
280         }));
281         assertThrows(IOException.class, () -> IOStream.of("A", "B").forEach(TestUtils.throwingIOConsumer()));
282         final StringBuilder sb = new StringBuilder();
283         IOStream.of("A", "B").forEachOrdered(sb::append);
284         assertEquals("AB", sb.toString());
285     }
286 
287     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
288     @Test
testIsParallel()289     public void testIsParallel() {
290         assertFalse(IOStream.of("A", "B").isParallel());
291     }
292 
293     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
294     @Test
testIterateException()295     public void testIterateException() throws IOException {
296         final IOStream<Long> stream = IOStream.iterate(1L, TestUtils.throwingIOUnaryOperator());
297         final IOIterator<Long> iterator = stream.iterator();
298         assertEquals(1L, iterator.next());
299         assertThrows(IOException.class, () -> iterator.next());
300     }
301 
302     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
303     @Test
testIterateLong()304     public void testIterateLong() throws IOException {
305         final IOStream<Long> stream = IOStream.iterate(1L, i -> i + 1);
306         final IOIterator<Long> iterator = stream.iterator();
307         assertEquals(1L, iterator.next());
308         assertEquals(2L, iterator.next());
309     }
310 
311     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
312     @Test
testIterator()313     public void testIterator() throws IOException {
314         final AtomicInteger ref = new AtomicInteger();
315         IOStream.of("A", "B").iterator().forEachRemaining(e -> ref.incrementAndGet());
316         assertEquals(2, ref.get());
317     }
318 
319     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
320     @Test
testLimit()321     public void testLimit() {
322         assertEquals(1, IOStream.of("A", "B").limit(1).count());
323     }
324 
325     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
326     @Test
testMap()327     public void testMap() throws IOException {
328         assertEquals(Arrays.asList("AC", "BC"), IOStream.of("A", "B").map(e -> e + "C").collect(Collectors.toList()));
329     }
330 
331     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
332     @Test
testMapToDouble()333     public void testMapToDouble() {
334         assertArrayEquals(new double[] { Double.parseDouble("1"), Double.parseDouble("2") }, IOStream.of("1", "2").mapToDouble(Double::parseDouble).toArray());
335     }
336 
337     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
338     @Test
testMapToInt()339     public void testMapToInt() {
340         assertArrayEquals(new int[] { 1, 2 }, IOStream.of("1", "2").mapToInt(Integer::parseInt).toArray());
341     }
342 
343     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
344     @Test
testMapToLong()345     public void testMapToLong() {
346         assertArrayEquals(new long[] { 1L, 2L }, IOStream.of("1", "2").mapToLong(Long::parseLong).toArray());
347     }
348 
349     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
350     @Test
testMax()351     public void testMax() throws IOException {
352         assertEquals("B", IOStream.of("A", "B").max(String::compareTo).get());
353     }
354 
355     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
356     @Test
testMin()357     public void testMin() throws IOException {
358         assertEquals("A", IOStream.of("A", "B").min(String::compareTo).get());
359     }
360 
361     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
362     @Test
testNoneMatch()363     public void testNoneMatch() throws IOException {
364         assertThrows(IOException.class, () -> IOStream.of("A", "B").noneMatch(TestConstants.THROWING_IO_PREDICATE));
365         assertFalse(IOStream.of("A", "B").noneMatch(IOPredicate.alwaysTrue()));
366         assertTrue(IOStream.of("A", "B").noneMatch(IOPredicate.alwaysFalse()));
367     }
368 
369     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
370     @Test
testOfArray()371     public void testOfArray() {
372         assertEquals(0, IOStream.of((String[]) null).count());
373         assertEquals(0, IOStream.of().count());
374         assertEquals(2, IOStream.of("A", "B").count());
375     }
376 
377     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
378     @Test
testOfIterable()379     public void testOfIterable() {
380         assertEquals(0, IOStream.of((Iterable<?>) null).count());
381         assertEquals(0, IOStream.of(Collections.emptyList()).count());
382         assertEquals(0, IOStream.of(Collections.emptySet()).count());
383         assertEquals(0, IOStream.of(Collections.emptySortedSet()).count());
384         assertEquals(1, IOStream.of(Arrays.asList("a")).count());
385         assertEquals(2, IOStream.of(Arrays.asList("a", "b")).count());
386     }
387 
388     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
389     @Test
testOfOne()390     public void testOfOne() {
391         assertEquals(1, IOStream.of("A").count());
392     }
393 
394     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
395     @Test
testOnClose()396     public void testOnClose() throws IOException {
397         assertThrows(IOException.class, () -> IOStream.of("A").onClose(TestConstants.THROWING_IO_RUNNABLE).close());
398         final AtomicReference<String> ref = new AtomicReference<>();
399         IOStream.of("A").onClose(() -> compareAndSetIO(ref, null, "new1")).close();
400         assertEquals("new1", ref.get());
401     }
402 
403     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
404     @Test
testOnCloseMultipleHandlers()405     public void testOnCloseMultipleHandlers() {
406         //
407         final AtomicReference<String> ref = new AtomicReference<>();
408         // Sanity check
409         ref.set(null);
410         final RuntimeException thrownRE = assertThrows(RuntimeException.class, () -> {
411             // @formatter:off
412             final Stream<String> stream = Stream.of("A")
413                 .onClose(() -> compareAndSetRE(ref, null, "new1"))
414                 .onClose(() -> TestConstants.throwRuntimeException("Failure 2"));
415             // @formatter:on
416             stream.close();
417         });
418         assertEquals("new1", ref.get());
419         assertEquals("Failure 2", thrownRE.getMessage());
420         assertEquals(0, thrownRE.getSuppressed().length);
421         // Test
422         ref.set(null);
423         final IOException thrownIO = assertThrows(IOException.class, () -> {
424             // @formatter:off
425             final IOStream<String> stream = IOStream.of("A")
426                 .onClose(() -> compareAndSetIO(ref, null, "new1"))
427                 .onClose(() -> TestConstants.throwIOException("Failure 2"));
428             // @formatter:on
429             stream.close();
430         });
431         assertEquals("new1", ref.get());
432         assertEquals("Failure 2", thrownIO.getMessage());
433         assertEquals(0, thrownIO.getSuppressed().length);
434         //
435         final IOException thrownB = assertThrows(IOException.class, () -> {
436             // @formatter:off
437             final IOStream<String> stream = IOStream.of("A")
438                 .onClose(TestConstants.throwIOException("Failure 1"))
439                 .onClose(TestConstants.throwIOException("Failure 2"));
440             // @formatter:on
441             stream.close();
442         });
443         assertEquals("Failure 1", thrownB.getMessage());
444         assertEquals(0, thrownB.getSuppressed().length);
445     }
446 
447     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
448     @Test
testParallel()449     public void testParallel() {
450         assertEquals(2, IOStream.of("A", "B").parallel().count());
451     }
452 
453     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
454     @Test
testPeek()455     public void testPeek() throws IOException {
456         final AtomicReference<String> ref = new AtomicReference<>();
457         // Stream sanity check
458         assertEquals(1, Stream.of("A").peek(e -> compareAndSetRE(ref, null, e)).count());
459         // TODO Resolve, abstract or document these differences?
460         assertEquals(AT_LEAST_JAVA_11 ? null : "A", ref.get());
461         if (AT_LEAST_JAVA_11) {
462             assertEquals(1, IOStream.of("B").peek(e -> compareAndSetRE(ref, null, e)).count());
463             assertEquals(1, IOStream.of("B").peek(e -> compareAndSetIO(ref, null, e)).count());
464             assertEquals(null, ref.get());
465         } else {
466             // Java 8
467             assertThrows(RuntimeException.class, () -> IOStream.of("B").peek(e -> compareAndSetRE(ref, null, e)).count());
468             assertThrows(IOException.class, () -> IOStream.of("B").peek(e -> compareAndSetIO(ref, null, e)).count());
469             assertEquals("A", ref.get());
470         }
471     }
472 
473     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
474     @Test
testReduceBinaryOperatorOfT()475     public void testReduceBinaryOperatorOfT() throws IOException {
476         assertEquals("AB", IOStream.of("A", "B").reduce((t, u) -> t + u).get());
477         assertEquals(TestConstants.ABS_PATH_A.toRealPath(),
478                 IOStream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B).reduce((t, u) -> t.toRealPath()).get());
479     }
480 
481     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
482     @Test
testReduceTBinaryOperatorOfT()483     public void testReduceTBinaryOperatorOfT() throws IOException {
484         assertEquals("_AB", IOStream.of("A", "B").reduce("_", (t, u) -> t + u));
485         assertEquals(TestConstants.ABS_PATH_A.toRealPath(),
486                 IOStream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B).reduce(TestConstants.ABS_PATH_A, (t, u) -> t.toRealPath()));
487     }
488 
489     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
490     @Test
testReduceUBiFunctionOfUQsuperTUBinaryOperatorOfU()491     public void testReduceUBiFunctionOfUQsuperTUBinaryOperatorOfU() throws IOException {
492         assertEquals("_AB", IOStream.of("A", "B").reduce("_", (t, u) -> t + u, (t, u) -> t + u));
493         assertEquals(TestConstants.ABS_PATH_A.toRealPath(), IOStream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B).reduce(TestConstants.ABS_PATH_A,
494                 (t, u) -> t.toRealPath(), (t, u) -> u.toRealPath()));
495     }
496 
497     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
498     @Test
testSequential()499     public void testSequential() {
500         assertEquals(2, IOStream.of("A", "B").sequential().count());
501     }
502 
503     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
504     @Test
testSkip()505     public void testSkip() throws IOException {
506         final AtomicReference<String> ref = new AtomicReference<>();
507         assertEquals(1, Stream.of("A", "B").skip(1).peek(e -> compareAndSetRE(ref, null, e)).count());
508         // TODO Resolve, abstract or document these differences?
509         assertEquals(AT_LEAST_JAVA_17 ? null : "B", ref.get());
510         if (AT_LEAST_JAVA_17) {
511             assertEquals(1, IOStream.of("C", "D").skip(1).peek(e -> compareAndSetRE(ref, null, e)).count());
512             assertEquals(1, IOStream.of("C", "D").skip(1).peek(e -> compareAndSetIO(ref, null, e)).count());
513             assertNull(ref.get());
514         } else {
515             if (AT_LEAST_JAVA_11) {
516                 assertThrows(RuntimeException.class, () -> IOStream.of("C", "D").skip(1).peek(e -> compareAndSetRE(ref, null, e)).count());
517                 assertThrows(IOException.class, () -> IOStream.of("C", "D").skip(1).peek(e -> compareAndSetIO(ref, null, e)).count());
518             } else {
519                 assertThrows(RuntimeException.class, () -> IOStream.of("C", "D").skip(1).peek(e -> compareAndSetRE(ref, null, e)).count());
520                 assertThrows(IOException.class, () -> IOStream.of("C", "D").skip(1).peek(e -> compareAndSetIO(ref, null, e)).count());
521             }
522             assertEquals("B", ref.get());
523         }
524     }
525 
526     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
527     @Test
testSorted()528     public void testSorted() throws IOException {
529         assertEquals(Arrays.asList("A", "B", "C", "D"), IOStream.of("D", "A", "B", "C").sorted().collect(Collectors.toList()));
530         assertEquals(Arrays.asList("A", "B", "C", "D"), IOStream.of("D", "A", "B", "C").sorted().peek(this::ioExceptionOnNull).collect(Collectors.toList()));
531     }
532 
533     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
534     @Test
testSortedComparatorOfQsuperT()535     public void testSortedComparatorOfQsuperT() throws IOException {
536         assertEquals(Arrays.asList("A", "B", "C", "D"), IOStream.of("D", "A", "B", "C").sorted(String::compareTo).collect(Collectors.toList()));
537         assertEquals(Arrays.asList("A", "B", "C", "D"),
538                 IOStream.of("D", "A", "B", "C").sorted(String::compareTo).peek(this::ioExceptionOnNull).collect(Collectors.toList()));
539     }
540 
541     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
542     @Test
testSpliterator()543     public void testSpliterator() {
544         final AtomicInteger ref = new AtomicInteger();
545         IOStream.of("A", "B").spliterator().forEachRemaining(e -> ref.incrementAndGet());
546         assertEquals(2, ref.get());
547     }
548 
549     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
550     @Test
testToArray()551     public void testToArray() {
552         assertArrayEquals(new String[] { "A", "B" }, IOStream.of("A", "B").toArray());
553     }
554 
555     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
556     @Test
testToArrayIntFunctionOfA()557     public void testToArrayIntFunctionOfA() {
558         assertArrayEquals(new String[] { "A", "B" }, IOStream.of("A", "B").toArray(String[]::new));
559     }
560 
561     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
562     @Test
testUnordered()563     public void testUnordered() {
564         // Sanity check
565         assertArrayEquals(new String[] { "A", "B" }, Stream.of("A", "B").unordered().toArray());
566         // Test
567         assertArrayEquals(new String[] { "A", "B" }, IOStream.of("A", "B").unordered().toArray());
568     }
569 
570     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
571     @Test
testUnwrap()572     public void testUnwrap() {
573         final Stream<String> unwrap = IOStream.of("A", "B").unwrap();
574         assertNotNull(unwrap);
575         assertEquals(2, unwrap.count());
576     }
577 
578 }
579