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  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.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.io.ByteArrayInputStream;
26  import java.nio.charset.StandardCharsets;
27  import java.util.concurrent.atomic.AtomicBoolean;
28  
29  import org.apache.commons.io.IOUtils;
30  import org.apache.commons.lang3.mutable.MutableInt;
31  import org.junit.jupiter.api.Test;
32  import org.junit.jupiter.params.ParameterizedTest;
33  import org.junit.jupiter.params.provider.ValueSource;
34  
35  /**
36   * Tests for {@link BoundedInputStream}.
37   */
38  public class BoundedInputStreamTest {
39  
40      private void compare(final String message, final byte[] expected, final byte[] actual) {
41          assertEquals(expected.length, actual.length, () -> message + " (array length equals check)");
42          final MutableInt mi = new MutableInt();
43          for (int i = 0; i < expected.length; i++) {
44              mi.setValue(i);
45              assertEquals(expected[i], actual[i], () -> message + " byte[" + mi + "]");
46          }
47      }
48  
49      @Test
50      public void testBuilderGet() {
51          // java.lang.IllegalStateException: origin == null
52          assertThrows(IllegalStateException.class, () -> BoundedInputStream.builder().get());
53      }
54  
55      @SuppressWarnings("deprecation")
56      @ParameterizedTest
57      @ValueSource(longs = { -100, -1, 0, 1, 2, 4, 8, 16, 32, 64 })
58      public void testCounts(final long startCount) throws Exception {
59  
60          final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
61          final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
62          final long actualStart = startCount < 0 ? 0 : startCount;
63  
64          // limit = length
65          try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setCount(startCount)
66                  .setMaxCount(helloWorld.length).get()) {
67              assertTrue(bounded.markSupported());
68              assertEquals(helloWorld.length, bounded.getMaxCount());
69              assertEquals(helloWorld.length, bounded.getMaxLength());
70              assertEquals(actualStart, bounded.getCount());
71              assertEquals(Math.max(0, bounded.getMaxCount() - actualStart), bounded.getRemaining());
72              assertEquals(Math.max(0, bounded.getMaxLength() - actualStart), bounded.getRemaining());
73              int readCount = 0;
74              for (int i = 0; i < helloWorld.length; i++) {
75                  final byte expectedCh = bounded.getRemaining() > 0 ? helloWorld[i] : EOF;
76                  final int actualCh = bounded.read();
77                  assertEquals(expectedCh, actualCh, "limit = length byte[" + i + "]");
78                  if (actualCh != EOF) {
79                      readCount++;
80                  }
81                  assertEquals(helloWorld.length, bounded.getMaxCount());
82                  assertEquals(helloWorld.length, bounded.getMaxLength());
83                  assertEquals(actualStart + readCount, bounded.getCount(), "i=" + i);
84                  assertEquals(Math.max(0, bounded.getMaxCount() - (readCount + actualStart)), bounded.getRemaining());
85                  assertEquals(Math.max(0, bounded.getMaxLength() - (readCount + actualStart)), bounded.getRemaining());
86              }
87              assertEquals(-1, bounded.read(), "limit = length end");
88              assertEquals(helloWorld.length, bounded.getMaxLength());
89              assertEquals(readCount + actualStart, bounded.getCount());
90              assertEquals(0, bounded.getRemaining());
91              assertEquals(0, bounded.available());
92              // should be invariant
93              assertTrue(bounded.markSupported());
94          }
95          // limit > length
96          final int maxCountP1 = helloWorld.length + 1;
97          try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setCount(startCount)
98                  .setMaxCount(maxCountP1).get()) {
99              assertTrue(bounded.markSupported());
100             assertEquals(maxCountP1, bounded.getMaxLength());
101             assertEquals(actualStart, bounded.getCount());
102             assertEquals(Math.max(0, bounded.getMaxCount() - actualStart), bounded.getRemaining());
103             assertEquals(Math.max(0, bounded.getMaxLength() - actualStart), bounded.getRemaining());
104             int readCount = 0;
105             for (int i = 0; i < helloWorld.length; i++) {
106                 final byte expectedCh = bounded.getRemaining() > 0 ? helloWorld[i] : EOF;
107                 final int actualCh = bounded.read();
108                 assertEquals(expectedCh, actualCh, "limit = length byte[" + i + "]");
109                 if (actualCh != EOF) {
110                     readCount++;
111                 }
112                 assertEquals(maxCountP1, bounded.getMaxCount());
113                 assertEquals(maxCountP1, bounded.getMaxLength());
114                 assertEquals(actualStart + readCount, bounded.getCount(), "i=" + i);
115                 assertEquals(Math.max(0, bounded.getMaxCount() - (readCount + actualStart)), bounded.getRemaining());
116                 assertEquals(Math.max(0, bounded.getMaxLength() - (readCount + actualStart)), bounded.getRemaining());
117             }
118             assertEquals(-1, bounded.read(), "limit > length end");
119             assertEquals(0, bounded.available());
120             assertEquals(maxCountP1, bounded.getMaxLength());
121             assertEquals(readCount + actualStart, bounded.getCount());
122             assertEquals(Math.max(0, maxCountP1 - bounded.getCount()), bounded.getRemaining());
123             // should be invariant
124             assertTrue(bounded.markSupported());
125         }
126         // limit < length
127         try (BoundedInputStream bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), hello.length)) {
128             assertTrue(bounded.markSupported());
129             assertEquals(hello.length, bounded.getMaxLength());
130             assertEquals(0, bounded.getCount());
131             assertEquals(bounded.getMaxLength(), bounded.getRemaining());
132             int readCount = 0;
133             for (int i = 0; i < hello.length; i++) {
134                 assertEquals(hello[i], bounded.read(), "limit < length byte[" + i + "]");
135                 readCount++;
136                 assertEquals(hello.length, bounded.getMaxLength());
137                 assertEquals(readCount, bounded.getCount());
138                 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
139             }
140             assertEquals(-1, bounded.read(), "limit < length end");
141             assertEquals(0, bounded.available());
142             assertEquals(hello.length, bounded.getMaxLength());
143             assertEquals(readCount, bounded.getCount());
144             assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
145             // should be invariant
146             assertTrue(bounded.markSupported());
147         }
148     }
149 
150     @Test
151     public void testMarkReset() throws Exception {
152         final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
153         final int helloWorldLen = helloWorld.length;
154         final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
155         final byte[] world = " World".getBytes(StandardCharsets.UTF_8);
156         final int helloLen = hello.length;
157         // limit = -1
158         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).get()) {
159             assertTrue(bounded.markSupported());
160             bounded.mark(0);
161             compare("limit = -1", helloWorld, IOUtils.toByteArray(bounded));
162             // should be invariant
163             assertTrue(bounded.markSupported());
164             // again
165             bounded.reset();
166             compare("limit = -1", hello, IOUtils.toByteArray(bounded, helloLen));
167             bounded.mark(helloWorldLen);
168             compare("limit = -1", world, IOUtils.toByteArray(bounded));
169             bounded.reset();
170             compare("limit = -1", world, IOUtils.toByteArray(bounded));
171             // should be invariant
172             assertTrue(bounded.markSupported());
173         }
174         // limit = 0
175         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(0).get()) {
176             assertTrue(bounded.markSupported());
177             bounded.mark(0);
178             compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
179             // should be invariant
180             assertTrue(bounded.markSupported());
181             // again
182             bounded.reset();
183             compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
184             bounded.mark(helloWorldLen);
185             compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
186             // should be invariant
187             assertTrue(bounded.markSupported());
188         }
189         // limit = length
190         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
191                 .setMaxCount(helloWorld.length).get()) {
192             assertTrue(bounded.markSupported());
193             bounded.mark(0);
194             compare("limit = length", helloWorld, IOUtils.toByteArray(bounded));
195             // should be invariant
196             assertTrue(bounded.markSupported());
197             // again
198             bounded.reset();
199             compare("limit = length", hello, IOUtils.toByteArray(bounded, helloLen));
200             bounded.mark(helloWorldLen);
201             compare("limit = length", world, IOUtils.toByteArray(bounded));
202             bounded.reset();
203             compare("limit = length", world, IOUtils.toByteArray(bounded));
204             // should be invariant
205             assertTrue(bounded.markSupported());
206         }
207         // limit > length
208         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
209                 .setMaxCount(helloWorld.length + 1).get()) {
210             assertTrue(bounded.markSupported());
211             bounded.mark(0);
212             compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
213             // should be invariant
214             assertTrue(bounded.markSupported());
215             // again
216             bounded.reset();
217             compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
218             bounded.reset();
219             compare("limit > length", hello, IOUtils.toByteArray(bounded, helloLen));
220             bounded.mark(helloWorldLen);
221             compare("limit > length", world, IOUtils.toByteArray(bounded));
222             bounded.reset();
223             compare("limit > length", world, IOUtils.toByteArray(bounded));
224             // should be invariant
225             assertTrue(bounded.markSupported());
226         }
227         // limit < length
228         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
229                 .setMaxCount(helloWorld.length - (hello.length + 1)).get()) {
230             assertTrue(bounded.markSupported());
231             bounded.mark(0);
232             compare("limit < length", hello, IOUtils.toByteArray(bounded));
233             // should be invariant
234             assertTrue(bounded.markSupported());
235             // again
236             bounded.reset();
237             compare("limit < length", hello, IOUtils.toByteArray(bounded));
238             bounded.reset();
239             compare("limit < length", hello, IOUtils.toByteArray(bounded, helloLen));
240             bounded.mark(helloWorldLen);
241             compare("limit < length", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
242             bounded.reset();
243             compare("limit < length", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
244             // should be invariant
245             assertTrue(bounded.markSupported());
246         }
247     }
248 
249     @SuppressWarnings("deprecation")
250     @Test
251     public void testOnMaxLength() throws Exception {
252         final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
253         final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
254         final AtomicBoolean boolRef = new AtomicBoolean();
255         // limit = length
256         try (BoundedInputStream bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), helloWorld.length) {
257             @Override
258             protected void onMaxLength(final long max, final long readCount) {
259                 boolRef.set(true);
260             }
261         }) {
262             assertTrue(bounded.markSupported());
263             assertEquals(helloWorld.length, bounded.getMaxCount());
264             assertEquals(helloWorld.length, bounded.getMaxLength());
265             assertEquals(0, bounded.getCount());
266             assertEquals(bounded.getMaxCount(), bounded.getRemaining());
267             assertEquals(bounded.getMaxLength(), bounded.getRemaining());
268             assertFalse(boolRef.get());
269             int readCount = 0;
270             for (int i = 0; i < helloWorld.length; i++) {
271                 assertEquals(helloWorld[i], bounded.read(), "limit = length byte[" + i + "]");
272                 readCount++;
273                 assertEquals(helloWorld.length, bounded.getMaxCount());
274                 assertEquals(helloWorld.length, bounded.getMaxLength());
275                 assertEquals(readCount, bounded.getCount());
276                 assertEquals(bounded.getMaxCount() - readCount, bounded.getRemaining());
277                 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
278             }
279             assertEquals(-1, bounded.read(), "limit = length end");
280             assertEquals(0, bounded.available());
281             assertEquals(helloWorld.length, bounded.getMaxLength());
282             assertEquals(readCount, bounded.getCount());
283             assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
284             assertTrue(boolRef.get());
285             // should be invariant
286             assertTrue(bounded.markSupported());
287         }
288         // limit > length
289         boolRef.set(false);
290         final int length2 = helloWorld.length + 1;
291         try (BoundedInputStream bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), length2) {
292             @Override
293             protected void onMaxLength(final long max, final long readCount) {
294                 boolRef.set(true);
295             }
296         }) {
297             assertTrue(bounded.markSupported());
298             assertEquals(length2, bounded.getMaxLength());
299             assertEquals(0, bounded.getCount());
300             assertEquals(bounded.getMaxLength(), bounded.getRemaining());
301             assertFalse(boolRef.get());
302             int readCount = 0;
303             for (int i = 0; i < helloWorld.length; i++) {
304                 assertEquals(helloWorld[i], bounded.read(), "limit > length byte[" + i + "]");
305                 readCount++;
306                 assertEquals(length2, bounded.getMaxLength());
307                 assertEquals(readCount, bounded.getCount());
308                 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
309             }
310             assertEquals(0, bounded.available());
311             assertEquals(-1, bounded.read(), "limit > length end");
312             assertEquals(length2, bounded.getMaxLength());
313             assertEquals(readCount, bounded.getCount());
314             assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
315             assertFalse(boolRef.get());
316             // should be invariant
317             assertTrue(bounded.markSupported());
318         }
319         // limit < length
320         boolRef.set(false);
321         try (BoundedInputStream bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), hello.length) {
322             @Override
323             protected void onMaxLength(final long max, final long readCount) {
324                 boolRef.set(true);
325             }
326         }) {
327             assertTrue(bounded.markSupported());
328             assertEquals(hello.length, bounded.getMaxLength());
329             assertEquals(0, bounded.getCount());
330             assertEquals(bounded.getMaxLength(), bounded.getRemaining());
331             assertFalse(boolRef.get());
332             int readCount = 0;
333             for (int i = 0; i < hello.length; i++) {
334                 assertEquals(hello[i], bounded.read(), "limit < length byte[" + i + "]");
335                 readCount++;
336                 assertEquals(hello.length, bounded.getMaxLength());
337                 assertEquals(readCount, bounded.getCount());
338                 assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
339             }
340             assertEquals(-1, bounded.read(), "limit < length end");
341             assertEquals(hello.length, bounded.getMaxLength());
342             assertEquals(readCount, bounded.getCount());
343             assertEquals(bounded.getMaxLength() - readCount, bounded.getRemaining());
344             assertTrue(boolRef.get());
345             // should be invariant
346             assertTrue(bounded.markSupported());
347         }
348     }
349 
350     @Test
351     public void testReadArray() throws Exception {
352         final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
353         final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
354         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).get()) {
355             assertTrue(bounded.markSupported());
356             compare("limit = -1", helloWorld, IOUtils.toByteArray(bounded));
357             // should be invariant
358             assertTrue(bounded.markSupported());
359         }
360         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(0).get()) {
361             assertTrue(bounded.markSupported());
362             compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
363             // should be invariant
364             assertTrue(bounded.markSupported());
365         }
366         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
367                 .setMaxCount(helloWorld.length).get()) {
368             assertTrue(bounded.markSupported());
369             compare("limit = length", helloWorld, IOUtils.toByteArray(bounded));
370             // should be invariant
371             assertTrue(bounded.markSupported());
372         }
373         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
374                 .setMaxCount(helloWorld.length + 1).get()) {
375             assertTrue(bounded.markSupported());
376             compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
377             // should be invariant
378             assertTrue(bounded.markSupported());
379         }
380         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
381                 .setMaxCount(helloWorld.length - 6).get()) {
382             assertTrue(bounded.markSupported());
383             compare("limit < length", hello, IOUtils.toByteArray(bounded));
384             // should be invariant
385             assertTrue(bounded.markSupported());
386         }
387     }
388 
389     @SuppressWarnings("deprecation")
390     @Test
391     public void testReadSingle() throws Exception {
392         final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
393         final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
394         // limit = length
395         try (BoundedInputStream bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), helloWorld.length)) {
396             assertTrue(bounded.markSupported());
397             for (int i = 0; i < helloWorld.length; i++) {
398                 assertEquals(helloWorld[i], bounded.read(), "limit = length byte[" + i + "]");
399             }
400             assertEquals(-1, bounded.read(), "limit = length end");
401             // should be invariant
402             assertTrue(bounded.markSupported());
403         }
404         // limit > length
405         try (BoundedInputStream bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), helloWorld.length + 1)) {
406             assertTrue(bounded.markSupported());
407             for (int i = 0; i < helloWorld.length; i++) {
408                 assertEquals(helloWorld[i], bounded.read(), "limit > length byte[" + i + "]");
409             }
410             assertEquals(-1, bounded.read(), "limit > length end");
411             // should be invariant
412             assertTrue(bounded.markSupported());
413         }
414         // limit < length
415         try (BoundedInputStream bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), hello.length)) {
416             assertTrue(bounded.markSupported());
417             for (int i = 0; i < hello.length; i++) {
418                 assertEquals(hello[i], bounded.read(), "limit < length byte[" + i + "]");
419             }
420             assertEquals(-1, bounded.read(), "limit < length end");
421             // should be invariant
422             assertTrue(bounded.markSupported());
423         }
424     }
425 
426     @Test
427     public void testReset() throws Exception {
428         final byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
429         final byte[] hello = "Hello".getBytes(StandardCharsets.UTF_8);
430         // limit = -1
431         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).get()) {
432             assertTrue(bounded.markSupported());
433             bounded.reset();
434             compare("limit = -1", helloWorld, IOUtils.toByteArray(bounded));
435             // should be invariant
436             assertTrue(bounded.markSupported());
437             // again
438             bounded.reset();
439             compare("limit = -1", helloWorld, IOUtils.toByteArray(bounded));
440             // should be invariant
441             assertTrue(bounded.markSupported());
442         }
443         // limit = 0
444         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld)).setMaxCount(0).get()) {
445             assertTrue(bounded.markSupported());
446             bounded.reset();
447             compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
448             // should be invariant
449             assertTrue(bounded.markSupported());
450             // again
451             bounded.reset();
452             compare("limit = 0", IOUtils.EMPTY_BYTE_ARRAY, IOUtils.toByteArray(bounded));
453             // should be invariant
454             assertTrue(bounded.markSupported());
455         }
456         // limit = length
457         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
458                 .setMaxCount(helloWorld.length).get()) {
459             assertTrue(bounded.markSupported());
460             bounded.reset();
461             compare("limit = length", helloWorld, IOUtils.toByteArray(bounded));
462             // should be invariant
463             assertTrue(bounded.markSupported());
464             // again
465             bounded.reset();
466             compare("limit = length", helloWorld, IOUtils.toByteArray(bounded));
467             // should be invariant
468             assertTrue(bounded.markSupported());
469         }
470         // limit > length
471         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
472                 .setMaxCount(helloWorld.length + 1).get()) {
473             assertTrue(bounded.markSupported());
474             bounded.reset();
475             compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
476             // should be invariant
477             assertTrue(bounded.markSupported());
478             // again
479             bounded.reset();
480             compare("limit > length", helloWorld, IOUtils.toByteArray(bounded));
481             // should be invariant
482             assertTrue(bounded.markSupported());
483         }
484         // limit < length
485         try (BoundedInputStream bounded = BoundedInputStream.builder().setInputStream(new ByteArrayInputStream(helloWorld))
486                 .setMaxCount(helloWorld.length - 6).get()) {
487             assertTrue(bounded.markSupported());
488             bounded.reset();
489             compare("limit < length", hello, IOUtils.toByteArray(bounded));
490             // should be invariant
491             assertTrue(bounded.markSupported());
492             // again
493             bounded.reset();
494             compare("limit < length", hello, IOUtils.toByteArray(bounded));
495             // should be invariant
496             assertTrue(bounded.markSupported());
497         }
498     }
499 }