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.vfs2.provider.ram;
18  
19  import static org.junit.Assert.assertNotSame;
20  import static org.junit.Assert.assertSame;
21  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  import static org.junit.jupiter.api.Assertions.fail;
26  
27  import java.io.Closeable;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.OutputStream;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.List;
34  
35  import org.apache.commons.io.IOUtils;
36  import org.apache.commons.vfs2.AllFileSelector;
37  import org.apache.commons.vfs2.FileContent;
38  import org.apache.commons.vfs2.FileObject;
39  import org.apache.commons.vfs2.FileSystemException;
40  import org.apache.commons.vfs2.FileSystemOptions;
41  import org.apache.commons.vfs2.FileType;
42  import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
43  import org.apache.commons.vfs2.provider.UriParser;
44  import org.junit.jupiter.api.AfterEach;
45  import org.junit.jupiter.api.BeforeEach;
46  import org.junit.jupiter.api.Test;
47  
48  /**
49   * Custom tests for RamProvider.
50   */
51  public class CustomRamProviderTest {
52  
53      private static final byte[] NON_EMPTY_FILE_CONTENT = { 1, 2, 3 };
54  
55      /** List of URL special characters encoded for AbstractFileObject#getChild */
56      final char[] ENC = { /*'#',*/ '!', '?'};
57  
58      private final List<Closeable> closeables = new ArrayList<>();
59  
60      FileSystemOptions defaultRamFso = new FileSystemOptions();
61  
62      DefaultFileSystemManager manager;
63  
64      FileSystemOptions smallSizedFso = new FileSystemOptions();
65  
66      FileSystemOptions zeroSizedFso = new FileSystemOptions();
67  
68      /**
69       * Closes the given {@link Closeable} during the tearDown phase.
70       */
71      private <C extends Closeable> C closeOnTearDown(final C closeable) {
72          closeables.add(closeable);
73          return closeable;
74      }
75  
76      private InputStream createEmptyFile() throws FileSystemException, IOException {
77          final FileObject root = manager.resolveFile("ram://file");
78          root.createFile();
79          return this.closeOnTearDown(root.getContent().getInputStream());
80      }
81  
82      private InputStream createNonEmptyFile() throws FileSystemException, IOException {
83          final FileObject root = manager.resolveFile("ram://file");
84          root.createFile();
85  
86          final FileContent content = root.getContent();
87          final OutputStream output = this.closeOnTearDown(content.getOutputStream());
88          output.write(1);
89          output.write(2);
90          output.write(3);
91          output.flush();
92          output.close();
93  
94          return this.closeOnTearDown(content.getInputStream());
95      }
96  
97      /** Create directory structure for {@link #testSpecialName()} and {@link #testSchemePrefix()} */
98      private FileObject prepareSpecialFile(final String dirName, final String testFileName) throws FileSystemException
99      {
100         // set up a folder containing a file name with special characters:
101         final FileObject dir = manager.resolveFile("ram:" + dirName);
102         dir.createFolder();
103         // construct the absolute name to make sure the relative name is not miss-interpreted
104         // ("./" + UriParser.encode(testFileName, ENC) would also work)
105         final String filePath = dir.getName().getPath() + "/" + UriParser.encode(testFileName, ENC);
106 
107         final FileObject specialFile = dir.resolveFile(filePath);
108         specialFile.createFile();
109 
110         return dir;
111     }
112 
113     @BeforeEach
114     public void setUp() throws Exception {
115         manager = new DefaultFileSystemManager();
116         manager.addProvider("ram", new RamFileProvider());
117         manager.init();
118 
119         // File Systems Options
120         RamFileSystemConfigBuilder.getInstance().setMaxSize(zeroSizedFso, 0L);
121         RamFileSystemConfigBuilder.getInstance().setMaxSize(smallSizedFso, 10L);
122     }
123 
124     @AfterEach
125     public void tearDown() throws IOException {
126         IOUtils.closeQuietly(closeables);
127         if (manager != null) {
128             manager.close();
129         }
130     }
131 
132     @Test
133     public void testFSOptions() throws Exception {
134         // Default FS
135         final FileObject fo1 = manager.resolveFile("ram:/");
136         final FileObject fo2 = manager.resolveFile("ram:/");
137         assertSame("Both files should exist in the same fs instance.", fo1.getFileSystem(), fo2.getFileSystem());
138 
139         FileSystemOptions fsOptions = fo1.getFileSystem().getFileSystemOptions();
140         long maxFilesystemSize = RamFileSystemConfigBuilder.getInstance().getLongMaxSize(fsOptions);
141         assertEquals(Long.MAX_VALUE, maxFilesystemSize, "Filesystem option maxSize must be unlimited");
142 
143         // Small FS
144         final FileObject fo3 = manager.resolveFile("ram:/fo3", smallSizedFso);
145         final FileObject fo4 = manager.resolveFile("ram:/", smallSizedFso);
146         assertSame("Both files should exist in the same FileSystem instance.", fo3.getFileSystem(), fo4.getFileSystem());
147         assertNotSame("Both files should exist in different FileSystem instance.", fo1.getFileSystem(), fo3.getFileSystem());
148 
149         fsOptions = fo3.getFileSystem().getFileSystemOptions();
150         maxFilesystemSize = RamFileSystemConfigBuilder.getInstance().getLongMaxSize(fsOptions);
151         assertEquals(10, maxFilesystemSize, "Filesystem option maxSize must be set");
152     }
153 
154     /**
155      * Tests VFS-625.
156      * @throws FileSystemException
157      */
158     @Test
159     public void testMoveFile() throws FileSystemException {
160         final FileObject fileSource = manager.resolveFile("ram://virtual/source");
161         fileSource.createFile();
162         final FileObject fileDest = manager.resolveFile("ram://virtual/dest");
163         assertTrue(fileSource.canRenameTo(fileDest));
164         fileSource.moveTo(fileDest);
165     }
166 
167     @Test
168     public void testReadEmptyFileByteByByte() throws FileSystemException, IOException {
169         final InputStream input = createEmptyFile();
170         assertEquals(-1, input.read(),"Empty file didn't return EOF -1");
171     }
172 
173     @Test
174     public void testReadEmptyFileIntoBuffer() throws FileSystemException, IOException {
175         final InputStream input = createEmptyFile();
176 
177         final byte[] buffer = new byte[100];
178         assertEquals(-1, input.read(buffer), "Empty file didn't return when filling buffer");
179         assertArrayEquals(new byte[100], buffer, "Buffer was written too");
180     }
181 
182     @Test
183     public void testReadEmptyFileIntoBufferWithOffsetAndLength() throws FileSystemException, IOException {
184         final InputStream input = createEmptyFile();
185         final byte[] buffer = new byte[100];
186         assertEquals(-1, input.read(buffer, 10, 90), "Empty file didn't return when filling buffer");
187         assertArrayEquals(new byte[100], buffer, "Buffer was written too");
188     }
189 
190     @Test
191     public void testReadNonEmptyFileByteByByte() throws FileSystemException, IOException {
192         final InputStream input = createNonEmptyFile();
193 
194         assertEquals(1, input.read(), "Read 1st byte failed");
195         assertEquals(2, input.read(), "Read 2st byte failed");
196         assertEquals(3, input.read(), "Read 3st byte failed");
197         assertEquals(-1, input.read(), "File should be empty");
198     }
199 
200     @Test
201     public void testReadNonEmptyFileIntoBuffer() throws FileSystemException, IOException {
202         final InputStream input = createNonEmptyFile();
203 
204         final byte[] buffer = new byte[100];
205         assertEquals(NON_EMPTY_FILE_CONTENT.length, input.read(buffer), "Filling buffer failed when file is not empty");
206 
207         final byte[] expectedBuffer = Arrays.copyOf(NON_EMPTY_FILE_CONTENT, 100);
208         assertArrayEquals(expectedBuffer, buffer, "Buffer not filled");
209 
210         Arrays.fill(buffer, (byte) 0);
211         Arrays.fill(expectedBuffer, (byte) 0);
212 
213         assertEquals(-1, input.read(buffer), "File should be empty after filling buffer");
214         assertArrayEquals(expectedBuffer, buffer, "Buffer was written when empty");
215     }
216 
217     @Test
218     public void testReadNonEmptyFileIntoBufferWithOffsetAndLength() throws FileSystemException, IOException {
219         final InputStream input = createNonEmptyFile();
220 
221         final byte[] buffer = new byte[100];
222         final int offset = 10;
223         assertEquals(NON_EMPTY_FILE_CONTENT.length,
224                 input.read(buffer, offset, 100 - offset), "Filling buffer failed when file is not empty");
225 
226         final byte[] expectedBuffer = new byte[100];
227         System.arraycopy(NON_EMPTY_FILE_CONTENT, 0, expectedBuffer, offset, NON_EMPTY_FILE_CONTENT.length);
228         assertArrayEquals(expectedBuffer, buffer, "Buffer not filled");
229 
230         Arrays.fill(buffer, (byte) 0);
231         Arrays.fill(expectedBuffer, (byte) 0);
232         assertEquals(-1, input.read(buffer, 10, 90), "File should be empty after filling buffer");
233         assertArrayEquals(expectedBuffer, buffer, "Buffer was written when empty");
234     }
235 
236     /**
237      * Checks root folder exists.
238      *
239      * @throws FileSystemException
240      */
241     @Test
242     public void testRootFolderExists() throws FileSystemException {
243         final FileObject root = manager.resolveFile("ram:///", defaultRamFso);
244         assertTrue(root.getType().hasChildren());
245 
246         try {
247             root.delete();
248             fail();
249         } catch (final FileSystemException e) {
250             // Expected
251         }
252 
253     }
254 
255     /**
256      * Test if listing files with known scheme prefix works.
257      * <p>
258      * This test is not RamProvider specific but it uses it as a simple test-bed.
259      * Verifies VFS-741.
260      * </p>
261      */
262     @Test
263     public void testSchemePrefix() throws FileSystemException
264     {
265         // use a :-prefix with a known scheme (unknown scheme works since VFS-398)
266         final String KNOWN_SCHEME = manager.getSchemes()[0]; // typically "ram"
267 
268         // we test with this file name
269         final String testDir = "/prefixtest/";
270         final String testFileName = KNOWN_SCHEME + ":test:txt";
271         final String expectedName = testDir + testFileName;
272 
273         final FileObject dir = prepareSpecialFile(testDir, testFileName);
274 
275         // verify we can list dir
276 
277         // if not it throws:
278         // Caused by: org.apache.commons.vfs2.FileSystemException: Invalid descendent file name "ram:data:test.txt".
279         //   at org.apache.commons.vfs2.impl.DefaultFileSystemManager.resolveName
280         //   at org.apache.commons.vfs2.provider.AbstractFileObject.getChildren
281         //   at org.apache.commons.vfs2.provider.AbstractFileObject.traverse
282         //   at org.apache.commons.vfs2.provider.AbstractFileObject.findFiles
283 
284         // test methods to get the child:
285         final FileObject[] findFilesResult = dir.findFiles(new AllFileSelector()); // includes dir
286         final FileObject[] getChildrenResult = dir.getChildren();
287         final FileObject getChildResult = dir.getChild(testFileName);
288 
289         // validate findFiles returns expected result
290         assertEquals(2, findFilesResult.length, () -> "Unexpected result findFiles: " + Arrays.toString(findFilesResult));
291         String resultName = findFilesResult[0].getName().getPathDecoded();
292         assertEquals(expectedName, resultName, "findFiles Child name does not match");
293         assertEquals(FileType.FILE, findFilesResult[0].getType(), "Did findFiles but child was no file");
294 
295         // validate getChildren returns expected result
296         assertEquals(1, getChildrenResult.length, () -> "Unexpected result getChildren: " + Arrays.toString(getChildrenResult));
297         resultName = getChildrenResult[0].getName().getPathDecoded();
298         assertEquals(expectedName, resultName, "getChildren Child name does not match");
299         assertEquals(FileType.FILE, getChildrenResult[0].getType(), "Did getChildren but child was no file");
300 
301         // validate getChild returns expected child
302         assertNotNull(getChildResult, "Did not find direct child");
303         resultName = getChildResult.getName().getPathDecoded();
304         assertEquals(expectedName, resultName, "getChild name does not match");
305         assertEquals(FileType.FILE, getChildResult.getType(), "getChild was no file");
306     }
307 
308     @Test
309     public void testSmallFS() throws Exception {
310         // Small FS
311         final FileObject fo3 = manager.resolveFile("ram:/fo3", smallSizedFso);
312         fo3.createFile();
313         try {
314             final OutputStream os = fo3.getContent().getOutputStream();
315             os.write(new byte[10]);
316             os.close();
317         } catch (final FileSystemException e) {
318             fail("Test should be able to save such a small file");
319         }
320 
321         try {
322             final OutputStream os = fo3.getContent().getOutputStream();
323             os.write(new byte[11]);
324             os.close();
325             fail("It shouldn't save such a big file");
326         } catch (final FileSystemException e) {
327             // Expected
328         }
329     }
330 
331     /**
332      * Test some special file name symbols.
333      * <p>
334      * Use the RamProvider since it has no character limitations like
335      * the (Windows) LocalFileProvider.
336      */
337     @Test
338     public void testSpecialName() throws FileSystemException
339     {
340         // we test with this file name
341         // does not work with '!'
342         final String testDir = "/specialtest/";
343         final String testFileName = "test:+-_ \"()<>%#.txt";
344         final String expectedName = testDir + testFileName;
345 
346         final FileObject dir = prepareSpecialFile(testDir, testFileName);
347 
348         // DO: verify you can list it:
349         final FileObject[] findFilesResult = dir.findFiles(new AllFileSelector()); // includes dir
350         final FileObject[] getChildrenResult = dir.getChildren();
351         final FileObject getChildResult = dir.getChild(UriParser.encode(testFileName, ENC));
352 
353         // validate findFiles returns expected result
354         assertEquals(2, findFilesResult.length, () -> "Unexpected result findFiles: " + Arrays.toString(findFilesResult));
355         String resultName = findFilesResult[0].getName().getPathDecoded();
356         assertEquals(expectedName, resultName, "findFiles Child name does not match");
357         assertEquals(FileType.FILE, findFilesResult[0].getType(), "Did findFiles but child was no file");
358 
359         // validate getChildren returns expected result
360         assertEquals(1, getChildrenResult.length, () -> "Unexpected result getChildren: " + Arrays.toString(getChildrenResult));
361         resultName = getChildrenResult[0].getName().getPathDecoded();
362         assertEquals(expectedName, resultName, "getChildren Child name does not match");
363         assertEquals(FileType.FILE, getChildrenResult[0].getType(), "Did getChildren but child was no file");
364 
365         // validate getChild returns expected child
366         assertNotNull(getChildResult, "Did not find direct child");
367         resultName = getChildResult.getName().getPathDecoded();
368         assertEquals(expectedName, resultName, "getChild name does not match");
369         assertEquals(FileType.FILE, getChildResult.getType(), "getChild was no file");
370     }
371 
372 }