View Javadoc
1   package org.codehaus.plexus.util;
2   
3   /*
4    * Copyright The Codehaus Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.io.File;
20  import java.io.IOException;
21  import java.net.URI;
22  import java.net.URISyntaxException;
23  import java.net.URL;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.List;
27  
28  /**
29   * Base class for testcases doing tests with files.
30   * 
31   * @author Dan T. Tran
32   */
33  public class DirectoryScannerTest
34      extends FileBasedTestCase
35  {
36      private static String testDir = getTestDirectory().getPath();
37  
38      public void testCrossPlatformIncludesString()
39          throws IOException, URISyntaxException
40      {
41          DirectoryScanner ds = new DirectoryScanner();
42          ds.setBasedir( new File( getTestResourcesDir() + File.separator + "directory-scanner" ).getCanonicalFile() );
43  
44          String fs;
45          if ( File.separatorChar == '/' )
46          {
47              fs = "\\";
48          }
49          else
50          {
51              fs = "/";
52          }
53  
54          ds.setIncludes( new String[] { "foo" + fs } );
55          ds.addDefaultExcludes();
56          ds.scan();
57  
58          String[] files = ds.getIncludedFiles();
59          assertEquals( 1, files.length );
60      }
61  
62      public void testCrossPlatformExcludesString()
63          throws IOException, URISyntaxException
64      {
65          DirectoryScanner ds = new DirectoryScanner();
66          ds.setBasedir( new File( getTestResourcesDir() + File.separator + "directory-scanner" ).getCanonicalFile() );
67          ds.setIncludes( new String[] { "**" } );
68  
69          String fs;
70          if ( File.separatorChar == '/' )
71          {
72              fs = "\\";
73          }
74          else
75          {
76              fs = "/";
77          }
78  
79          ds.setExcludes( new String[] { "foo" + fs } );
80          ds.addDefaultExcludes();
81          ds.scan();
82  
83          String[] files = ds.getIncludedFiles();
84          assertEquals( 0, files.length );
85      }
86  
87      private String getTestResourcesDir()
88          throws URISyntaxException
89      {
90          ClassLoader cloader = Thread.currentThread().getContextClassLoader();
91          URL resource = cloader.getResource( "test.txt" );
92          if ( resource == null )
93          {
94              fail( "Cannot locate test-resources directory containing 'test.txt' in the classloader." );
95          }
96  
97          File file = new File( new URI( resource.toExternalForm() ).normalize().getPath() );
98  
99          return file.getParent();
100     }
101 
102     private void createTestFiles()
103         throws IOException
104     {
105         FileUtils.mkdir( testDir );
106         this.createFile( new File( testDir + "/scanner1.dat" ), 0 );
107         this.createFile( new File( testDir + "/scanner2.dat" ), 0 );
108         this.createFile( new File( testDir + "/scanner3.dat" ), 0 );
109         this.createFile( new File( testDir + "/scanner4.dat" ), 0 );
110         this.createFile( new File( testDir + "/scanner5.dat" ), 0 );
111     }
112 
113     public void testGeneral()
114         throws IOException
115     {
116         this.createTestFiles();
117 
118         String includes = "scanner1.dat,scanner2.dat,scanner3.dat,scanner4.dat,scanner5.dat";
119         String excludes = "scanner1.dat,scanner2.dat";
120 
121         List<File> fileNames = FileUtils.getFiles( new File( testDir ), includes, excludes, false );
122 
123         assertEquals( "Wrong number of results.", 3, fileNames.size() );
124         assertTrue( "3 not found.", fileNames.contains( new File( "scanner3.dat" ) ) );
125         assertTrue( "4 not found.", fileNames.contains( new File( "scanner4.dat" ) ) );
126         assertTrue( "5 not found.", fileNames.contains( new File( "scanner5.dat" ) ) );
127 
128     }
129 
130     public void testIncludesExcludesWithWhiteSpaces()
131         throws IOException
132     {
133         this.createTestFiles();
134 
135         String includes = "scanner1.dat,\n  \n,scanner2.dat  \n\r, scanner3.dat\n, \tscanner4.dat,scanner5.dat\n,";
136 
137         String excludes = "scanner1.dat,\n  \n,scanner2.dat  \n\r,,";
138 
139         List<File> fileNames = FileUtils.getFiles( new File( testDir ), includes, excludes, false );
140 
141         assertEquals( "Wrong number of results.", 3, fileNames.size() );
142         assertTrue( "3 not found.", fileNames.contains( new File( "scanner3.dat" ) ) );
143         assertTrue( "4 not found.", fileNames.contains( new File( "scanner4.dat" ) ) );
144         assertTrue( "5 not found.", fileNames.contains( new File( "scanner5.dat" ) ) );
145     }
146 
147     public void testFollowSymlinksFalse()
148     {
149         DirectoryScanner ds = new DirectoryScanner();
150         ds.setBasedir( new File( "src/test/resources/symlinks/src/" ) );
151         ds.setFollowSymlinks( false );
152         ds.scan();
153         List<String> included = Arrays.asList( ds.getIncludedFiles() );
154         assertAlwaysIncluded( included );
155         assertEquals( 9, included.size() );
156         List<String> includedDirs = Arrays.asList( ds.getIncludedDirectories() );
157         assertTrue( includedDirs.contains( "" ) ); // w00t !
158         assertTrue( includedDirs.contains( "aRegularDir" ) );
159         assertTrue( includedDirs.contains( "symDir" ) );
160         assertTrue( includedDirs.contains( "symLinkToDirOnTheOutside" ) );
161         assertTrue( includedDirs.contains( "targetDir" ) );
162         assertEquals( 5, includedDirs.size() );
163     }
164 
165     private void assertAlwaysIncluded( List<String> included )
166     {
167         assertTrue( included.contains( "aRegularDir" + File.separator + "aRegularFile.txt" ) );
168         assertTrue( included.contains( "targetDir" + File.separator + "targetFile.txt" ) );
169         assertTrue( included.contains( "fileR.txt" ) );
170         assertTrue( included.contains( "fileW.txt" ) );
171         assertTrue( included.contains( "fileX.txt" ) );
172         assertTrue( included.contains( "symR" ) );
173         assertTrue( included.contains( "symW" ) );
174         assertTrue( included.contains( "symX" ) );
175         assertTrue( included.contains( "symLinkToFileOnTheOutside" ) );
176     }
177 
178     public void testFollowSymlinks()
179     {
180         DirectoryScanner ds = new DirectoryScanner();
181         ds.setBasedir( new File( "src/test/resources/symlinks/src/" ) );
182         ds.setFollowSymlinks( true );
183         ds.scan();
184         List<String> included = Arrays.asList( ds.getIncludedFiles() );
185         assertAlwaysIncluded( included );
186         assertTrue( included.contains( "symDir" + File.separator + "targetFile.txt" ) );
187         assertTrue( included.contains( "symLinkToDirOnTheOutside" + File.separator + "FileInDirOnTheOutside.txt" ) );
188         assertEquals( 11, included.size() );
189 
190         List<String> includedDirs = Arrays.asList( ds.getIncludedDirectories() );
191         assertTrue( includedDirs.contains( "" ) ); // w00t !
192         assertTrue( includedDirs.contains( "aRegularDir" ) );
193         assertTrue( includedDirs.contains( "symDir" ) );
194         assertTrue( includedDirs.contains( "symLinkToDirOnTheOutside" ) );
195         assertTrue( includedDirs.contains( "targetDir" ) );
196         assertEquals( 5, includedDirs.size() );
197     }
198 
199     private void createTestDirectories()
200         throws IOException
201     {
202         FileUtils.mkdir( testDir + File.separator + "directoryTest" );
203         FileUtils.mkdir( testDir + File.separator + "directoryTest" + File.separator + "testDir123" );
204         FileUtils.mkdir( testDir + File.separator + "directoryTest" + File.separator + "test_dir_123" );
205         FileUtils.mkdir( testDir + File.separator + "directoryTest" + File.separator + "test-dir-123" );
206         this.createFile( new File( testDir + File.separator + "directoryTest" + File.separator + "testDir123"
207             + File.separator + "file1.dat" ), 0 );
208         this.createFile( new File( testDir + File.separator + "directoryTest" + File.separator + "test_dir_123"
209             + File.separator + "file1.dat" ), 0 );
210         this.createFile( new File( testDir + File.separator + "directoryTest" + File.separator + "test-dir-123"
211             + File.separator + "file1.dat" ), 0 );
212     }
213 
214     public void testDirectoriesWithHyphens()
215         throws IOException
216     {
217         this.createTestDirectories();
218 
219         DirectoryScanner ds = new DirectoryScanner();
220         String[] includes = { "**/*.dat" };
221         String[] excludes = { "" };
222         ds.setIncludes( includes );
223         ds.setExcludes( excludes );
224         ds.setBasedir( new File( testDir + File.separator + "directoryTest" ) );
225         ds.setCaseSensitive( true );
226         ds.scan();
227 
228         String[] files = ds.getIncludedFiles();
229         assertEquals( "Wrong number of results.", 3, files.length );
230     }
231 
232     public void testAntExcludesOverrideIncludes()
233         throws IOException
234     {
235         printTestHeader();
236 
237         File dir = new File( testDir, "regex-dir" );
238         dir.mkdirs();
239 
240         String[] excludedPaths = { "target/foo.txt" };
241 
242         createFiles( dir, excludedPaths );
243 
244         String[] includedPaths = { "src/main/resources/project/target/foo.txt" };
245 
246         createFiles( dir, includedPaths );
247 
248         DirectoryScanner ds = new DirectoryScanner();
249 
250         String[] includes = { "**/target/*" };
251         String[] excludes = { "target/*" };
252 
253         // This doesn't work, since excluded patterns refine included ones, meaning they operate on
254         // the list of paths that passed the included patterns, and can override them.
255         // String[] includes = {"**src/**/target/**/*" };
256         // String[] excludes = { "**/target/**/*" };
257 
258         ds.setIncludes( includes );
259         ds.setExcludes( excludes );
260         ds.setBasedir( dir );
261         ds.scan();
262 
263         assertInclusionsAndExclusions( ds.getIncludedFiles(), excludedPaths, includedPaths );
264     }
265 
266     public void testAntExcludesOverrideIncludesWithExplicitAntPrefix()
267         throws IOException
268     {
269         printTestHeader();
270 
271         File dir = new File( testDir, "regex-dir" );
272         dir.mkdirs();
273 
274         String[] excludedPaths = { "target/foo.txt" };
275 
276         createFiles( dir, excludedPaths );
277 
278         String[] includedPaths = { "src/main/resources/project/target/foo.txt" };
279 
280         createFiles( dir, includedPaths );
281 
282         DirectoryScanner ds = new DirectoryScanner();
283 
284         String[] includes =
285             { SelectorUtils.ANT_HANDLER_PREFIX + "**/target/**/*" + SelectorUtils.PATTERN_HANDLER_SUFFIX };
286         String[] excludes = { SelectorUtils.ANT_HANDLER_PREFIX + "target/**/*" + SelectorUtils.PATTERN_HANDLER_SUFFIX };
287 
288         // This doesn't work, since excluded patterns refine included ones, meaning they operate on
289         // the list of paths that passed the included patterns, and can override them.
290         // String[] includes = {"**src/**/target/**/*" };
291         // String[] excludes = { "**/target/**/*" };
292 
293         ds.setIncludes( includes );
294         ds.setExcludes( excludes );
295         ds.setBasedir( dir );
296         ds.scan();
297 
298         assertInclusionsAndExclusions( ds.getIncludedFiles(), excludedPaths, includedPaths );
299     }
300 
301     public void testRegexIncludeWithExcludedPrefixDirs()
302         throws IOException
303     {
304         printTestHeader();
305 
306         File dir = new File( testDir, "regex-dir" );
307         dir.mkdirs();
308 
309         String[] excludedPaths = { "src/main/foo.txt" };
310 
311         createFiles( dir, excludedPaths );
312 
313         String[] includedPaths = { "src/main/resources/project/target/foo.txt" };
314 
315         createFiles( dir, includedPaths );
316 
317         String regex = ".+/target.*";
318 
319         DirectoryScanner ds = new DirectoryScanner();
320 
321         String includeExpr = SelectorUtils.REGEX_HANDLER_PREFIX + regex + SelectorUtils.PATTERN_HANDLER_SUFFIX;
322 
323         String[] includes = { includeExpr };
324         ds.setIncludes( includes );
325         ds.setBasedir( dir );
326         ds.scan();
327 
328         assertInclusionsAndExclusions( ds.getIncludedFiles(), excludedPaths, includedPaths );
329     }
330 
331     public void testRegexExcludeWithNegativeLookahead()
332         throws IOException
333     {
334         printTestHeader();
335 
336         File dir = new File( testDir, "regex-dir" );
337         try
338         {
339             FileUtils.deleteDirectory( dir );
340         }
341         catch ( IOException e )
342         {
343         }
344 
345         dir.mkdirs();
346 
347         String[] excludedPaths = { "target/foo.txt" };
348 
349         createFiles( dir, excludedPaths );
350 
351         String[] includedPaths = { "src/main/resources/project/target/foo.txt" };
352 
353         createFiles( dir, includedPaths );
354 
355         String regex = "(?!.*src/).*target.*";
356 
357         DirectoryScanner ds = new DirectoryScanner();
358 
359         String excludeExpr = SelectorUtils.REGEX_HANDLER_PREFIX + regex + SelectorUtils.PATTERN_HANDLER_SUFFIX;
360 
361         String[] excludes = { excludeExpr };
362         ds.setExcludes( excludes );
363         ds.setBasedir( dir );
364         ds.scan();
365 
366         assertInclusionsAndExclusions( ds.getIncludedFiles(), excludedPaths, includedPaths );
367     }
368 
369     public void testRegexWithSlashInsideCharacterClass()
370         throws IOException
371     {
372         printTestHeader();
373 
374         File dir = new File( testDir, "regex-dir" );
375         try
376         {
377             FileUtils.deleteDirectory( dir );
378         }
379         catch ( IOException e )
380         {
381         }
382 
383         dir.mkdirs();
384 
385         String[] excludedPaths = { "target/foo.txt", "target/src/main/target/foo.txt" };
386 
387         createFiles( dir, excludedPaths );
388 
389         String[] includedPaths = { "module/src/main/target/foo.txt" };
390 
391         createFiles( dir, includedPaths );
392 
393         // NOTE: The portion "[^/]" is the interesting part of this pattern.
394         String regex = "(?!((?!target/)[^/]+/)*src/).*target.*";
395 
396         DirectoryScanner ds = new DirectoryScanner();
397 
398         String excludeExpr = SelectorUtils.REGEX_HANDLER_PREFIX + regex + SelectorUtils.PATTERN_HANDLER_SUFFIX;
399 
400         String[] excludes = { excludeExpr };
401         ds.setExcludes( excludes );
402         ds.setBasedir( dir );
403         ds.scan();
404 
405         assertInclusionsAndExclusions( ds.getIncludedFiles(), excludedPaths, includedPaths );
406     }
407 
408     public void testIsSymbolicLink()
409         throws IOException
410     {
411         // TODO: Uncomment when PR #25 merged
412         // if ( !checkTestFilesSymlinks() )
413         // {
414         // return;
415         // }
416 
417         final File directory = new File( "src/test/resources/symlinks/src" );
418         DirectoryScanner ds = new DirectoryScanner();
419         assertTrue( ds.isSymbolicLink( directory, "symR" ) );
420         assertTrue( ds.isSymbolicLink( directory, "symDir" ) );
421         assertFalse( ds.isSymbolicLink( directory, "fileR.txt" ) );
422         assertFalse( ds.isSymbolicLink( directory, "aRegularDir" ) );
423     }
424 
425     public void testIsParentSymbolicLink()
426         throws IOException
427     {
428         // TODO: Uncomment when PR #25 merged
429         // if ( !checkTestFilesSymlinks() )
430         // {
431         // return;
432         // }
433 
434         final File directory = new File( "src/test/resources/symlinks/src" );
435         DirectoryScanner ds = new DirectoryScanner();
436         assertFalse( ds.isParentSymbolicLink( directory, "symR" ) );
437         assertFalse( ds.isParentSymbolicLink( directory, "symDir" ) );
438         assertFalse( ds.isParentSymbolicLink( directory, "fileR.txt" ) );
439         assertFalse( ds.isParentSymbolicLink( directory, "aRegularDir" ) );
440         assertFalse( ds.isParentSymbolicLink( new File( directory, "aRegularDir" ), "aRegulatFile.txt" ) );
441         assertTrue( ds.isParentSymbolicLink( new File( directory, "symDir" ), "targetFile.txt" ) );
442         assertTrue( ds.isParentSymbolicLink( new File( directory, "symLinkToDirOnTheOutside" ),
443                                              "FileInDirOnTheOutside.txt" ) );
444     }
445 
446     private void printTestHeader()
447     {
448         StackTraceElement ste = new Throwable().getStackTrace()[1];
449         System.out.println( "Test: " + ste.getMethodName() );
450     }
451 
452     private void assertInclusionsAndExclusions( String[] files, String[] excludedPaths, String... includedPaths )
453     {
454         Arrays.sort( files );
455 
456         System.out.println( "Included files: " );
457         for ( String file : files )
458         {
459             System.out.println( file );
460         }
461 
462         List<String> failedToExclude = new ArrayList<String>();
463         for ( String excludedPath : excludedPaths )
464         {
465             String alt = excludedPath.replace( '/', '\\' );
466             System.out.println( "Searching for exclusion as: " + excludedPath + "\nor: " + alt );
467             if ( Arrays.binarySearch( files, excludedPath ) > -1 || Arrays.binarySearch( files, alt ) > -1 )
468             {
469                 failedToExclude.add( excludedPath );
470             }
471         }
472 
473         List<String> failedToInclude = new ArrayList<String>();
474         for ( String includedPath : includedPaths )
475         {
476             String alt = includedPath.replace( '/', '\\' );
477             System.out.println( "Searching for inclusion as: " + includedPath + "\nor: " + alt );
478             if ( Arrays.binarySearch( files, includedPath ) < 0 && Arrays.binarySearch( files, alt ) < 0 )
479             {
480                 failedToInclude.add( includedPath );
481             }
482         }
483 
484         StringBuilder buffer = new StringBuilder();
485         if ( !failedToExclude.isEmpty() )
486         {
487             buffer.append( "Should NOT have included:\n" ).append( StringUtils.join( failedToExclude.iterator(),
488                                                                                      "\n\t- " ) );
489         }
490 
491         if ( !failedToInclude.isEmpty() )
492         {
493             if ( buffer.length() > 0 )
494             {
495                 buffer.append( "\n\n" );
496             }
497 
498             buffer.append( "Should have included:\n" ).append( StringUtils.join( failedToInclude.iterator(),
499                                                                                  "\n\t- " ) );
500         }
501 
502         if ( buffer.length() > 0 )
503         {
504             fail( buffer.toString() );
505         }
506     }
507 
508     private void createFiles( File dir, String... paths )
509         throws IOException
510     {
511         for ( String path1 : paths )
512         {
513             String path = path1.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
514             File file = new File( dir, path );
515 
516             if ( path.endsWith( File.separator ) )
517             {
518                 file.mkdirs();
519             }
520             else
521             {
522                 if ( file.getParentFile() != null )
523                 {
524                     file.getParentFile().mkdirs();
525                 }
526 
527                 createFile( file, 0 );
528             }
529         }
530     }
531 }