1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.io.input;
18
19 import static org.apache.commons.io.IOUtils.EOF;
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertFalse;
22 import static org.junit.jupiter.api.Assertions.assertSame;
23 import static org.junit.jupiter.api.Assertions.assertThrows;
24 import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
25 import static org.junit.jupiter.api.Assertions.assertTrue;
26 import static org.junit.jupiter.api.Assertions.fail;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.verifyNoMoreInteractions;
31 import static org.mockito.Mockito.when;
32
33 import java.io.ByteArrayInputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.nio.charset.StandardCharsets;
37 import java.util.concurrent.atomic.AtomicBoolean;
38 import java.util.stream.Stream;
39
40 import org.apache.commons.io.IOUtils;
41 import org.apache.commons.io.test.CustomIOException;
42 import org.apache.commons.lang3.mutable.MutableInt;
43 import org.junit.jupiter.api.Test;
44 import org.junit.jupiter.params.ParameterizedTest;
45 import org.junit.jupiter.params.provider.Arguments;
46 import org.junit.jupiter.params.provider.MethodSource;
47 import org.junit.jupiter.params.provider.ValueSource;
48
49
50
51
52 class BoundedInputStreamTest {
53
54 static Stream<Arguments> testAvailableAfterClose() throws IOException {
55
56 final InputStream noOpClose = mock(InputStream.class);
57 when(noOpClose.available()).thenReturn(42, 42);
58
59
60 final InputStream returnsZeroAfterClose = mock(InputStream.class);
61 when(returnsZeroAfterClose.available()).thenReturn(42, 0);
62
63
64 final InputStream throwsAfterClose = mock(InputStream.class);
65 when(throwsAfterClose.available()).thenReturn(42).thenThrow(new IOException("Stream closed"));
66
67 return Stream.of(
68 Arguments.of("underlying stream still returns 42 after close", noOpClose, 42),
69 Arguments.of("underlying stream returns 0 after close", returnsZeroAfterClose, 42),
70 Arguments.of("underlying stream throws IOException after close", throwsAfterClose, 42));
71 }
72
73 static Stream<Arguments> testAvailableUpperLimit() {
74 final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
75 return Stream.of(
76
77 Arguments.of(new ByteArrayInputStream(helloWorld), helloWorld.length - 1, helloWorld.length - 1, 0),
78
79 Arguments.of(new ByteArrayInputStream(helloWorld), helloWorld.length + 1, helloWorld.length, 0),
80
81 Arguments.of(
82 new NullInputStream(Long.MAX_VALUE), Long.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE));
83 }
84
85 static Stream<Arguments> testReadAfterClose() throws IOException {
86
87 final InputStream noOpClose = mock(InputStream.class);
88 when(noOpClose.read()).thenReturn(42);
89
90
91 final InputStream returnsEofAfterClose = mock(InputStream.class);
92 when(returnsEofAfterClose.read()).thenReturn(IOUtils.EOF);
93
94
95 final InputStream throwsAfterClose = mock(InputStream.class);
96 final IOException closed = new IOException("Stream closed");
97 when(throwsAfterClose.read()).thenThrow(closed);
98
99 return Stream.of(
100 Arguments.of("underlying stream still reads data after close", noOpClose, 42),
101 Arguments.of("underlying stream returns EOF after close", returnsEofAfterClose, IOUtils.EOF),
102 Arguments.of("underlying stream throws IOException after close", throwsAfterClose, closed));
103 }
104
105 static Stream<Arguments> testRemaining() {
106 return Stream.of(
107
108 Arguments.of("unbounded (EOF constant)", IOUtils.EOF, Long.MAX_VALUE),
109 Arguments.of("unbounded (arbitrary negative)", Long.MIN_VALUE, Long.MAX_VALUE),
110
111
112 Arguments.of("bounded (zero)", 0L, 0L),
113 Arguments.of("bounded (small)", 1024L, 1024L),
114 Arguments.of("bounded (Integer.MAX_VALUE)", Integer.MAX_VALUE, (long) Integer.MAX_VALUE),
115
116
117 Arguments.of("bounded (Long.MAX_VALUE)", Long.MAX_VALUE, Long.MAX_VALUE));
118 }
119
120 private void compare(final String message, final byte[] expected, final byte[] actual) {
121 assertEquals(expected.length, actual.length, () -> message + " (array length equals check)");
122 final MutableInt mi = new MutableInt();
123 for (int i = 0; i < expected.length; i++) {
124 mi.setValue(i);
125 assertEquals(expected[i], actual[i], () -> message + " byte[" + mi + "]");
126 }
127 }
128
129 @Test
130 void testAfterReadConsumer() throws Exception {
131 final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
132 final AtomicBoolean boolRef = new AtomicBoolean();
133
134 try (InputStream bounded = BoundedInputStream.builder()
135 .setInputStream(new ByteArrayInputStream(hello))
136 .setMaxCount(hello.length)
137 .setAfterRead(i -> boolRef.set(true))
138 .get()) {
139 IOUtils.consume(bounded);
140 }
141
142 assertTrue(boolRef.get());
143
144 final String message = "test exception message";
145
146 try (InputStream bounded = BoundedInputStream.builder()
147 .setInputStream(new ByteArrayInputStream(hello))
148 .setMaxCount(hello.length)
149 .setAfterRead(i -> {
150 throw new CustomIOException(message);
151 })
152 .get()) {
153 assertEquals(message, assertThrowsExactly(CustomIOException.class, () -> IOUtils.consume(bounded)).getMessage());
154 }
155
156 }
157
158 @ParameterizedTest(name = "{index} — {0}")
159 @MethodSource
160 void testAvailableAfterClose(final String caseName, final InputStream delegate, final int expectedBeforeClose)
161 throws Exception {
162 final InputStream shadow;
163 try (InputStream in = BoundedInputStream.builder()
164 .setInputStream(delegate)
165 .setPropagateClose(true)
166 .get()) {
167
168 assertEquals(expectedBeforeClose, in.available(), caseName + " (before close)");
169 shadow = in;
170 }
171
172 verify(delegate, times(1)).close();
173
174 assertEquals(0, shadow.available(), caseName + " (after close)");
175
176 verify(delegate, times(1)).available();
177 verifyNoMoreInteractions(delegate);
178 }
179
180 @ParameterizedTest
181 @MethodSource
182 void testAvailableUpperLimit(final InputStream input, final long maxCount, final int expectedBeforeSkip, final int expectedAfterSkip)
183 throws Exception {
184 try (BoundedInputStream bounded = BoundedInputStream.builder()
185 .setInputStream(input)
186 .setMaxCount(maxCount)
187 .get()) {
188 assertEquals(
189 expectedBeforeSkip, bounded.available(), "available should be limited by maxCount and data length");
190 IOUtils.skip(bounded, expectedBeforeSkip);
191 assertEquals(
192 expectedAfterSkip,
193 bounded.available(),
194 "after skipping available should be limited by maxCount and data length");
195 }
196 }
197
198 @Test
199 void testBuilderGet() {
200
201 assertThrows(IllegalStateException.class, () -> BoundedInputStream.builder().get());
202 }
203
204 @Test
205 void testCloseHandleIOException() throws IOException {
206 ProxyInputStreamTest.testCloseHandleIOException(BoundedInputStream.builder());
207 }
208
209 @ParameterizedTest
210 @ValueSource(longs = { -100, -1, 0, 1, 2, 4, 8, 16, 32, 64 })
211 void testCounts(final long startCount) throws Exception {
212 final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
213 final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
214 final long actualStart = startCount < 0 ? 0 : startCount;
215
216 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setCount(startCount)
217 .setMaxCount(helloWorld.length).get()) {
218 assertTrue(bounded.markSupported());
219 assertEquals(helloWorld.length, bounded.getMaxCount());
220 assertEquals(helloWorld.length, bounded.getMaxLength());
221 assertEquals(actualStart, bounded.getCount());
222 assertEquals(Math.max(0, bounded.getMaxCount() - actualStart), bounded.getRemaining());
223 assertEquals(Math.max(0, bounded.getMaxLength() - actualStart), bounded.getRemaining());
224 int readCount = 0;
225 for (int i = 0; i < helloWorld.length; i++) {
226 final byte expectedCh = bounded.getRemaining() > 0 ? helloWorld[i] : EOF;
227 final int actualCh = bounded.read();
228 assertEquals(expectedCh, actualCh, "limit = length byte[" + i + "]");
229 if (actualCh != EOF) {
230 readCount++;
231 }
232 assertEquals(helloWorld.length, bounded.getMaxCount());
233 assertEquals(helloWorld.length, bounded.getMaxLength());
234 assertEquals(actualStart + readCount, bounded.getCount(), "i=" + i);
235 assertEquals(Math.max(0, bounded.getMaxCount() - (readCount + actualStart)), bounded.getRemaining());
236 assertEquals(Math.max(0, bounded.getMaxLength() - (readCount + actualStart)), bounded.getRemaining());
237 }
238 assertEquals(-1, bounded.read(), "limit = length end");
239 assertEquals(helloWorld.length, bounded.getMaxLength());
240 assertEquals(readCount + actualStart, bounded.getCount());
241 assertEquals(0, bounded.getRemaining());
242 assertEquals(0, bounded.available());
243
244 assertTrue(bounded.markSupported());
245 }
246
247 final int maxCountP1 = helloWorld.length + 1;
248 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setCount(startCount)
249 .setMaxCount(maxCountP1).get()) {
250 assertTrue(bounded.markSupported());
251 assertEquals(maxCountP1, bounded.getMaxLength());
252 assertEquals(actualStart, bounded.getCount());
253 assertEquals(Math.max(0, bounded.getMaxCount() - actualStart), bounded.getRemaining());
254 assertEquals(Math.max(0, bounded.getMaxLength() - actualStart), bounded.getRemaining());
255 int readCount = 0;
256 for (int i = 0; i < helloWorld.length; i++) {
257 final byte expectedCh = bounded.getRemaining() > 0 ? helloWorld[i] : EOF;
258 final int actualCh = bounded.read();
259 assertEquals(expectedCh, actualCh, "limit = length byte[" + i + "]");
260 if (actualCh != EOF) {
261 readCount++;
262 }
263 assertEquals(maxCountP1, bounded.getMaxCount());
264 assertEquals(maxCountP1, bounded.getMaxLength());
265 assertEquals(actualStart + readCount, bounded.getCount(), "i=" + i);
266 assertEquals(Math.max(0, bounded.getMaxCount() - (readCount + actualStart)), bounded.getRemaining());
267 assertEquals(Math.max(0, bounded.getMaxLength() - (readCount + actualStart)), bounded.getRemaining());
268 }
269 assertEquals(-1, bounded.read(), "limit > length end");
270 assertEquals(0, bounded.available());
271 assertEquals(maxCountP1, bounded.getMaxLength());
272 assertEquals(readCount + actualStart, bounded.getCount());
273 assertEquals(Math.max(0, maxCountP1 - bounded.getCount()), bounded.getRemaining());
274
275 assertTrue(bounded.markSupported());
276 }
277
278 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(hello.length).get()) {
279 assertTrue(bounded.markSupported());
280 assertEquals(hello.length, bounded.getMaxLength());
281 assertEquals(0, bounded.getCount());
282 assertEquals(bounded.getMaxLength(), bounded.getRemaining());
283 int readCount = 0;
284 for (int i = 0; i < hello.length; i++) {
285 assertEquals(hello[i], bounded.read(), "limit < length byte[" + i + "]");
286 readCount++;
287 assertEquals(hello.length, bounded.getMaxLength());
288 assertEquals(readCount, bounded.getCount());
289 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
290 }
291 assertEquals(-1, bounded.read(), "limit < length end");
292 assertEquals(0, bounded.available());
293 assertEquals(hello.length, bounded.getMaxLength());
294 assertEquals(readCount, bounded.getCount());
295 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
296
297 assertTrue(bounded.markSupported());
298 }
299 }
300
301 @Test
302 void testMarkReset() throws Exception {
303 final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
304 final int helloWorldLen = helloWorld.length;
305 final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
306 final byte[] world = " World".getBytes(StandardCharsets.UTF_8);
307 final int helloLen = hello.length;
308
309 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).get()) {
310 assertTrue(bounded.markSupported());
311 bounded.mark(0);
312 compare("limit = -1", helloWorld, IOUtils.toByteArray(bounded));
313
314 assertTrue(bounded.markSupported());
315
316 bounded.reset();
317 compare("limit = -1", hello, IOUtils.toByteArray(bounded, helloLen));
318 bounded.mark(helloWorldLen);
319 compare("limit = -1", world, IOUtils.toByteArray(bounded));
320 bounded.reset();
321 compare("limit = -1", world, IOUtils.toByteArray(bounded));
322
323 assertTrue(bounded.markSupported());
324 }
325
326 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(0).get()) {
327 assertTrue(bounded.markSupported());
328 bounded.mark(0);
329 compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
330
331 assertTrue(bounded.markSupported());
332
333 bounded.reset();
334 compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
335 bounded.mark(helloWorldLen);
336 compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
337
338 assertTrue(bounded.markSupported());
339 }
340
341 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
342 .setMaxCount(helloWorld.length).get()) {
343 assertTrue(bounded.markSupported());
344 bounded.mark(0);
345 compare("limit = length", helloWorld, IOUtils.toByteArray(bounded));
346
347 assertTrue(bounded.markSupported());
348
349 bounded.reset();
350 compare("limit = length", hello, IOUtils.toByteArray(bounded, helloLen));
351 bounded.mark(helloWorldLen);
352 compare("limit = length", world, IOUtils.toByteArray(bounded));
353 bounded.reset();
354 compare("limit = length", world, IOUtils.toByteArray(bounded));
355
356 assertTrue(bounded.markSupported());
357 }
358
359 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
360 .setMaxCount(helloWorld.length + 1).get()) {
361 assertTrue(bounded.markSupported());
362 bounded.mark(0);
363 compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
364
365 assertTrue(bounded.markSupported());
366
367 bounded.reset();
368 compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
369 bounded.reset();
370 compare("limit > length", hello, IOUtils.toByteArray(bounded, helloLen));
371 bounded.mark(helloWorldLen);
372 compare("limit > length", world, IOUtils.toByteArray(bounded));
373 bounded.reset();
374 compare("limit > length", world, IOUtils.toByteArray(bounded));
375
376 assertTrue(bounded.markSupported());
377 }
378
379 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
380 .setMaxCount(helloWorld.length - (hello.length + 1)).get()) {
381 assertTrue(bounded.markSupported());
382 bounded.mark(0);
383 compare("limit < length", hello, IOUtils.toByteArray(bounded));
384
385 assertTrue(bounded.markSupported());
386
387 bounded.reset();
388 compare("limit < length", hello, IOUtils.toByteArray(bounded));
389 bounded.reset();
390 compare("limit < length", hello, IOUtils.toByteArray(bounded, helloLen));
391 bounded.mark(helloWorldLen);
392 compare("limit < length", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
393 bounded.reset();
394 compare("limit < length", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
395
396 assertTrue(bounded.markSupported());
397 }
398 }
399
400 @Test
401 void testOnMaxCountConsumer() throws Exception {
402 final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
403 final AtomicBoolean boolRef = new AtomicBoolean();
404
405 try (BoundedInputStream bounded = BoundedInputStream.builder()
406 .setInputStream(new ByteArrayInputStream(hello))
407 .setMaxCount(hello.length)
408 .setOnMaxCount(null)
409 .setOnMaxCount((m, c) -> boolRef.set(true))
410 .get()) {
411 IOUtils.consume(bounded);
412 }
413
414 assertTrue(boolRef.get());
415
416 final String message = "test exception message";
417
418 try (BoundedInputStream bounded = BoundedInputStream.builder()
419 .setInputStream(new ByteArrayInputStream(hello))
420 .setMaxCount(hello.length)
421 .setOnMaxCount((m, c) -> {
422 throw new CustomIOException(message);
423 })
424 .get()) {
425 assertEquals(message, assertThrowsExactly(CustomIOException.class, () -> IOUtils.consume(bounded)).getMessage());
426 }
427
428 }
429
430 @SuppressWarnings("deprecation")
431 @Test
432 void testOnMaxLength() throws Exception {
433 final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
434 final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
435 final AtomicBoolean boolRef = new AtomicBoolean();
436
437 try (BoundedInputStream bounded = BoundedInputStream.builder()
438 .setInputStream(new ByteArrayInputStream(helloWorld))
439 .setMaxCount(helloWorld.length)
440 .setOnMaxCount((m, c) -> boolRef.set(true))
441 .get()) {
442 assertTrue(bounded.markSupported());
443 assertEquals(helloWorld.length, bounded.getMaxCount());
444 assertEquals(helloWorld.length, bounded.getMaxLength());
445 assertEquals(0, bounded.getCount());
446 assertEquals(bounded.getMaxCount(), bounded.getRemaining());
447 assertEquals(bounded.getMaxLength(), bounded.getRemaining());
448 assertFalse(boolRef.get());
449 int readCount = 0;
450 for (int i = 0; i < helloWorld.length; i++) {
451 assertEquals(helloWorld[i], bounded.read(), "limit = length byte[" + i + "]");
452 readCount++;
453 assertEquals(helloWorld.length, bounded.getMaxCount());
454 assertEquals(helloWorld.length, bounded.getMaxLength());
455 assertEquals(readCount, bounded.getCount());
456 assertEquals(bounded.getMaxCount() - readCount, bounded.getRemaining());
457 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
458 }
459 assertEquals(-1, bounded.read(), "limit = length end");
460 assertEquals(0, bounded.available());
461 assertEquals(helloWorld.length, bounded.getMaxLength());
462 assertEquals(readCount, bounded.getCount());
463 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
464 assertTrue(boolRef.get());
465
466 assertTrue(bounded.markSupported());
467 }
468
469 boolRef.set(false);
470 final int length2 = helloWorld.length + 1;
471 try (BoundedInputStream bounded = BoundedInputStream.builder()
472 .setInputStream(new ByteArrayInputStream(helloWorld))
473 .setMaxCount(length2)
474 .setOnMaxCount((m, c) -> boolRef.set(true))
475 .get()) {
476 assertTrue(bounded.markSupported());
477 assertEquals(length2, bounded.getMaxLength());
478 assertEquals(0, bounded.getCount());
479 assertEquals(bounded.getMaxLength(), bounded.getRemaining());
480 assertFalse(boolRef.get());
481 int readCount = 0;
482 for (int i = 0; i < helloWorld.length; i++) {
483 assertEquals(helloWorld[i], bounded.read(), "limit > length byte[" + i + "]");
484 readCount++;
485 assertEquals(length2, bounded.getMaxLength());
486 assertEquals(readCount, bounded.getCount());
487 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
488 }
489 assertEquals(0, bounded.available());
490 assertEquals(-1, bounded.read(), "limit > length end");
491 assertEquals(length2, bounded.getMaxLength());
492 assertEquals(readCount, bounded.getCount());
493 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
494 assertFalse(boolRef.get());
495
496 assertTrue(bounded.markSupported());
497 }
498
499 boolRef.set(false);
500 try (BoundedInputStream bounded = BoundedInputStream.builder()
501 .setInputStream(new ByteArrayInputStream(helloWorld))
502 .setMaxCount(hello.length)
503 .setOnMaxCount((m, c) -> boolRef.set(true))
504 .get()) {
505 assertTrue(bounded.markSupported());
506 assertEquals(hello.length, bounded.getMaxLength());
507 assertEquals(0, bounded.getCount());
508 assertEquals(bounded.getMaxLength(), bounded.getRemaining());
509 assertFalse(boolRef.get());
510 int readCount = 0;
511 for (int i = 0; i < hello.length; i++) {
512 assertEquals(hello[i], bounded.read(), "limit < length byte[" + i + "]");
513 readCount++;
514 assertEquals(hello.length, bounded.getMaxLength());
515 assertEquals(readCount, bounded.getCount());
516 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
517 }
518 assertEquals(-1, bounded.read(), "limit < length end");
519 assertEquals(hello.length, bounded.getMaxLength());
520 assertEquals(readCount, bounded.getCount());
521 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
522 assertTrue(boolRef.get());
523
524 assertTrue(bounded.markSupported());
525 }
526 }
527
528 @SuppressWarnings("deprecation")
529 @Test
530 void testPublicConstructors() throws IOException {
531 final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
532 try (ByteArrayInputStream baos = new ByteArrayInputStream(helloWorld);
533 BoundedInputStream inputStream = new BoundedInputStream(baos)) {
534 assertSame(baos, inputStream.unwrap());
535 }
536 final long maxCount = 2;
537 try (ByteArrayInputStream baos = new ByteArrayInputStream(helloWorld);
538 BoundedInputStream inputStream = new BoundedInputStream(baos, maxCount)) {
539 assertSame(baos, inputStream.unwrap());
540 assertSame(maxCount, inputStream.getMaxCount());
541 }
542 }
543
544 @ParameterizedTest(name = "{index} — {0}")
545 @MethodSource("testReadAfterClose")
546 void testReadAfterClose(
547 final String caseName,
548 final InputStream delegate,
549 final Object expectedAfterClose
550 ) throws Exception {
551
552 final InputStream bounded;
553 try (InputStream in = BoundedInputStream.builder()
554 .setInputStream(delegate)
555 .setPropagateClose(true)
556 .get()) {
557 bounded = in;
558 }
559
560
561 verify(delegate, times(1)).close();
562
563 if (expectedAfterClose instanceof Integer) {
564 assertEquals(expectedAfterClose, bounded.read(), caseName + " (after close)");
565 } else if (expectedAfterClose instanceof IOException) {
566 final IOException actual = assertThrows(IOException.class, bounded::read, caseName + " (after close)");
567
568 assertSame(expectedAfterClose, actual, caseName + " (exception instance)");
569 } else {
570 fail("Unexpected expectedAfterClose type: " + expectedAfterClose);
571 }
572
573
574 verify(delegate, times(1)).read();
575 verifyNoMoreInteractions(delegate);
576 }
577
578 @Test
579 void testReadArray() throws Exception {
580 final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
581 final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
582 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).get()) {
583 assertTrue(bounded.markSupported());
584 compare("limit = -1", helloWorld, IOUtils.toByteArray(bounded));
585
586 assertTrue(bounded.markSupported());
587 }
588 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(0).get()) {
589 assertTrue(bounded.markSupported());
590 compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
591
592 assertTrue(bounded.markSupported());
593 }
594 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
595 .setMaxCount(helloWorld.length).get()) {
596 assertTrue(bounded.markSupported());
597 compare("limit = length", helloWorld, IOUtils.toByteArray(bounded));
598
599 assertTrue(bounded.markSupported());
600 }
601 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
602 .setMaxCount(helloWorld.length + 1).get()) {
603 assertTrue(bounded.markSupported());
604 compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
605
606 assertTrue(bounded.markSupported());
607 }
608 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
609 .setMaxCount(helloWorld.length - 6).get()) {
610 assertTrue(bounded.markSupported());
611 compare("limit < length", hello, IOUtils.toByteArray(bounded));
612
613 assertTrue(bounded.markSupported());
614 }
615 }
616
617 @Test
618 void testReadSingle() throws Exception {
619 final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
620 final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
621
622 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(helloWorld.length)
623 .get()) {
624 assertTrue(bounded.markSupported());
625 for (int i = 0; i < helloWorld.length; i++) {
626 assertEquals(helloWorld[i], bounded.read(), "limit = length byte[" + i + "]");
627 }
628 assertEquals(-1, bounded.read(), "limit = length end");
629
630 assertTrue(bounded.markSupported());
631 }
632
633 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(helloWorld.length + 1)
634 .get()) {
635 assertTrue(bounded.markSupported());
636 for (int i = 0; i < helloWorld.length; i++) {
637 assertEquals(helloWorld[i], bounded.read(), "limit > length byte[" + i + "]");
638 }
639 assertEquals(-1, bounded.read(), "limit > length end");
640
641 assertTrue(bounded.markSupported());
642 }
643
644 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(hello.length).get()) {
645 assertTrue(bounded.markSupported());
646 for (int i = 0; i < hello.length; i++) {
647 assertEquals(hello[i], bounded.read(), "limit < length byte[" + i + "]");
648 }
649 assertEquals(-1, bounded.read(), "limit < length end");
650
651 assertTrue(bounded.markSupported());
652 }
653 }
654
655 @ParameterizedTest(name = "{index}: {0} -> initial remaining {2}")
656 @MethodSource
657 void testRemaining(final String caseName, final long maxCount, final long expectedInitialRemaining)
658 throws Exception {
659 final byte[] data = "Hello World".getBytes(StandardCharsets.UTF_8);
660
661 try (BoundedInputStream in = BoundedInputStream.builder()
662 .setByteArray(data)
663 .setMaxCount(maxCount)
664 .get()) {
665
666 assertEquals(expectedInitialRemaining, in.getRemaining(), caseName + " (initial)");
667
668
669 final long skipped = IOUtils.skip(in, 42);
670
671
672
673 final long expectedAfterSkip =
674 in.getMaxCount() == IOUtils.EOF ? expectedInitialRemaining : expectedInitialRemaining - skipped;
675
676 assertEquals(expectedAfterSkip, in.getRemaining(), caseName + " (after skip)");
677 }
678 }
679
680 @Test
681 void testReset() throws Exception {
682 final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
683 final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
684
685 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).get()) {
686 assertTrue(bounded.markSupported());
687 bounded.reset();
688 compare("limit = -1", helloWorld, IOUtils.toByteArray(bounded));
689
690 assertTrue(bounded.markSupported());
691
692 bounded.reset();
693 compare("limit = -1", helloWorld, IOUtils.toByteArray(bounded));
694
695 assertTrue(bounded.markSupported());
696 }
697
698 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(0).get()) {
699 assertTrue(bounded.markSupported());
700 bounded.reset();
701 compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
702
703 assertTrue(bounded.markSupported());
704
705 bounded.reset();
706 compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
707
708 assertTrue(bounded.markSupported());
709 }
710
711 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
712 .setMaxCount(helloWorld.length).get()) {
713 assertTrue(bounded.markSupported());
714 bounded.reset();
715 compare("limit = length", helloWorld, IOUtils.toByteArray(bounded));
716
717 assertTrue(bounded.markSupported());
718
719 bounded.reset();
720 compare("limit = length", helloWorld, IOUtils.toByteArray(bounded));
721
722 assertTrue(bounded.markSupported());
723 }
724
725 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
726 .setMaxCount(helloWorld.length + 1).get()) {
727 assertTrue(bounded.markSupported());
728 bounded.reset();
729 compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
730
731 assertTrue(bounded.markSupported());
732
733 bounded.reset();
734 compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
735
736 assertTrue(bounded.markSupported());
737 }
738
739 try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
740 .setMaxCount(helloWorld.length - 6).get()) {
741 assertTrue(bounded.markSupported());
742 bounded.reset();
743 compare("limit < length", hello, IOUtils.toByteArray(bounded));
744
745 assertTrue(bounded.markSupported());
746
747 bounded.reset();
748 compare("limit < length", hello, IOUtils.toByteArray(bounded));
749
750 assertTrue(bounded.markSupported());
751 }
752 }
753 }