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.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.io.EOFException;
26  import java.io.IOException;
27  import java.io.InputStream;
28  
29  import org.junit.jupiter.api.Test;
30  import org.junit.jupiter.params.ParameterizedTest;
31  import org.junit.jupiter.params.provider.MethodSource;
32  import org.junit.platform.commons.util.StringUtils;
33  
34  /**
35   * Tests {@link NullInputStream}.
36   */
37  public class NullInputStreamTest {
38  
39      private static final class TestNullInputStream extends NullInputStream {
40  
41          TestNullInputStream(final int size) {
42              super(size);
43          }
44  
45          TestNullInputStream(final int size, final boolean markSupported, final boolean throwEofException) {
46              super(size, markSupported, throwEofException);
47          }
48  
49          @Override
50          protected int processByte() {
51              return (int) getPosition() - 1;
52          }
53  
54          @Override
55          protected void processBytes(final byte[] bytes, final int offset, final int length) {
56              final int startPos = (int) getPosition() - length;
57              for (int i = offset; i < length; i++) {
58                  bytes[i] = (byte) (startPos + i);
59              }
60          }
61  
62      }
63  
64      /** Use the same message as in java.io.InputStream.reset() in OpenJDK 8.0.275-1. */
65      private static final String MARK_RESET_NOT_SUPPORTED = "mark/reset not supported";
66  
67      @SuppressWarnings("resource")
68      @ParameterizedTest
69      @MethodSource(AbstractInputStreamTest.ARRAY_LENGTHS_NAME)
70      public void testAvailableAfterClose(final int len) throws Exception {
71          final InputStream shadow;
72          try (InputStream in = new TestNullInputStream(len, false, false)) {
73              assertEquals(len, in.available());
74              shadow = in;
75          }
76          assertEquals(0, shadow.available());
77      }
78  
79      @ParameterizedTest
80      @MethodSource(AbstractInputStreamTest.ARRAY_LENGTHS_NAME)
81      public void testAvailableAfterOpen(final int len) throws Exception {
82          try (InputStream in = new TestNullInputStream(len, false, false)) {
83              assertEquals(len, in.available());
84          }
85      }
86  
87      @SuppressWarnings("deprecation")
88      @Test
89      public void testDeprecatedSingleton() throws Exception {
90          assertNotNull(NullInputStream.INSTANCE);
91      }
92  
93      @Test
94      public void testEOFException() throws Exception {
95          try (InputStream input = new TestNullInputStream(2, false, true)) {
96              assertEquals(0, input.read(), "Read 1");
97              assertEquals(1, input.read(), "Read 2");
98              assertThrows(EOFException.class, () -> input.read());
99          }
100     }
101 
102     @Test
103     public void testMarkAndReset() throws Exception {
104         int position = 0;
105         final int readLimit = 10;
106         try (InputStream input = new TestNullInputStream(100, true, false)) {
107 
108             assertTrue(input.markSupported(), "Mark Should be Supported");
109 
110             // No Mark
111             final IOException noMarkException = assertThrows(IOException.class, input::reset);
112             assertEquals("No position has been marked", noMarkException.getMessage(), "No Mark IOException message");
113 
114             for (; position < 3; position++) {
115                 assertEquals(position, input.read(), "Read Before Mark [" + position + "]");
116             }
117 
118             // Mark
119             input.mark(readLimit);
120 
121             // Read further
122             for (int i = 0; i < 3; i++) {
123                 assertEquals(position + i, input.read(), "Read After Mark [" + i + "]");
124             }
125 
126             // Reset
127             input.reset();
128 
129             // Read From marked position
130             for (int i = 0; i < readLimit + 1; i++) {
131                 assertEquals(position + i, input.read(), "Read After Reset [" + i + "]");
132             }
133 
134             // Reset after read limit passed
135             final IOException resetException = assertThrows(IOException.class, input::reset, "Read limit exceeded, expected IOException");
136             assertEquals("Marked position [" + position + "] is no longer valid - passed the read limit [" + readLimit + "]", resetException.getMessage(),
137                     "Read limit IOException message");
138         }
139     }
140 
141     @Test
142     public void testMarkNotSupported() throws Exception {
143         try (InputStream input = new TestNullInputStream(100, false, true)) {
144             assertFalse(input.markSupported(), "Mark Should NOT be Supported");
145 
146             final UnsupportedOperationException markException = assertThrows(UnsupportedOperationException.class, () -> input.mark(5));
147             assertEquals(MARK_RESET_NOT_SUPPORTED, markException.getMessage(), "mark() error message");
148 
149             final UnsupportedOperationException resetException = assertThrows(UnsupportedOperationException.class, input::reset);
150             assertEquals(MARK_RESET_NOT_SUPPORTED, resetException.getMessage(), "reset() error message");
151         }
152     }
153 
154     @Test
155     public void testRead() throws Exception {
156         final int size = 5;
157         try (InputStream input = new TestNullInputStream(size)) {
158             for (int i = 0; i < size; i++) {
159                 assertEquals(size - i, input.available(), "Check Size [" + i + "]");
160                 assertEquals(i, input.read(), "Check Value [" + i + "]");
161             }
162             assertEquals(0, input.available(), "Available after contents all read");
163 
164             // Check available is zero after End of file
165             assertEquals(-1, input.read(), "End of File");
166             assertEquals(0, input.available(), "Available after End of File");
167 
168             // Test reading after the end of file
169             assertEquals(-1, input.read(), "End of File");
170 
171             // Close
172             input.close();
173             assertEquals(0, input.available(), "Available after close");
174         }
175     }
176 
177     @Test
178     public void testReadAfterClose() throws Exception {
179         try (InputStream in = new NullInputStream()) {
180             assertEquals(0, in.available());
181             in.close();
182             assertThrows(IOException.class, in::read);
183         }
184     }
185 
186     @ParameterizedTest
187     @MethodSource(AbstractInputStreamTest.ARRAY_LENGTHS_NAME)
188     public void testReadAfterClose(final int len) throws Exception {
189         try (InputStream in = new TestNullInputStream(len, false, false)) {
190             assertEquals(len, in.available());
191             in.close();
192             assertThrows(IOException.class, in::read);
193         }
194     }
195 
196     @Test
197     public void testReadByteArray() throws Exception {
198         final byte[] bytes = new byte[10];
199         try (NullInputStream input = new TestNullInputStream(15)) {
200 
201             // Read into array
202             final int count1 = input.read(bytes);
203             assertEquals(bytes.length, count1, "Read 1");
204             for (int i = 0; i < count1; i++) {
205                 assertEquals(i, bytes[i], "Check Bytes 1");
206             }
207 
208             // Read into array
209             final int count2 = input.read(bytes);
210             assertEquals(5, count2, "Read 2");
211             for (int i = 0; i < count2; i++) {
212                 assertEquals(count1 + i, bytes[i], "Check Bytes 2");
213             }
214 
215             // End of File
216             final int count3 = input.read(bytes);
217             assertEquals(-1, count3, "Read 3 (EOF)");
218 
219             // Test reading after the end of file
220             final int count4 = input.read(bytes);
221             assertEquals(-1, count4, "Read 4 (EOF)");
222 
223             // re-init
224             input.init();
225 
226             // Read into array using offset & length
227             final int offset = 2;
228             final int len = 4;
229             final int count5 = input.read(bytes, offset, len);
230             assertEquals(len, count5, "Read 5");
231             for (int i = offset; i < len; i++) {
232                 assertEquals(i, bytes[i], "Check Bytes 2");
233             }
234         }
235     }
236 
237     @Test
238     public void testReadByteArrayAfterClose() throws Exception {
239         try (InputStream in = new NullInputStream()) {
240             assertEquals(0, in.available());
241             in.close();
242             assertEquals(0, in.read(new byte[0]));
243             assertThrows(IOException.class, () -> in.read(new byte[2]));
244         }
245     }
246 
247     @Test
248     public void testReadByteArrayIntIntAfterClose() throws Exception {
249         try (InputStream in = new NullInputStream()) {
250             assertEquals(0, in.available());
251             in.close();
252             assertEquals(0, in.read(new byte[0], 0, 1));
253             assertEquals(0, in.read(new byte[1], 0, 0));
254             assertThrows(IOException.class, () -> in.read(new byte[2], 0, 1));
255         }
256     }
257 
258     @Test
259     public void testReadByteArrayThrowAtEof() throws Exception {
260         final byte[] bytes = new byte[10];
261         try (NullInputStream input = new TestNullInputStream(15, true, true)) {
262 
263             // Read into array
264             final int count1 = input.read(bytes);
265             assertEquals(bytes.length, count1, "Read 1");
266             for (int i = 0; i < count1; i++) {
267                 assertEquals(i, bytes[i], "Check Bytes 1");
268             }
269 
270             // Read into array
271             final int count2 = input.read(bytes);
272             assertEquals(5, count2, "Read 2");
273             for (int i = 0; i < count2; i++) {
274                 assertEquals(count1 + i, bytes[i], "Check Bytes 2");
275             }
276 
277             // End of File
278             final IOException e1 = assertThrows(EOFException.class, () -> input.read(bytes));
279             assertTrue(StringUtils.isNotBlank(e1.getMessage()));
280 
281             // Test reading after the end of file
282             final IOException e2 = assertThrows(EOFException.class, () -> input.read(bytes));
283             assertTrue(StringUtils.isNotBlank(e2.getMessage()));
284 
285             // reset by closing
286             input.init();
287 
288             // Read into array using offset & length
289             final int offset = 2;
290             final int len = 4;
291             final int count5 = input.read(bytes, offset, len);
292             assertEquals(len, count5, "Read 5");
293             for (int i = offset; i < len; i++) {
294                 assertEquals(i, bytes[i], "Check Bytes 2");
295             }
296         }
297     }
298 
299     @Test
300     public void testReadThrowAtEof() throws Exception {
301         final int size = 5;
302         try (InputStream input = new TestNullInputStream(size, true, true)) {
303             for (int i = 0; i < size; i++) {
304                 assertEquals(size - i, input.available(), "Check Size [" + i + "]");
305                 assertEquals(i, input.read(), "Check Value [" + i + "]");
306             }
307             assertEquals(0, input.available(), "Available after contents all read");
308 
309             // Check available is zero after End of file
310             final IOException e1 = assertThrows(EOFException.class, input::read);
311             assertTrue(StringUtils.isNotBlank(e1.getMessage()));
312 
313             // Test reading after the end of file
314             final IOException e2 = assertThrows(EOFException.class, input::read);
315             assertTrue(StringUtils.isNotBlank(e2.getMessage()));
316 
317             // Close
318             input.close();
319             assertEquals(0, input.available(), "Available after close");
320         }
321     }
322 
323     @Test
324     public void testSkip() throws Exception {
325         try (InputStream input = new TestNullInputStream(10, true, false)) {
326             assertEquals(0, input.read(), "Read 1");
327             assertEquals(1, input.read(), "Read 2");
328             assertEquals(5, input.skip(5), "Skip 1");
329             assertEquals(7, input.read(), "Read 3");
330             assertEquals(2, input.skip(5), "Skip 2"); // only 2 left to skip
331             assertEquals(-1, input.skip(5), "Skip 3 (EOF)"); // End of file
332             assertEquals(-1, input.skip(5), "Skip 3 (EOF)"); // End of file
333         }
334     }
335 
336     @Test
337     public void testSkipThrowAtEof() throws Exception {
338         try (InputStream input = new TestNullInputStream(10, true, true)) {
339             assertEquals(0, input.read(), "Read 1");
340             assertEquals(1, input.read(), "Read 2");
341             assertEquals(5, input.skip(5), "Skip 1");
342             assertEquals(7, input.read(), "Read 3");
343             assertEquals(2, input.skip(5), "Skip 2"); // only 2 left to skip
344             // End of File
345             final IOException e1 = assertThrows(EOFException.class, () -> input.skip(5), "Skip 3 (EOF)");
346             assertTrue(StringUtils.isNotBlank(e1.getMessage()));
347 
348             final IOException e2 = assertThrows(IOException.class, () -> input.skip(5), "Expected IOException for skipping after end of file");
349             assertTrue(StringUtils.isNotBlank(e2.getMessage()));
350         }
351     }
352 }