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