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