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    *      https://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.lang3.ArrayUtils.EMPTY_BYTE_ARRAY;
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.nio.file.Files;
27  import java.nio.file.Path;
28  import java.util.Arrays;
29  
30  import org.apache.commons.io.IOUtils;
31  import org.apache.commons.lang3.RandomUtils;
32  import org.junit.jupiter.api.AfterEach;
33  import org.junit.jupiter.api.Test;
34  import org.junit.jupiter.api.io.TempDir;
35  import org.junit.jupiter.params.ParameterizedTest;
36  import org.junit.jupiter.params.provider.MethodSource;
37  import org.junit.jupiter.params.provider.ValueSource;
38  
39  /**
40   * Tests {@link MemoryMappedFileInputStream}.
41   */
42  class MemoryMappedFileInputStreamTest {
43  
44      @TempDir
45      Path tempDir;
46  
47      @AfterEach
48      public void afterEach() {
49          // Ask to run the garbage collector to clean up memory mapped buffers,
50          // otherwise the temporary files won't be able to be removed when running on
51          // Windows. Calling gc() is just a hint to the VM.
52          System.gc();
53          Thread.yield();
54          System.runFinalization();
55          Thread.yield();
56          System.gc();
57          Thread.yield();
58          System.runFinalization();
59          Thread.yield();
60      }
61  
62      private Path createTestFile(final int size) throws IOException {
63          return Files.write(Files.createTempFile(tempDir, null, null), RandomUtils.insecure().randomBytes(size));
64      }
65  
66      private MemoryMappedFileInputStream newInputStream(final Path file) throws IOException {
67          return MemoryMappedFileInputStream.builder().setPath(file).get();
68      }
69  
70      private MemoryMappedFileInputStream newInputStream(final Path file, final int bufferSize) throws IOException {
71          return MemoryMappedFileInputStream.builder().setPath(file).setBufferSize(bufferSize).get();
72      }
73  
74      @Test
75      void testAlternateBufferSize() throws IOException {
76          // setup
77          final Path file = createTestFile(1024 * 1024);
78          final byte[] expectedData = Files.readAllBytes(file);
79          // test
80          try (InputStream inputStream = newInputStream(file, 1024)) {
81              // verify
82              assertArrayEquals(expectedData, IOUtils.toByteArray(inputStream));
83          }
84      }
85  
86      @SuppressWarnings("resource")
87      @ParameterizedTest
88      @MethodSource(AbstractInputStreamTest.ARRAY_LENGTHS_NAME)
89      void testAvailableAfterClose(final int len) throws Exception {
90          final Path file = createTestFile(len);
91          final InputStream shadow;
92          try (InputStream inputStream = newInputStream(file, 1024)) {
93              // verify
94              assertEquals(0, inputStream.available());
95              shadow = inputStream;
96          }
97          assertEquals(0, shadow.available());
98      }
99  
100     @ParameterizedTest
101     @MethodSource(AbstractInputStreamTest.ARRAY_LENGTHS_NAME)
102     void testAvailableAfterOpen(final int len) throws Exception {
103         final Path file = createTestFile(len);
104         try (InputStream inputStream = newInputStream(file, 1024)) {
105             // verify
106             assertEquals(0, inputStream.available());
107             inputStream.read();
108             assertEquals(Math.max(len - 1, 0), inputStream.available());
109             IOUtils.toByteArray(inputStream);
110             assertEquals(0, inputStream.available());
111         }
112     }
113 
114     @Test
115     void testEmptyFile() throws IOException {
116         // setup
117         final Path file = createTestFile(0);
118         // test
119         try (InputStream inputStream = newInputStream(file)) {
120             // verify
121             assertArrayEquals(EMPTY_BYTE_ARRAY, IOUtils.toByteArray(inputStream));
122         }
123     }
124 
125     @Test
126     void testLargerFile() throws IOException {
127         // setup
128         final Path file = createTestFile(1024 * 1024);
129         final byte[] expectedData = Files.readAllBytes(file);
130         // test
131         try (InputStream inputStream = newInputStream(file)) {
132             // verify
133             assertArrayEquals(expectedData, IOUtils.toByteArray(inputStream));
134         }
135     }
136 
137     @Test
138     void testReadAfterClose() throws IOException {
139         // setup
140         final Path file = createTestFile(1 * 1024 * 1024);
141         // test
142         try (InputStream inputStream = newInputStream(file, 1024)) {
143             inputStream.close();
144             // verify
145             assertThrows(IOException.class, () -> IOUtils.toByteArray(inputStream));
146         }
147     }
148 
149     @ParameterizedTest
150     @MethodSource(AbstractInputStreamTest.ARRAY_LENGTHS_NAME)
151     void testReadAfterClose(final int len) throws Exception {
152         final Path file = createTestFile(len);
153         try (InputStream inputStream = newInputStream(file, 1024)) {
154             inputStream.close();
155             assertThrows(IOException.class, inputStream::read);
156         }
157     }
158 
159     @Test
160     void testReadSingleByte() throws IOException {
161         // setup
162         final Path file = createTestFile(2);
163         final byte[] expectedData = Files.readAllBytes(file);
164         // test
165         try (InputStream inputStream = newInputStream(file, 1024)) {
166             final int b1 = inputStream.read();
167             final int b2 = inputStream.read();
168             assertEquals(-1, inputStream.read());
169             // verify
170             assertArrayEquals(expectedData, new byte[] {(byte) b1, (byte) b2});
171         }
172     }
173 
174     @Test
175     void testSkipAtStart() throws IOException {
176         // setup
177         final Path file = createTestFile(100);
178         final byte[] expectedData = Files.readAllBytes(file);
179         // test
180         try (InputStream inputStream = newInputStream(file, 10)) {
181             assertEquals(1, inputStream.skip(1));
182             final byte[] data = IOUtils.toByteArray(inputStream);
183             // verify
184             assertArrayEquals(Arrays.copyOfRange(expectedData, 1, expectedData.length), data);
185         }
186     }
187 
188     @Test
189     void testSkipEmpty() throws IOException {
190         // setup
191         final Path file = createTestFile(0);
192         // test
193         try (InputStream inputStream = newInputStream(file)) {
194             assertEquals(0, inputStream.skip(5));
195             // verify
196             assertArrayEquals(EMPTY_BYTE_ARRAY, IOUtils.toByteArray(inputStream));
197         }
198     }
199 
200     @Test
201     void testSkipInCurrentBuffer() throws IOException {
202         // setup
203         final Path file = createTestFile(100);
204         final byte[] expectedData = Files.readAllBytes(file);
205         // test
206         try (InputStream inputStream = newInputStream(file, 10)) {
207             IOUtils.toByteArray(inputStream, 5);
208             assertEquals(3, inputStream.skip(3));
209             final byte[] data = IOUtils.toByteArray(inputStream);
210             // verify
211             assertArrayEquals(Arrays.copyOfRange(expectedData, 8, expectedData.length), data);
212         }
213     }
214 
215     @ParameterizedTest
216     @ValueSource(ints = {-5, -1, 0})
217     void testSkipNoop(final int amountToSkip) throws IOException {
218         // setup
219         final Path file = createTestFile(10);
220         final byte[] expectedData = Files.readAllBytes(file);
221         // test
222         try (InputStream inputStream = newInputStream(file)) {
223             assertEquals(0, inputStream.skip(amountToSkip));
224             // verify
225             assertArrayEquals(expectedData, IOUtils.toByteArray(inputStream));
226         }
227     }
228 
229     @Test
230     void testSkipOutOfCurrentBuffer() throws IOException {
231         // setup
232         final Path file = createTestFile(100);
233         final byte[] expectedData = Files.readAllBytes(file);
234         // test
235         try (InputStream inputStream = newInputStream(file, 10)) {
236             IOUtils.toByteArray(inputStream, 5);
237             assertEquals(6, inputStream.skip(6));
238             final byte[] data = IOUtils.toByteArray(inputStream);
239             // verify
240             assertArrayEquals(Arrays.copyOfRange(expectedData, 11, expectedData.length), data);
241         }
242     }
243 
244     @Test
245     void testSkipPastEof() throws IOException {
246         // setup
247         final Path file = createTestFile(100);
248         // test
249         try (InputStream inputStream = newInputStream(file, 10)) {
250             IOUtils.toByteArray(inputStream, 5);
251             assertEquals(95, inputStream.skip(96));
252             // verify
253             assertArrayEquals(EMPTY_BYTE_ARRAY, IOUtils.toByteArray(inputStream));
254         }
255     }
256 
257     @Test
258     void testSkipToEndOfCurrentBuffer() throws IOException {
259         // setup
260         final Path file = createTestFile(100);
261         final byte[] expectedData = Files.readAllBytes(file);
262 
263         // test
264         try (InputStream inputStream = newInputStream(file, 10)) {
265             IOUtils.toByteArray(inputStream, 5);
266             assertEquals(5, inputStream.skip(5));
267             final byte[] data = IOUtils.toByteArray(inputStream);
268             // verify
269             assertArrayEquals(Arrays.copyOfRange(expectedData, 10, expectedData.length), data);
270         }
271     }
272 
273     @Test
274     void testSkipToEndOfCurrentBufferBuilder() throws IOException {
275         // setup
276         final Path file = createTestFile(100);
277         final byte[] expectedData = Files.readAllBytes(file);
278         // test
279         try (MemoryMappedFileInputStream inputStream = MemoryMappedFileInputStream.builder().setPath(file).setBufferSize(10).get()) {
280             assertEquals(10, inputStream.getBufferSize());
281             IOUtils.toByteArray(inputStream, 5);
282             assertEquals(5, inputStream.skip(5));
283             final byte[] data = IOUtils.toByteArray(inputStream);
284             // verify
285             assertArrayEquals(Arrays.copyOfRange(expectedData, 10, expectedData.length), data);
286         }
287     }
288 
289     @Test
290     void testSmallFileBuilder() throws IOException {
291         // setup
292         final Path file = createTestFile(100);
293         final byte[] expectedData = Files.readAllBytes(file);
294         // test
295         try (InputStream inputStream = MemoryMappedFileInputStream.builder().setFile(file.toFile()).get()) {
296             // verify
297             assertArrayEquals(expectedData, IOUtils.toByteArray(inputStream));
298         }
299     }
300 
301     @Test
302     void testSmallPath() throws IOException {
303         // setup
304         final Path file = createTestFile(100);
305         final byte[] expectedData = Files.readAllBytes(file);
306 
307         // test
308         try (InputStream inputStream = newInputStream(file)) {
309             // verify
310             assertArrayEquals(expectedData, IOUtils.toByteArray(inputStream));
311         }
312     }
313 
314     @Test
315     void testSmallPathBuilder() throws IOException {
316         // setup
317         final Path file = createTestFile(100);
318         final byte[] expectedData = Files.readAllBytes(file);
319         // test
320         try (InputStream inputStream = MemoryMappedFileInputStream.builder().setPath(file).get()) {
321             // verify
322             assertArrayEquals(expectedData, IOUtils.toByteArray(inputStream));
323         }
324     }
325 
326 }