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