View Javadoc
1   package org.apache.maven.shared.utils.io;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.shared.utils.Os;
23  import org.apache.maven.shared.utils.testhelpers.FileTestHelper;
24  import org.junit.Assert;
25  import org.junit.Ignore;
26  import org.junit.Rule;
27  import org.junit.Test;
28  import org.junit.rules.TemporaryFolder;
29  
30  import java.io.File;
31  import java.io.IOException;
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.List;
35  
36  import static org.junit.Assert.assertEquals;
37  import static org.junit.Assert.assertTrue;
38  import static org.junit.Assume.assumeFalse;
39  
40  @SuppressWarnings( "deprecation" )
41  public class DirectoryScannerTest
42  {
43      private static final String[] NONE = new String[0];
44  
45      @Rule
46      public TemporaryFolder tempFolder = new TemporaryFolder();
47  
48      private void createTestData()
49          throws IOException
50      {
51          File rootDir = tempFolder.getRoot();
52          File folder1 = new File( rootDir, "folder1" );
53          if ( !folder1.mkdirs() )
54          {
55              Assert.fail();
56          }
57  
58          FileTestHelper.generateTestFile( new File( rootDir, "file1.txt" ), 11 );
59          FileTestHelper.generateTestFile( new File( rootDir, "file2.txt" ), 12 );
60          FileTestHelper.generateTestFile( new File( rootDir, "file3.dat" ), 13 );
61  
62          FileTestHelper.generateTestFile( new File( folder1, "file4.txt" ), 14 );
63          FileTestHelper.generateTestFile( new File( folder1, "file5.dat" ), 15 );
64  
65          File folder2 = new File( folder1, "ignorefolder" );
66          if ( !folder2.mkdirs() )
67          {
68              Assert.fail();
69          }
70          FileTestHelper.generateTestFile( new File( folder2, "file7.txt" ), 17 );
71      }
72  
73      @Test
74      public void testSimpleScan()
75          throws Exception
76      {
77          createTestData();
78  
79          fitScanTest( true, true, true,
80                  /* includes */        null,
81                  /* excludes */        null,
82                  /* expInclFiles */
83                  new String[]{ "file1.txt", "file2.txt", "file3.dat", "folder1/file4.txt", "folder1/file5.dat" },
84                  /* expInclDirs */     new String[]{ "", "folder1" },
85                  /* expNotInclFiles */ NONE,
86                  /* expNotInclDirs  */ NONE,
87                  /* expNotExclFiles */ NONE,
88                  /* expNotExclDirs  */ NONE );
89  
90          // same without followSymlinks
91          fitScanTest( true, false, true,
92                  /* includes */        null,
93                  /* excludes */        null,
94                  /* expInclFiles */
95                  new String[]{ "file1.txt", "file2.txt", "file3.dat", "folder1/file4.txt", "folder1/file5.dat" },
96                  /* expInclDirs */     new String[]{ "", "folder1" },
97                  /* expNotInclFiles */ NONE,
98                  /* expNotInclDirs  */ NONE,
99                  /* expNotExclFiles */ NONE,
100                 /* expNotExclDirs  */ NONE );
101     }
102 
103     @Test
104     public void testSimpleIncludes()
105         throws Exception
106     {
107         createTestData();
108 
109         fitScanTest( true, true, true,
110                 /* includes        */ new String[]{ "**/*.dat", "*.somethingelse" },
111                 /* excludes        */ null,
112                 /* expInclFiles    */ new String[]{ "file3.dat", "folder1/file5.dat" },
113                 /* expInclDirs     */ NONE,
114                 /* expNotInclFiles */ new String[]{ "file1.txt", "file2.txt", "folder1/file4.txt" },
115                 /* expNotInclDirs  */ new String[]{ "", "folder1" },
116                 /* expExclFiles    */ NONE,
117                 /* expExclDirs     */ NONE );
118 
119         // same without followSymlinks
120         fitScanTest( true, false, true,
121                 /* includes        */ new String[]{ "**/*.dat", "*.somethingelse" },
122                 /* excludes        */ null,
123                 /* expInclFiles    */ new String[]{ "file3.dat", "folder1/file5.dat" },
124                 /* expInclDirs     */ NONE,
125                 /* expNotInclFiles */ new String[]{ "file1.txt", "file2.txt", "folder1/file4.txt" },
126                 /* expNotInclDirs  */ new String[]{ "", "folder1" },
127                 /* expExclFiles    */ NONE,
128                 /* expExclDirs     */ NONE );
129     }
130 
131     @Test
132     public void checkSymlinkBehaviour()
133     {
134         DirectoryScanner ds = new DirectoryScanner();
135         ds.setBasedir( new File( "src/test/resources/symlinks/src" ) );
136         ds.setFollowSymlinks( false );
137         ds.scan();
138         
139         String[] includedDirectories = ds.getIncludedDirectories();
140         // FIXME 3 (Windows) and 5 (Linux) are both wrong. The correct answer is 4.
141         // This method is broken in different ways on different operating systems.
142         assertTrue( includedDirectories.length == 3 || includedDirectories.length == 5);  
143         
144         String[] files = ds.getIncludedFiles();
145         assertAlwaysIncluded( Arrays.asList( files ) );
146         
147         // FIXME getIncludedFiles is broken on Windows; correct answer is 9
148         assertTrue("files.length is " + files.length, files.length == 9 || files.length == 11 );
149     }
150 
151     @Test
152     public void followSymlinksFalse()
153         throws IOException
154     {
155         assumeFalse( Os.isFamily( Os.FAMILY_WINDOWS ) );
156 
157         File testDir = SymlinkTestSetup.createStandardSymlinkTestDir( new File( "target/test/symlinkTestCase" ) );
158 
159         DirectoryScanner ds = new DirectoryScanner();
160         ds.setBasedir( testDir );
161         ds.setFollowSymlinks( false );
162         ds.scan();
163         List<String> included = Arrays.asList( ds.getIncludedFiles() );
164         assertAlwaysIncluded( included );
165         assertEquals( 9, included.size() );
166         List<String> includedDirs = Arrays.asList( ds.getIncludedDirectories() );
167         assertTrue( includedDirs.contains( "" ) );
168         assertTrue( includedDirs.contains( "aRegularDir" ) );
169         assertTrue( includedDirs.contains( "symDir" ) );
170         assertTrue( includedDirs.contains( "symLinkToDirOnTheOutside" ) );
171         assertTrue( includedDirs.contains( "targetDir" ) );
172         assertEquals( 5, includedDirs.size() );
173     }
174 
175     private void assertAlwaysIncluded( List<String> included )
176     {
177         assertTrue( included.contains( "aRegularDir" + File.separator + "aRegularFile.txt" ) );
178         assertTrue( included.contains( "targetDir" + File.separator + "targetFile.txt" ) );
179         assertTrue( included.contains( "fileR.txt" ) );
180         assertTrue( included.contains( "fileW.txt" ) );
181         assertTrue( included.contains( "fileX.txt" ) );
182         assertTrue( included.contains( "symR" ) );
183         assertTrue( included.contains( "symW" ) );
184         assertTrue( included.contains( "symX" ) );
185         assertTrue( included.contains( "symLinkToFileOnTheOutside" ) );
186     }
187 
188     @Test
189     public void followSymlinks()
190         throws IOException
191     {
192         assumeFalse( Os.isFamily( Os.FAMILY_WINDOWS ) );
193 
194         DirectoryScanner ds = new DirectoryScanner();
195         File testDir = SymlinkTestSetup.createStandardSymlinkTestDir( new File( "target/test/symlinkTestCase" ) );
196 
197         ds.setBasedir( testDir );
198         ds.setFollowSymlinks( true );
199         ds.scan();
200         List<String> included = Arrays.asList( ds.getIncludedFiles() );
201         assertAlwaysIncluded( included );
202         assertTrue( included.contains( "symDir/targetFile.txt" ) );
203         assertTrue( included.contains( "symLinkToDirOnTheOutside/FileInDirOnTheOutside.txt" ) );
204         assertEquals( 11, included.size() );
205 
206         List<String> includedDirs = Arrays.asList( ds.getIncludedDirectories() );
207         assertTrue( includedDirs.contains( "" ) ); // w00t !
208         assertTrue( includedDirs.contains( "aRegularDir" ) );
209         assertTrue( includedDirs.contains( "symDir" ) );
210         assertTrue( includedDirs.contains( "symLinkToDirOnTheOutside" ) );
211         assertTrue( includedDirs.contains( "targetDir" ) );
212         assertEquals( 5, includedDirs.size() );
213     }
214 
215     /*
216         Creates a standard directory layout with symlinks and files.
217      */
218     @Test
219     public void testSimpleExcludes()
220         throws Exception
221     {
222         createTestData();
223 
224         fitScanTest( true, true, true,
225                 /* includes        */ null,
226                 /* excludes        */ new String[]{ "**/*.dat", "*.somethingelse" },
227                 /* expInclFiles    */ new String[]{ "file1.txt", "file2.txt", "folder1/file4.txt" },
228                 /* expInclDirs     */ new String[]{ "", "folder1" },
229                 /* expNotInclFiles */ NONE,
230                 /* expNotInclDirs  */ NONE,
231                 /* expExclFiles    */ new String[]{ "file3.dat", "folder1/file5.dat" },
232                 /* expExclDirs     */ NONE );
233 
234         // same without followSymlinks
235         fitScanTest( true, false, true,
236                 /* includes        */ null,
237                 /* excludes        */ new String[]{ "**/*.dat", "*.somethingelse" },
238                 /* expInclFiles    */ new String[]{ "file1.txt", "file2.txt", "folder1/file4.txt" },
239                 /* expInclDirs     */ new String[]{ "", "folder1" },
240                 /* expNotInclFiles */ NONE,
241                 /* expNotInclDirs  */ NONE,
242                 /* expExclFiles    */ new String[]{ "file3.dat", "folder1/file5.dat" },
243                 /* expExclDirs     */ NONE );
244     }
245 
246     public void testIsSymbolicLink()
247         throws IOException
248     {
249         File file = new File( "." );
250         DirectoryScanner ds = new DirectoryScanner();
251         ds.isSymbolicLink( file, "abc" );
252     }
253 
254     /**
255      * Performs a scan and test for the given parameters if not null.
256      */
257     private void fitScanTest( boolean caseSensitive, boolean followSymLinks, boolean addDefaultExcludes,
258                               String[] includes, String[] excludes, String[] expectedIncludedFiles,
259                               String[] expectedIncludedDirectories, String[] expectedNotIncludedFiles,
260                               String[] expectedNotIncludedDirectories, String[] expectedExcludedFiles,
261                               String[] expectedExcludedDirectories )
262     {
263         DirectoryScanner ds = new DirectoryScanner();
264         ds.setBasedir( tempFolder.getRoot() );
265 
266         ds.setCaseSensitive( caseSensitive );
267         ds.setFollowSymlinks( followSymLinks );
268 
269         if ( addDefaultExcludes )
270         {
271             ds.addDefaultExcludes();
272         }
273         if ( includes != null )
274         {
275             ds.setIncludes( includes );
276         }
277         if ( excludes != null )
278         {
279             ds.setExcludes( excludes );
280         }
281 
282         TestScanConductor scanConductor = new TestScanConductor();
283 
284         ds.setScanConductor( scanConductor );
285 
286         ds.scan();
287 
288         checkFiles( "expectedIncludedFiles", expectedIncludedFiles, ds.getIncludedFiles() );
289         checkFiles( "expectedIncludedDirectories", expectedIncludedDirectories, ds.getIncludedDirectories() );
290         checkFiles( "expectedNotIncludedFiles", expectedNotIncludedFiles, ds.getNotIncludedFiles() );
291         checkFiles( "expectedNotIncludedDirectories", expectedNotIncludedDirectories, ds.getNotIncludedDirectories() );
292         checkFiles( "expectedExcludedFiles", expectedExcludedFiles, ds.getExcludedFiles() );
293         checkFiles( "expectedExcludedDirectories", expectedExcludedDirectories, ds.getExcludedDirectories() );
294 
295         checkFiles( "visitedFiles", expectedIncludedFiles,
296                     scanConductor.visitedFiles.toArray( new String[scanConductor.visitedFiles.size()] ) );
297     }
298 
299     /**
300      * Check if the resolved files match the rules of the expected files.
301      *
302      * @param expectedFiles
303      * @param resolvedFiles
304      */
305     private void checkFiles( String category, String[] expectedFiles, String[] resolvedFiles )
306     {
307         if ( expectedFiles != null )
308         {
309             String msg = category + " expected: " + Arrays.toString( expectedFiles ) + " but got: " + Arrays.toString(
310                 resolvedFiles );
311             Assert.assertNotNull( msg, resolvedFiles );
312             assertEquals( msg, expectedFiles.length, resolvedFiles.length );
313 
314             Arrays.sort( expectedFiles );
315             Arrays.sort( resolvedFiles );
316 
317             for ( int i = 0; i < resolvedFiles.length; i++ )
318             {
319                 assertEquals( msg, expectedFiles[i], resolvedFiles[i].replace( "\\", "/" ) );
320             }
321         }
322     }
323 
324     private static class TestScanConductor
325         implements ScanConductor
326     {
327         final List<String> visitedFiles = new ArrayList<String>();
328 
329         public ScanConductor.ScanAction visitDirectory( String name, File directory )
330         {
331             assertTrue( directory.isDirectory() );
332 
333             if ( directory.getName().equals( "ignorefolder" ) )
334             {
335                 return ScanAction.NO_RECURSE;
336             }
337 
338             return ScanAction.CONTINUE;
339         }
340 
341         public ScanConductor.ScanAction visitFile( String name, File file )
342         {
343             assertTrue( file.isFile() );
344             visitedFiles.add( name );
345             return ScanAction.CONTINUE;
346         }
347     }
348 
349     private void removeAndAddSomeFiles()
350         throws IOException
351     {
352         File rootDir = tempFolder.getRoot();
353         File file2 = new File( rootDir, "file2.txt" );
354         file2.delete();
355 
356         FileTestHelper.generateTestFile( new File( rootDir, "folder1/file9.txt" ), 15 );
357 
358         File folder2 = new File( rootDir, "folder1/ignorefolder" );
359         FileUtils.deleteDirectory( folder2 );
360     }
361 
362     @Test
363     public void testScanDiff()
364         throws Exception
365     {
366         createTestData();
367 
368         DirectoryScanner dss = new DirectoryScanner();
369         dss.setBasedir( tempFolder.getRoot() );
370         Assert.assertNotNull( dss );
371 
372         // we take the initial snapshot
373         dss.scan();
374         String[] oldFiles = dss.getIncludedFiles();
375 
376         // now we change 3 files. add one and remove
377         removeAndAddSomeFiles();
378 
379         dss.scan();
380 
381         DirectoryScanResult dsr = dss.diffIncludedFiles( oldFiles );
382 
383         String[] addedFiles = dsr.getFilesAdded();
384         String[] removedFiles = dsr.getFilesRemoved();
385         Assert.assertNotNull( addedFiles );
386         Assert.assertNotNull( removedFiles );
387         assertEquals( 1, addedFiles.length );
388         assertEquals( 2, removedFiles.length );
389     }
390 
391     @Ignore( "Enable this test to run performance checks" )
392     @Test
393     public void performanceTest()
394         throws Exception
395     {
396 
397         File rootFolder = tempFolder.getRoot();
398 
399         // do some warmup
400         for ( int i = 1; i < 200; i++ )
401         {
402             createTestData();
403             removeAndAddSomeFiles();
404             FileUtils.deleteDirectory( rootFolder );
405         }
406 
407         int cycles = 2000;
408 
409         // and now we take the time _without_
410         long startTime = System.nanoTime();
411         for ( int i = 1; i < cycles; i++ )
412         {
413             createTestData();
414             removeAndAddSomeFiles();
415             FileUtils.deleteDirectory( rootFolder );
416             rootFolder.mkdir();
417         }
418         long endTime = System.nanoTime();
419 
420         long durationEmptyRun = endTime - startTime;
421         System.out.println( "durationEmptyRun            [ns]: " + durationEmptyRun );
422 
423         startTime = System.nanoTime();
424         for ( int i = 1; i < cycles; i++ )
425         {
426             createTestData();
427             DirectoryScanner directoryScanner = new DirectoryScanner();
428             directoryScanner.setBasedir( rootFolder );
429             directoryScanner.scan();
430             String[] oldFiles = directoryScanner.getIncludedFiles();
431 
432             removeAndAddSomeFiles();
433 
434             directoryScanner.scan();
435 
436             DirectoryScanResult directoryScanResult = directoryScanner.diffIncludedFiles( oldFiles );
437             Assert.assertNotNull( directoryScanResult );
438 
439             FileUtils.deleteDirectory( rootFolder );
440             rootFolder.mkdir();
441         }
442         endTime = System.nanoTime();
443 
444         long durationWithSnapshotScanner = endTime - startTime;
445         System.out.println( "durationWithSnapshotScanner [ns]: " + durationWithSnapshotScanner );
446 
447         long dirScannerOverhead = durationWithSnapshotScanner - durationEmptyRun;
448 
449         System.out.println( "Overhead for n cycles [ns]: " + dirScannerOverhead );
450     }
451 
452 }