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