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.vfs.test;
18  
19  import org.apache.commons.vfs.Capability;
20  import org.apache.commons.vfs.FileChangeEvent;
21  import org.apache.commons.vfs.FileListener;
22  import org.apache.commons.vfs.FileObject;
23  import org.apache.commons.vfs.FileSystem;
24  import org.apache.commons.vfs.FileSystemException;
25  import org.apache.commons.vfs.FileType;
26  import org.apache.commons.vfs.Selectors;
27  
28  import java.io.OutputStream;
29  import java.util.ArrayList;
30  import java.util.HashSet;
31  import java.util.Set;
32  
33  /***
34   * File system test that check that a file system can be modified.
35   *
36   * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
37   */
38  public class ProviderWriteTests
39      extends AbstractProviderTestCase
40  {
41      /***
42       * Returns the capabilities required by the tests of this test case.
43       */
44      protected Capability[] getRequiredCaps()
45      {
46          return new Capability[]
47          {
48              Capability.CREATE,
49              Capability.DELETE,
50              Capability.GET_TYPE,
51              Capability.LIST_CHILDREN,
52              Capability.READ_CONTENT,
53              Capability.WRITE_CONTENT
54          };
55      }
56  
57      /***
58       * Sets up a scratch folder for the test to use.
59       */
60      protected FileObject createScratchFolder() throws Exception
61      {
62          FileObject scratchFolder = getWriteFolder();
63  
64          // Make sure the test folder is empty
65          scratchFolder.delete(Selectors.EXCLUDE_SELF);
66          scratchFolder.createFolder();
67  
68          return scratchFolder;
69      }
70  
71      /***
72       * Tests folder creation.
73       */
74      public void testFolderCreate() throws Exception
75      {
76          FileObject scratchFolder = createScratchFolder();
77  
78          // Create direct child of the test folder
79          FileObject folder = scratchFolder.resolveFile("dir1");
80          assertTrue(!folder.exists());
81          folder.createFolder();
82          assertTrue(folder.exists());
83          assertSame(FileType.FOLDER, folder.getType());
84          assertEquals(0, folder.getChildren().length);
85  
86          // Create a descendant, where the intermediate folders don't exist
87          folder = scratchFolder.resolveFile("dir2/dir1/dir1");
88          assertTrue(!folder.exists());
89          assertTrue(!folder.getParent().exists());
90          assertTrue(!folder.getParent().getParent().exists());
91          folder.createFolder();
92          assertTrue(folder.exists());
93          assertSame(FileType.FOLDER, folder.getType());
94          assertEquals(0, folder.getChildren().length);
95          assertTrue(folder.getParent().exists());
96          assertTrue(folder.getParent().getParent().exists());
97  
98          // Test creating a folder that already exists
99          assertTrue(folder.exists());
100         folder.createFolder();
101     }
102 
103     /***
104      * Tests file creation
105      */
106     public void testFileCreate() throws Exception
107     {
108         FileObject scratchFolder = createScratchFolder();
109 
110         // Create direct child of the test folder
111         FileObject file = scratchFolder.resolveFile("file1.txt");
112         assertTrue(!file.exists());
113         file.createFile();
114         assertTrue(file.exists());
115         assertSame(FileType.FILE, file.getType());
116         assertEquals(0, file.getContent().getSize());
117         assertFalse(file.isHidden());
118         assertTrue(file.isReadable());
119         assertTrue(file.isWriteable());
120         
121         // Create direct child of the test folder - special name
122         file = scratchFolder.resolveFile("file1%25.txt");
123         assertTrue(!file.exists());
124         file.createFile();
125         assertTrue(file.exists());
126         assertSame(FileType.FILE, file.getType());
127         assertEquals(0, file.getContent().getSize());
128         assertFalse(file.isHidden());
129         assertTrue(file.isReadable());
130         assertTrue(file.isWriteable());
131 
132         // Create a descendant, where the intermediate folders don't exist
133         file = scratchFolder.resolveFile("dir1/dir1/file1.txt");
134         assertTrue(!file.exists());
135         assertTrue(!file.getParent().exists());
136         assertTrue(!file.getParent().getParent().exists());
137         file.createFile();
138         assertTrue(file.exists());
139         assertSame(FileType.FILE, file.getType());
140         assertEquals(0, file.getContent().getSize());
141         assertTrue(file.getParent().exists());
142         assertTrue(file.getParent().getParent().exists());
143         assertFalse(file.getParent().isHidden());
144         assertFalse(file.getParent().getParent().isHidden());
145         
146         // Test creating a file that already exists
147         assertTrue(file.exists());
148         file.createFile();
149         assertTrue(file.exists());
150         assertTrue(file.isReadable());
151         assertTrue(file.isWriteable());
152     }
153 
154     /***
155      * Tests file/folder creation with mismatched types.
156      */
157     public void testFileCreateMismatched() throws Exception
158     {
159         FileObject scratchFolder = createScratchFolder();
160 
161         // Create a test file and folder
162         FileObject file = scratchFolder.resolveFile("dir1/file1.txt");
163         file.createFile();
164         assertEquals(FileType.FILE, file.getType());
165 
166         FileObject folder = scratchFolder.resolveFile("dir1/dir2");
167         folder.createFolder();
168         assertEquals(FileType.FOLDER, folder.getType());
169 
170         // Attempt to create a file that already exists as a folder
171         try
172         {
173             folder.createFile();
174             fail();
175         }
176         catch (FileSystemException exc)
177         {
178         }
179 
180         // Attempt to create a folder that already exists as a file
181         try
182         {
183             file.createFolder();
184             fail();
185         }
186         catch (FileSystemException exc)
187         {
188         }
189 
190         // Attempt to create a folder as a child of a file
191         FileObject folder2 = file.resolveFile("some-child");
192         try
193         {
194             folder2.createFolder();
195             fail();
196         }
197         catch (FileSystemException exc)
198         {
199         }
200     }
201 
202     /***
203      * Tests deletion
204      */
205     public void testDelete() throws Exception
206     {
207         // Set-up the test structure
208         FileObject folder = createScratchFolder();
209         folder.resolveFile("file1.txt").createFile();
210         folder.resolveFile("file%25.txt").createFile();
211         folder.resolveFile("emptydir").createFolder();
212         folder.resolveFile("dir1/file1.txt").createFile();
213         folder.resolveFile("dir1/dir2/file2.txt").createFile();
214 
215         // Delete a file
216         FileObject file = folder.resolveFile("file1.txt");
217         assertTrue(file.exists());
218         file.delete(Selectors.SELECT_ALL);
219         assertTrue(!file.exists());
220 
221         // Delete a special name file
222         file = folder.resolveFile("file%25.txt");
223         assertTrue(file.exists());
224         file.delete(Selectors.SELECT_ALL);
225         assertTrue(!file.exists());
226 
227         // Delete an empty folder
228         file = folder.resolveFile("emptydir");
229         assertTrue(file.exists());
230         file.delete(Selectors.SELECT_ALL);
231         assertTrue(!file.exists());
232 
233         // Recursive delete
234         file = folder.resolveFile("dir1");
235         FileObject file2 = file.resolveFile("dir2/file2.txt");
236         assertTrue(file.exists());
237         assertTrue(file2.exists());
238         file.delete(Selectors.SELECT_ALL);
239         assertTrue(!file.exists());
240         assertTrue(!file2.exists());
241 
242         // Delete a file that does not exist
243         file = folder.resolveFile("some-folder/some-file");
244         assertTrue(!file.exists());
245         file.delete(Selectors.SELECT_ALL);
246         assertTrue(!file.exists());
247     }
248 
249     /***
250      * Tests concurrent read and write on the same file fails.
251      */
252     /* imario@apache.org leave this to some sort of LockManager
253     public void testConcurrentReadWrite() throws Exception
254     {
255         final FileObject scratchFolder = createScratchFolder();
256 
257         final FileObject file = scratchFolder.resolveFile("file1.txt");
258         file.createFile();
259 
260         // Start reading from the file
261         final InputStream instr = file.getContent().getInputStream();
262 
263         try
264         {
265             // Try to write to the file
266             file.getContent().getOutputStream();
267             fail();
268         }
269         catch (final FileSystemException e)
270         {
271             // Check error message
272             assertSameMessage("vfs.provider/write-in-use.error", file, e);
273         }
274         finally
275         {
276             instr.close();
277         }
278     }
279     */
280 
281     /***
282      * Tests concurrent writes on the same file fails.
283      */
284     /* imario@apache.org leave this to some sort of LockManager
285     public void testConcurrentWrite() throws Exception
286     {
287         final FileObject scratchFolder = createScratchFolder();
288 
289         final FileObject file = scratchFolder.resolveFile("file1.txt");
290         file.createFile();
291 
292         // Start writing to the file
293         final OutputStream outstr = file.getContent().getOutputStream();
294         final String testContent = "some content";
295         try
296         {
297             // Write some content to the first stream
298             outstr.write(testContent.getBytes());
299 
300             // Try to open another output stream
301             file.getContent().getOutputStream();
302             fail();
303         }
304         catch (final FileSystemException e)
305         {
306             // Check error message
307             assertSameMessage("vfs.provider/write-in-use.error", file, e);
308         }
309         finally
310         {
311             outstr.close();
312         }
313 
314         // Make sure that the content written to the first stream is actually applied
315         assertSameContent(testContent, file);
316     }
317     */
318 
319     /***
320      * Tests file copy to and from the same filesystem type.  This was a problem
321      * w/ FTP.
322      */
323     public void testCopySameFileSystem() throws Exception
324     {
325         final FileObject scratchFolder = createScratchFolder();
326 
327         // Create direct child of the test folder
328         final FileObject file = scratchFolder.resolveFile("file1.txt");
329         assertTrue(!file.exists());
330 
331         // Create the source file
332         final String content = "Here is some sample content for the file.  Blah Blah Blah.";
333         final OutputStream os = file.getContent().getOutputStream();
334         try
335         {
336             os.write(content.getBytes("utf-8"));
337         }
338         finally
339         {
340             os.close();
341         }
342 
343         assertSameContent(content, file);
344 
345         // Make sure we can copy the new file to another file on the same filesystem
346         FileObject fileCopy = scratchFolder.resolveFile("file1copy.txt");
347         assertTrue(!fileCopy.exists());
348         fileCopy.copyFrom(file, Selectors.SELECT_SELF);
349 
350         assertSameContent(content, fileCopy);
351     }
352 
353     /***
354      * Tests overwriting a file on the same file system.
355      */
356     public void testOverwriteSameFileSystem() throws Exception
357     {
358         final FileObject scratchFolder = createScratchFolder();
359 
360         // Create direct child of the test folder
361         final FileObject file = scratchFolder.resolveFile("file1.txt");
362         assertTrue(!file.exists());
363 
364         // Create the source file
365         final String content = "Here is some sample content for the file.  Blah Blah Blah.";
366         final OutputStream os = file.getContent().getOutputStream();
367         try
368         {
369             os.write(content.getBytes("utf-8"));
370         }
371         finally
372         {
373             os.close();
374         }
375 
376         assertSameContent(content, file);
377 
378         // Make sure we can copy the new file to another file on the same filesystem
379         FileObject fileCopy = scratchFolder.resolveFile("file1copy.txt");
380         assertTrue(!fileCopy.exists());
381         fileCopy.copyFrom(file, Selectors.SELECT_SELF);
382 
383         assertSameContent(content, fileCopy);
384         
385         // Make sure we can copy the same new file to the same target file on the same filesystem
386         assertTrue(fileCopy.exists());
387         fileCopy.copyFrom(file, Selectors.SELECT_SELF);
388 
389         assertSameContent(content, fileCopy);
390     }
391 
392     /***
393      * Tests create-delete-create-a-file sequence on the same file system.
394      */
395     public void testCreateDeleteCreateSameFileSystem() throws Exception
396     {
397         final FileObject scratchFolder = createScratchFolder();
398 
399         // Create direct child of the test folder
400         final FileObject file = scratchFolder.resolveFile("file1.txt");
401         assertTrue(!file.exists());
402 
403         // Create the source file
404         final String content = "Here is some sample content for the file.  Blah Blah Blah.";
405         final OutputStream os = file.getContent().getOutputStream();
406         try
407         {
408             os.write(content.getBytes("utf-8"));
409         }
410         finally
411         {
412             os.close();
413         }
414 
415         assertSameContent(content, file);
416 
417         // Make sure we can copy the new file to another file on the same filesystem
418         FileObject fileCopy = scratchFolder.resolveFile("file1copy.txt");
419         assertTrue(!fileCopy.exists());
420         fileCopy.copyFrom(file, Selectors.SELECT_SELF);
421 
422         assertSameContent(content, fileCopy);
423 
424         // Delete the file.
425         assertTrue(fileCopy.exists());
426         assertTrue(fileCopy.delete());
427         
428         // Make sure we can copy the same new file to the same target file on the same filesystem
429         assertTrue(!fileCopy.exists());
430         fileCopy.copyFrom(file, Selectors.SELECT_SELF);
431 
432         assertSameContent(content, fileCopy);
433     }
434 
435     /***
436      * Test that children are handled correctly by create and delete.
437      */
438     public void testListChildren() throws Exception
439     {
440         FileObject folder = createScratchFolder();
441         HashSet names = new HashSet();
442 
443         // Make sure the folder is empty
444         assertEquals(0, folder.getChildren().length);
445 
446         // Create a child folder
447         folder.resolveFile("dir1").createFolder();
448         names.add("dir1");
449         assertSameFileSet(names, folder.getChildren());
450 
451         // Create a child file
452         folder.resolveFile("file1.html").createFile();
453         names.add("file1.html");
454         assertSameFileSet(names, folder.getChildren());
455 
456         // Create a descendent
457         folder.resolveFile("dir2/file1.txt").createFile();
458         names.add("dir2");
459         assertSameFileSet(names, folder.getChildren());
460 
461         // Create a child file via an output stream
462         OutputStream outstr = folder.resolveFile("file2.txt").getContent().getOutputStream();
463         outstr.close();
464         names.add("file2.txt");
465         assertSameFileSet(names, folder.getChildren());
466 
467         // Delete a child folder
468         folder.resolveFile("dir1").delete(Selectors.SELECT_ALL);
469         names.remove("dir1");
470         assertSameFileSet(names, folder.getChildren());
471 
472         // Delete a child file
473         folder.resolveFile("file1.html").delete(Selectors.SELECT_ALL);
474         names.remove("file1.html");
475         assertSameFileSet(names, folder.getChildren());
476 
477         // Recreate the folder
478         folder.delete(Selectors.SELECT_ALL);
479         folder.createFolder();
480         assertEquals(0, folder.getChildren().length);
481     }
482 
483     /***
484      * Check listeners are notified of changes.
485      */
486     public void testListener() throws Exception
487     {
488         final FileObject baseFile = createScratchFolder();
489 
490         FileObject child = baseFile.resolveFile("newfile.txt");
491         assertTrue(!child.exists());
492 
493         FileSystem fs = baseFile.getFileSystem();
494         TestListener listener = new TestListener(child);
495         fs.addListener(child, listener);
496 
497         // Create as a folder
498         listener.addCreateEvent();
499         child.createFolder();
500         listener.assertFinished();
501 
502         // Create the folder again.  Should not get an event.
503         child.createFolder();
504 
505         // Delete
506         listener.addDeleteEvent();
507         child.delete();
508         listener.assertFinished();
509 
510         // Delete again.  Should not get an event
511         child.delete();
512 
513         // Create as a file
514         listener.addCreateEvent();
515         child.createFile();
516         listener.assertFinished();
517 
518         // Create the file again.  Should not get an event
519         child.createFile();
520 
521         listener.addDeleteEvent();
522         child.delete();
523 
524         // Create as a file, by writing to it.
525         listener.addCreateEvent();
526         child.getContent().getOutputStream().close();
527         listener.assertFinished();
528 
529         // Recreate the file by writing to it
530         child.getContent().getOutputStream().close();
531 
532         // Copy another file over the top
533         final FileObject otherChild = baseFile.resolveFile("folder1");
534         otherChild.createFolder();
535         listener.addDeleteEvent();
536         listener.addCreateEvent();
537         child.copyFrom(otherChild, Selectors.SELECT_SELF);
538         listener.assertFinished();
539 
540         fs.removeListener(child, listener);
541     }
542 
543     /***
544      * Ensures the names of a set of files match an expected set.
545      */
546     private void assertSameFileSet(Set names, FileObject[] files)
547     {
548         // Make sure the sets are the same length
549         assertEquals(names.size(), files.length);
550 
551         // Check for unexpected names
552         for (int i = 0; i < files.length; i++)
553         {
554             FileObject file = files[i];
555             assertTrue(names.contains(file.getName().getBaseName()));
556         }
557     }
558 
559     /***
560      * A test listener.
561      */
562     private static class TestListener implements FileListener
563     {
564         private final FileObject file;
565         private final ArrayList events = new ArrayList();
566         private static final Object CREATE = "create";
567         private static final Object DELETE = "delete";
568         private static final Object CHANGED = "changed";
569 
570         public TestListener(final FileObject file)
571         {
572             this.file = file;
573         }
574 
575         /***
576          * Called when a file is created.
577          */
578         public void fileCreated(final FileChangeEvent event)
579         {
580             assertTrue("Unexpected create event", events.size() > 0);
581             assertSame("Expecting a create event", CREATE, events.remove(0));
582             assertSame(file, event.getFile());
583             try
584             {
585                 assertTrue(file.exists());
586             }
587             catch (FileSystemException e)
588             {
589                 fail();
590             }
591         }
592 
593         /***
594          * Called when a file is deleted.
595          */
596         public void fileDeleted(final FileChangeEvent event)
597         {
598             assertTrue("Unexpected delete event", events.size() > 0);
599             assertSame("Expecting a delete event", DELETE, events.remove(0));
600             assertSame(file, event.getFile());
601             try
602             {
603                 assertTrue(!file.exists());
604             }
605             catch (FileSystemException e)
606             {
607                 fail();
608             }
609         }
610 
611         public void fileChanged(FileChangeEvent event) throws Exception
612         {
613             assertTrue("Unexpected changed event", events.size() > 0);
614             assertSame("Expecting a changed event", CHANGED, events.remove(0));
615             assertSame(file, event.getFile());
616             try
617             {
618                 assertTrue(!file.exists());
619             }
620             catch (FileSystemException e)
621             {
622                 fail();
623             }
624         }
625 
626         public void addCreateEvent()
627         {
628             events.add(CREATE);
629         }
630 
631         public void addDeleteEvent()
632         {
633             events.add(DELETE);
634         }
635 
636         public void assertFinished()
637         {
638             assertEquals("Missing event", 0, events.size());
639         }
640     }
641 }