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;
18  
19  import java.io.File;
20  import java.io.FileFilter;
21  import java.io.IOException;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  
27  import junit.framework.Assert;
28  import junit.framework.Test;
29  import junit.framework.TestCase;
30  import junit.framework.TestSuite;
31  
32  import org.apache.commons.io.filefilter.FileFilterUtils;
33  import org.apache.commons.io.filefilter.IOFileFilter;
34  import org.apache.commons.io.filefilter.NameFileFilter;
35  import org.apache.commons.io.filefilter.OrFileFilter;
36  
37  /**
38   * This is used to test DirectoryWalker for correctness.
39   *
40   * @version $Id: DirectoryWalkerTestCase.java 481847 2006-12-03 18:05:37Z scolebourne $
41   * @see DirectoryWalker
42   *
43   */
44  public class DirectoryWalkerTestCase extends TestCase {
45  
46      // Directories
47      private static final File current      = new File(".");
48      private static final File javaDir      = new File("src/java");
49      private static final File orgDir       = new File(javaDir, "org");
50      private static final File apacheDir    = new File(orgDir, "apache");
51      private static final File commonsDir   = new File(apacheDir, "commons");
52      private static final File ioDir        = new File(commonsDir, "io");
53      private static final File outputDir    = new File(ioDir, "output");
54      private static final File[] dirs       = new File[] {orgDir, apacheDir, commonsDir, ioDir, outputDir};
55  
56      // Files
57      private static final File copyUtils     = new File(ioDir, "CopyUtils.java");
58      private static final File ioUtils       = new File(ioDir, "IOUtils.java");
59      private static final File proxyWriter   = new File(outputDir, "ProxyWriter.java");
60      private static final File nullStream    = new File(outputDir, "NullOutputStream.java");
61      private static final File[] ioFiles     = new File[] {copyUtils, ioUtils};
62      private static final File[] outputFiles = new File[] {proxyWriter, nullStream};
63      
64      // Filters
65      private static final IOFileFilter dirsFilter        = createNameFilter(dirs);
66      private static final IOFileFilter iofilesFilter     = createNameFilter(ioFiles);
67      private static final IOFileFilter outputFilesFilter = createNameFilter(outputFiles);
68      private static final IOFileFilter ioDirAndFilesFilter = new OrFileFilter(dirsFilter, iofilesFilter);
69      private static final IOFileFilter dirsAndFilesFilter = new OrFileFilter(ioDirAndFilesFilter, outputFilesFilter);
70  
71      // Filter to exclude SVN files
72      private static final IOFileFilter NOT_SVN = FileFilterUtils.makeSVNAware(null);
73  
74      public static Test suite() {
75          return new TestSuite(DirectoryWalkerTestCase.class);
76      }
77  
78      /** Construct the TestCase using the name */
79      public DirectoryWalkerTestCase(String name) {
80          super(name);
81      }
82  
83      /** Set Up */
84      protected void setUp() throws Exception {
85          super.setUp();
86      }
87  
88      /** Tear Down */
89      protected void tearDown() throws Exception {
90          super.tearDown();
91      }
92  
93      //-----------------------------------------------------------------------
94  
95      /**
96       * Test Filtering
97       */
98      public void testFilter() {
99          List results = new TestFileFinder(dirsAndFilesFilter, -1).find(javaDir);
100         assertEquals("Result Size", (1 + dirs.length + ioFiles.length + outputFiles.length), results.size());
101         assertTrue("Start Dir", results.contains(javaDir));
102         checkContainsFiles("Dir", dirs, results);
103         checkContainsFiles("IO File", ioFiles, results);
104         checkContainsFiles("Output File", outputFiles, results);
105     }
106 
107     /**
108      * Test Filtering and limit to depth 0
109      */
110     public void testFilterAndLimitA() {
111         List results = new TestFileFinder(NOT_SVN, 0).find(javaDir);
112         assertEquals("[A] Result Size", 1, results.size());
113         assertTrue("[A] Start Dir",   results.contains(javaDir));
114     }
115 
116     /**
117      * Test Filtering and limit to depth 1
118      */
119     public void testFilterAndLimitB() {
120         List results = new TestFileFinder(NOT_SVN, 1).find(javaDir);
121         assertEquals("[B] Result Size", 2, results.size());
122         assertTrue("[B] Start Dir",   results.contains(javaDir));
123         assertTrue("[B] Org Dir",     results.contains(orgDir));
124     }
125 
126     /**
127      * Test Filtering and limit to depth 3
128      */
129     public void testFilterAndLimitC() {
130         List results = new TestFileFinder(NOT_SVN, 3).find(javaDir);
131         assertEquals("[C] Result Size", 4, results.size());
132         assertTrue("[C] Start Dir",   results.contains(javaDir));
133         assertTrue("[C] Org Dir",     results.contains(orgDir));
134         assertTrue("[C] Apache Dir",  results.contains(apacheDir));
135         assertTrue("[C] Commons Dir", results.contains(commonsDir));
136     }
137 
138     /**
139      * Test Filtering and limit to depth 5
140      */
141     public void testFilterAndLimitD() {
142         List results = new TestFileFinder(dirsAndFilesFilter, 5).find(javaDir);
143         assertEquals("[D] Result Size", (1 + dirs.length + ioFiles.length), results.size());
144         assertTrue("[D] Start Dir", results.contains(javaDir));
145         checkContainsFiles("[D] Dir", dirs, results);
146         checkContainsFiles("[D] File", ioFiles, results);
147     }
148 
149     /**
150      * Test separate dir and file filters
151      */
152     public void testFilterDirAndFile1() {
153         List results = new TestFileFinder(dirsFilter, iofilesFilter, -1).find(javaDir);
154         assertEquals("[DirAndFile1] Result Size", (1 + dirs.length + ioFiles.length), results.size());
155         assertTrue("[DirAndFile1] Start Dir", results.contains(javaDir));
156         checkContainsFiles("[DirAndFile1] Dir", dirs, results);
157         checkContainsFiles("[DirAndFile1] File", ioFiles, results);
158     }
159 
160     /**
161      * Test separate dir and file filters
162      */
163     public void testFilterDirAndFile2() {
164         List results = new TestFileFinder((IOFileFilter) null, (IOFileFilter) null, -1).find(javaDir);
165         assertTrue("[DirAndFile2] Result Size", results.size() > (1 + dirs.length + ioFiles.length));
166         assertTrue("[DirAndFile2] Start Dir", results.contains(javaDir));
167         checkContainsFiles("[DirAndFile2] Dir", dirs, results);
168         checkContainsFiles("[DirAndFile2] File", ioFiles, results);
169     }
170 
171     /**
172      * Test separate dir and file filters
173      */
174     public void testFilterDirAndFile3() {
175         List results = new TestFileFinder(dirsFilter, (IOFileFilter) null, -1).find(javaDir);
176         List resultDirs = directoriesOnly(results);
177         assertEquals("[DirAndFile3] Result Size", (1 + dirs.length), resultDirs.size());
178         assertTrue("[DirAndFile3] Start Dir", results.contains(javaDir));
179         checkContainsFiles("[DirAndFile3] Dir", dirs, resultDirs);
180     }
181 
182     /**
183      * Test separate dir and file filters
184      */
185     public void testFilterDirAndFile4() {
186         List results = new TestFileFinder((IOFileFilter) null, iofilesFilter, -1).find(javaDir);
187         List resultFiles = filesOnly(results);
188         assertEquals("[DirAndFile4] Result Size", ioFiles.length, resultFiles.size());
189         assertTrue("[DirAndFile4] Start Dir", results.contains(javaDir));
190         checkContainsFiles("[DirAndFile4] File", ioFiles, resultFiles);
191     }
192 
193     /**
194      * Test Limiting to current directory
195      */
196     public void testLimitToCurrent() {
197         List results = new TestFileFinder(null, 0).find(current);
198         assertEquals("Result Size", 1, results.size());
199         assertTrue("Current Dir", results.contains(new File(".")));
200     }
201 
202     /**
203      * test an invalid start directory
204      */
205     public void testMissingStartDirectory() {
206 
207         // TODO is this what we want with invalid directory?
208         File invalidDir = new File("invalid-dir");
209         List results = new TestFileFinder(null, -1).find(invalidDir);
210         assertEquals("Result Size", 1, results.size());
211         assertTrue("Current Dir", results.contains(invalidDir));
212  
213         try {
214             new TestFileFinder(null, -1).find(null);
215             fail("Null start directory didn't throw Exception");
216         } catch (NullPointerException ignore) {
217             // expected result
218         }
219     }
220 
221     /**
222      * test an invalid start directory
223      */
224     public void testHandleStartDirectoryFalse() {
225 
226         List results = new TestFalseFileFinder(null, -1).find(current);
227         assertEquals("Result Size", 0, results.size());
228 
229     }
230 
231     // ------------ Convenience Test Methods ------------------------------------
232 
233     /**
234      * Check the files in the array are in the results list.
235      */
236     private void checkContainsFiles(String prefix, File[] files, Collection results) {
237         for (int i = 0; i < files.length; i++) {
238             assertTrue(prefix + "["+i+"] " + files[i], results.contains(files[i]));
239         }
240     }
241 
242     /**
243      * Extract the directories.
244      */
245     private List directoriesOnly(Collection results) {
246         List list = new ArrayList(results.size());
247         for (Iterator it = results.iterator(); it.hasNext(); ) {
248             File file = (File) it.next();
249             if (file.isDirectory()) {
250                 list.add(file);
251             }
252         }
253         return list;
254     }
255 
256     /**
257      * Extract the files.
258      */
259     private List filesOnly(Collection results) {
260         List list = new ArrayList(results.size());
261         for (Iterator it = results.iterator(); it.hasNext(); ) {
262             File file = (File) it.next();
263             if (file.isFile()) {
264                 list.add(file);
265             }
266         }
267         return list;
268     }
269 
270     /**
271      * Create an name filter containg the names of the files
272      * in the array.
273      */
274     private static IOFileFilter createNameFilter(File[] files) {
275         String[] names = new String[files.length];
276         for (int i = 0; i < files.length; i++) {
277             names[i] = files[i].getName();
278         }
279         return new NameFileFilter(names);
280     }
281 
282     /**
283      * Test Cancel
284      */
285     public void testCancel() {
286         String cancelName = null;
287 
288         // Cancel on a file
289         try {
290             cancelName = "DirectoryWalker.java";
291             new TestCancelWalker(cancelName, false).find(javaDir);
292             fail("CancelException not thrown for '" + cancelName + "'");
293         } catch (DirectoryWalker.CancelException cancel) {
294             assertEquals("File:  " + cancelName,   cancelName, cancel.getFile().getName());
295             assertEquals("Depth: " + cancelName,  5, cancel.getDepth());
296         } catch(IOException ex) {
297             fail("IOException: " + cancelName + " " + ex);
298         }
299 
300         // Cancel on a directory
301         try {
302             cancelName = "commons";
303             new TestCancelWalker(cancelName, false).find(javaDir);
304             fail("CancelException not thrown for '" + cancelName + "'");
305         } catch (DirectoryWalker.CancelException cancel) {
306             assertEquals("File:  " + cancelName,   cancelName, cancel.getFile().getName());
307             assertEquals("Depth: " + cancelName,  3, cancel.getDepth());
308         } catch(IOException ex) {
309             fail("IOException: " + cancelName + " " + ex);
310         }
311 
312         // Suppress CancelException (use same file name as preceeding test)
313         try {
314             List results = new TestCancelWalker(cancelName, true).find(javaDir);
315             File lastFile = (File)results.get(results.size() - 1);
316             assertEquals("Suppress:  " + cancelName,   cancelName, lastFile.getName());
317         } catch(IOException ex) {
318             fail("Suppress threw " + ex);
319         }
320 
321     }
322 
323     /**
324      * Test Cancel
325      */
326     public void testMultiThreadCancel() {
327         String cancelName = null;
328         TestMultiThreadCancelWalker walker = null;
329         // Cancel on a file
330         try {
331             cancelName = "DirectoryWalker.java";
332             walker = new TestMultiThreadCancelWalker(cancelName, false);
333             walker.find(javaDir);
334             fail("CancelException not thrown for '" + cancelName + "'");
335         } catch (DirectoryWalker.CancelException cancel) {
336             File last = (File) walker.results.get(walker.results.size() - 1);
337             assertEquals(cancelName, last.getName());
338             assertEquals("Depth: " + cancelName,  5, cancel.getDepth());
339         } catch(IOException ex) {
340             fail("IOException: " + cancelName + " " + ex);
341         }
342         
343         // Cancel on a directory
344         try {
345             cancelName = "commons";
346             walker = new TestMultiThreadCancelWalker(cancelName, false);
347             walker.find(javaDir);
348             fail("CancelException not thrown for '" + cancelName + "'");
349         } catch (DirectoryWalker.CancelException cancel) {
350             assertEquals("File:  " + cancelName,   cancelName, cancel.getFile().getName());
351             assertEquals("Depth: " + cancelName,  3, cancel.getDepth());
352         } catch(IOException ex) {
353             fail("IOException: " + cancelName + " " + ex);
354         }
355         
356         // Suppress CancelException (use same file name as preceeding test)
357         try {
358             walker = new TestMultiThreadCancelWalker(cancelName, true);
359             List results = walker.find(javaDir);
360             File lastFile = (File) results.get(results.size() - 1);
361             assertEquals("Suppress:  " + cancelName, cancelName, lastFile.getName());
362         } catch(IOException ex) {
363             fail("Suppress threw " + ex);
364         }
365 
366     }
367 
368     // ------------ Test DirectoryWalker implementation --------------------------
369 
370     /**
371      * Test DirectoryWalker implementation that finds files in a directory hierarchy
372      * applying a file filter.
373      */
374     private static class TestFileFinder extends DirectoryWalker {
375 
376         protected TestFileFinder(FileFilter filter, int depthLimit) {
377             super(filter, depthLimit);
378         }
379 
380         protected TestFileFinder(IOFileFilter dirFilter, IOFileFilter fileFilter, int depthLimit) {
381             super(dirFilter, fileFilter, depthLimit);
382         }
383 
384         /** find files. */
385         protected List find(File startDirectory) {
386            List results = new ArrayList();
387            try {
388                walk(startDirectory, results);
389            } catch(IOException ex) {
390                Assert.fail(ex.toString());
391            }
392            return results;
393         }
394 
395         /** Handles a directory end by adding the File to the result set. */
396         protected void handleDirectoryEnd(File directory, int depth, Collection results) {
397             results.add(directory);
398         }
399 
400         /** Handles a file by adding the File to the result set. */
401         protected void handleFile(File file, int depth, Collection results) {
402             results.add(file);
403         }
404     }
405 
406     // ------------ Test DirectoryWalker implementation --------------------------
407 
408     /**
409      * Test DirectoryWalker implementation that always returns false
410      * from handleDirectoryStart()
411      */
412     private static class TestFalseFileFinder extends TestFileFinder {
413 
414         protected TestFalseFileFinder(FileFilter filter, int depthLimit) {
415             super(filter, depthLimit);
416         }
417 
418         /** Always returns false. */
419         protected boolean handleDirectory(File directory, int depth, Collection results) {
420             return false;
421         }
422     }
423 
424     // ------------ Test DirectoryWalker implementation --------------------------
425 
426     /**
427      * Test DirectoryWalker implementation that finds files in a directory hierarchy
428      * applying a file filter.
429      */
430     static class TestCancelWalker extends DirectoryWalker {
431         private String cancelFileName;
432         private boolean suppressCancel;
433 
434         TestCancelWalker(String cancelFileName,boolean suppressCancel) {
435             super();
436             this.cancelFileName = cancelFileName;
437             this.suppressCancel = suppressCancel;
438         }
439 
440         /** find files. */
441         protected List find(File startDirectory) throws IOException {
442            List results = new ArrayList();
443            walk(startDirectory, results);
444            return results;
445         }
446 
447         /** Handles a directory end by adding the File to the result set. */
448         protected void handleDirectoryEnd(File directory, int depth, Collection results) throws IOException {
449             results.add(directory);
450             if (cancelFileName.equals(directory.getName())) {
451                 throw new CancelException(directory, depth);
452             }
453         }
454 
455         /** Handles a file by adding the File to the result set. */
456         protected void handleFile(File file, int depth, Collection results) throws IOException {
457             results.add(file);
458             if (cancelFileName.equals(file.getName())) {
459                 throw new CancelException(file, depth);
460             }
461         }
462 
463         /** Handles Cancel. */
464         protected void handleCancelled(File startDirectory, Collection results,
465                        CancelException cancel) throws IOException {
466             if (!suppressCancel) {
467                 super.handleCancelled(startDirectory, results, cancel);
468             }
469         }
470     }
471 
472     /**
473      * Test DirectoryWalker implementation that finds files in a directory hierarchy
474      * applying a file filter.
475      */
476     static class TestMultiThreadCancelWalker extends DirectoryWalker {
477         private String cancelFileName;
478         private boolean suppressCancel;
479         private boolean cancelled;
480         public List results;
481 
482         TestMultiThreadCancelWalker(String cancelFileName, boolean suppressCancel) {
483             super();
484             this.cancelFileName = cancelFileName;
485             this.suppressCancel = suppressCancel;
486         }
487 
488         /** find files. */
489         protected List find(File startDirectory) throws IOException {
490            results = new ArrayList();
491            walk(startDirectory, results);
492            return results;
493         }
494 
495         /** Handles a directory end by adding the File to the result set. */
496         protected void handleDirectoryEnd(File directory, int depth, Collection results) throws IOException {
497             results.add(directory);
498             assertEquals(false, cancelled);
499             if (cancelFileName.equals(directory.getName())) {
500                 cancelled = true;
501             }
502         }
503 
504         /** Handles a file by adding the File to the result set. */
505         protected void handleFile(File file, int depth, Collection results) throws IOException {
506             results.add(file);
507             assertEquals(false, cancelled);
508             if (cancelFileName.equals(file.getName())) {
509                 cancelled = true;
510             }
511         }
512 
513         /** Handles Cancelled. */
514         protected boolean handleIsCancelled(File file, int depth, Collection results) throws IOException {
515             return cancelled;
516         }
517 
518         /** Handles Cancel. */
519         protected void handleCancelled(File startDirectory, Collection results,
520                        CancelException cancel) throws IOException {
521             if (!suppressCancel) {
522                 super.handleCancelled(startDirectory, results, cancel);
523             }
524         }
525     }
526 
527 }