1   package org.apache.rat.mp;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements.  See the NOTICE file distributed with
6    * this work for additional information regarding copyright ownership.
7    * The ASF licenses this file to You under the Apache License, Version 2.0
8    * (the "License"); you may not use this file except in compliance with
9    * the License.  You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  import java.io.BufferedReader;
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.FileOutputStream;
24  import java.io.FileReader;
25  import java.io.IOException;
26  import java.io.InputStreamReader;
27  import java.lang.reflect.InvocationHandler;
28  import java.lang.reflect.Method;
29  import java.lang.reflect.Proxy;
30  
31  import org.apache.maven.artifact.factory.ArtifactFactory;
32  import org.apache.maven.artifact.factory.DefaultArtifactFactory;
33  import org.apache.maven.artifact.repository.ArtifactRepository;
34  import org.apache.maven.artifact.repository.DefaultArtifactRepository;
35  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
36  import org.apache.maven.artifact.resolver.ArtifactResolver;
37  import org.apache.maven.artifact.resolver.DefaultArtifactResolver;
38  import org.apache.maven.doxia.siterenderer.Renderer;
39  import org.apache.maven.model.Build;
40  import org.apache.maven.plugin.testing.AbstractMojoTestCase;
41  import org.apache.maven.plugin.testing.stubs.MavenProjectStub;
42  import org.apache.maven.settings.Settings;
43  import org.apache.maven.settings.io.xpp3.SettingsXpp3Reader;
44  import org.apache.rat.mp.AbstractRatMojo;
45  import org.apache.rat.mp.RatCheckException;
46  import org.apache.rat.mp.RatCheckMojo;
47  import org.apache.rat.mp.RatReportMojo;
48  import org.codehaus.plexus.util.DirectoryScanner;
49  
50  
51  /**
52   * Test case for the {@link RatCheckMojo}.
53   */
54  public class RatCheckMojoTest extends AbstractMojoTestCase
55  {
56      /**
57       * Creates a new instance of {@link Renderer}.
58       * @return A configured instance of {@link DefaultRenderer}.
59       * @throws Exception Creating the object failed.
60       */
61      private Renderer newSiteRenderer()
62              throws Exception
63      {
64          return (Renderer) container.lookup( Renderer.ROLE, "default" );
65      }
66  
67      /**
68       * Creates a new instance of {@link ArtifactFactory}.
69       * @return A configured instance of {@link DefaultArtifactFactory}.
70       * @throws Exception Creating the object failed.
71       */
72      private ArtifactFactory newArtifactFactory()
73              throws Exception
74      {
75          final InvocationHandler handler = new InvocationHandler(){
76              public Object invoke( Object pProxy, Method pMethod, Object[] pArgs )
77                  throws Throwable
78              {
79                  System.out.println("Invoking method " + pMethod);
80                  throw new IllegalStateException( "Not implemented" );
81              }
82          };
83          return (ArtifactFactory)
84              Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(),
85                                      new Class[]{ ArtifactFactory.class },
86                                      handler );
87      }
88  
89      /**
90       * Creates a new instance of {@link ArtifactResolver}.
91       * @return A configured instance of {@link DefaultArtifactResolver}.
92       * @throws Exception Creating the object failed.
93       */
94      private ArtifactResolver newArtifactResolver()
95              throws Exception
96      {
97          final InvocationHandler handler = new InvocationHandler(){
98              public Object invoke( Object pProxy, Method pMethod, Object[] pArgs )
99                  throws Throwable
100             {
101                 System.out.println("Invoking method " + pMethod);
102                 throw new IllegalStateException( "Not implemented" );
103             }
104         };
105         return (ArtifactResolver)
106             Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(),
107                                     new Class[]{ ArtifactResolver.class },
108                                     handler );
109     }
110 
111     /**
112      * Creates an instance of {@link ArtifactRepository}.
113      * @return A configured instance of {@link DefaultArtifactRepository}.
114      * @throws Exception Creating the object failed.
115      */
116     private ArtifactRepository newArtifactRepository()
117             throws Exception
118     {
119         File m2Dir = new File( System.getProperty( "user.home" ), ".m2" );
120         File settingsFile = new File( m2Dir, "settings.xml" );
121         String localRepo = null;
122         if ( settingsFile.exists() )
123         {
124             Settings settings = new SettingsXpp3Reader().read( new FileReader( settingsFile ) );
125             localRepo = settings.getLocalRepository();
126         }
127         if ( localRepo == null )
128         {
129             localRepo = System.getProperty( "user.home" ) + "/.m2/repository";
130         }
131         ArtifactRepositoryLayout repositoryLayout =
132             (ArtifactRepositoryLayout) container.lookup(ArtifactRepositoryLayout.ROLE, "default" );
133         return new DefaultArtifactRepository( "local", "file://" + localRepo, repositoryLayout );
134     }
135 
136     /**
137      * Creates a new instance of {@link RatCheckMojo}.
138      * @param pDir The directory, where to look for a pom.xml file.
139      * @return The configured Mojo.
140      * @throws Exception An error occurred while creating the Mojo.
141      */
142     private RatCheckMojo newRatCheckMojo( String pDir )
143             throws Exception
144     {
145         return (RatCheckMojo) newRatMojo( pDir, "check", false );
146     }
147 
148     /**
149      * Creates a new instance of {@link AbstractRatMojo}.
150      * @param pDir The directory, where to look for a pom.xml file.
151      * @param pGoal The goal, which the Mojo must implement.
152      * @return The configured Mojo.
153      * @throws Exception An error occurred while creating the Mojo.
154      */
155     private AbstractRatMojo newRatMojo( String pDir, String pGoal, boolean pCreateCopy )
156             throws Exception
157     {
158         final File baseDir = new File( getBasedir() );
159         final File testBaseDir = getSourceDirectory(pDir, pCreateCopy, baseDir);
160         File testPom = new File( testBaseDir, "pom.xml" );
161         AbstractRatMojo mojo = (AbstractRatMojo) lookupMojo( pGoal, testPom );
162         assertNotNull( mojo );
163         final File buildDirectory = new File( new File( baseDir, "target/test" ), pDir );
164         setVariableValueToObject( mojo, "basedir", testBaseDir );
165         setVariableValueToObject( mojo, "addDefaultLicenseMatchers", Boolean.TRUE );
166         setVariableValueToObject( mojo, "useDefaultExcludes", Boolean.TRUE );
167         setVariableValueToObject( mojo, "useMavenDefaultExcludes", Boolean.TRUE );
168         setVariableValueToObject( mojo, "useEclipseDefaultExcludes", Boolean.TRUE );
169         setVariableValueToObject( mojo, "addLicenseHeaders", "false" );
170         final Build build = new Build();
171         build.setDirectory( buildDirectory.getPath() );
172         final MavenProjectStub project = new MavenProjectStub(){
173             public Build getBuild()
174             {
175                 return build;
176             }
177         };
178         setVariableValueToObject( mojo, "project", project );
179         if (mojo instanceof RatReportMojo)
180         {
181             setVariableValueToObject( mojo, "localRepository", newArtifactRepository() );
182             setVariableValueToObject( mojo, "resolver", newArtifactResolver() );
183             setVariableValueToObject( mojo, "factory", newArtifactFactory() );
184             setVariableValueToObject( mojo, "siteRenderer", newSiteRenderer() );
185         }
186         else if ( mojo instanceof RatCheckMojo )
187         {
188             final File ratTxtFile = new File( buildDirectory, "rat.txt" );
189             setVariableValueToObject( mojo, "reportFile", ratTxtFile );
190         }
191         return mojo;
192     }
193 
194     private File getSourceDirectory(String pDir, boolean pCreateCopy,
195             final File baseDir) throws IOException {
196         return makeSourceDirectory( new File( new File( baseDir, "src/test" ), pDir ), pDir, pCreateCopy );
197     }
198 
199     private void remove( File pDir ) throws IOException {
200         if ( pDir.isFile() )
201         {
202             if ( ! pDir.delete() )
203             {
204                 throw new IOException( "Unable to delete file: " + pDir );
205             }
206         }
207         else if ( pDir.isDirectory() )
208         {
209             final File[] files = pDir.listFiles();
210             for ( int i = 0;  i < files.length;  i++ )
211             {
212                 remove( files[i] );
213             }
214             if ( ! pDir.delete() )
215             {
216                 throw new IOException( "Unable to delete directory: " + pDir );
217             }
218         }
219         else if ( pDir.exists() )
220         {
221             throw new IOException( "Unable to delete unknown object " + pDir );
222         }
223     }
224 
225     private void copy( File pSource, File pTarget ) throws IOException
226     {
227         if ( pSource.isDirectory() )
228         {
229             if ( !pTarget.isDirectory()  &&  !pTarget.mkdirs() ) {
230                 throw new IOException("Unable to create directory: " + pTarget);
231             }
232             final DirectoryScanner scanner = new DirectoryScanner();
233             scanner.setBasedir(pSource);
234             scanner.addDefaultExcludes();
235             scanner.setIncludes(new String[]{"*"});
236             scanner.scan();
237             final String[] dirs = scanner.getIncludedDirectories();
238             
239             for (int i = 0;  i < dirs.length;  i++) {
240                 final String dir = dirs[i];
241                 if (!"".equals(dir)) {
242                     copy( new File(pSource, dir), new File(pTarget, dir));
243                 }
244             }
245             final String[] files = scanner.getIncludedFiles();
246             for (int i = 0;  i < files.length;  i++) {
247                 copy( new File(pSource, files[i]), new File(pTarget, files[i]));
248             }
249         }
250         else if ( pSource.isFile() )
251         {
252             final FileInputStream fis = new FileInputStream( pSource );
253             final FileOutputStream fos = new FileOutputStream( pTarget );
254             final byte[] buffer = new byte[8192];
255             for ( ;; )
256             {
257                 int res = fis.read(buffer);
258                 if (res == -1) {
259                     break;
260                 }
261                 if (res > 0) {
262                     fos.write(buffer, 0, res);
263                 }
264             }
265             fos.close();
266             fis.close();
267         }
268         else
269         {
270             throw new IOException( "Unable to copy unknown object " + pSource );
271         }
272     }
273     
274     private File makeSourceDirectory(File pFile, String pDir, boolean pCreateCopy) throws IOException {
275         if ( ! pCreateCopy )
276         {
277             return pFile;
278         }
279 
280         final File baseDir = new File( getBasedir() );
281         final File targetDir = new File( new File( baseDir, "target/it-source" ), pDir );
282         remove( targetDir );
283         copy( pFile, targetDir );
284         return targetDir;
285     }
286 
287     /**
288      * Reads the location of the rat text file from the Mojo.
289      * @param pMojo The configured Mojo.
290      * @return Value of the "reportFile" property.
291      * @throws Exception An error occurred while reading the property.
292      */
293     private File getRatTxtFile( RatCheckMojo pMojo )
294             throws Exception
295     {
296         return (File) getVariableValueFromObject( pMojo, "reportFile" );
297     }
298 
299     /**
300      * Runs a check, which should expose no problems.
301      * @throws Exception The test failed.
302      */
303     public void testIt1() throws Exception {
304         final RatCheckMojo mojo = newRatCheckMojo( "it1" );
305         final File ratTxtFile = getRatTxtFile(mojo);
306         mojo.execute();
307         checkResult( ratTxtFile, 1, 0 );
308     }
309 
310     /**
311      * Reads the created report file and verifies, whether the detected numbers
312      * are matching.
313      * @param pRatTxtFile The file to read.
314      * @param pNumAslFiles The number of files with ASL.
315      * @param pNumNoLicenseFiles The number of files without license.
316      * @throws IOException An error occurred while reading the file.
317      */
318     private void checkResult( File pRatTxtFile, int pNumAslFiles, int pNumNoLicenseFiles )
319             throws IOException
320     {
321         assertTrue( pRatTxtFile.exists() );
322         BufferedReader reader = new BufferedReader( new FileReader( pRatTxtFile ) );
323         Integer numAslFiles = null;
324         Integer numNoLicenseFiles = null;
325         for (;;)
326         {
327             String line = reader.readLine();
328             if ( line == null )
329             {
330                 break;
331             }
332             int offset = line.indexOf( "Apache Licensed: " );
333             if ( offset >= 0 )
334             {
335                 numAslFiles = new Integer( line.substring( offset + "Apache Licensed: ".length() ).trim() );
336             }
337             offset = line.indexOf( "Unknown Licenses" );
338             if ( offset >= 0 )
339             {
340                 numNoLicenseFiles = new Integer( line.substring( 0, offset ).trim() );
341             }
342         }
343         reader.close();
344         assertEquals( new Integer( pNumAslFiles), numAslFiles );
345         assertEquals( new Integer( pNumNoLicenseFiles ), numNoLicenseFiles );
346     }
347 
348     /**
349      * Runs a check, which should detect a problem.
350      * @throws Exception The test failed.
351      */
352     public void testIt2() throws Exception {
353         final RatCheckMojo mojo = newRatCheckMojo( "it2" );
354         final File ratTxtFile = getRatTxtFile(mojo);
355         try
356         {
357             mojo.execute();
358             fail( "Expected RatCheckException" );
359         }
360         catch ( RatCheckException e )
361         {
362             // Ok
363         }
364         checkResult( ratTxtFile, 1, 1 );
365     }
366 
367     private String getFirstLine(File pFile) throws IOException {
368         final FileInputStream fis = new FileInputStream(pFile);
369         final InputStreamReader reader = new InputStreamReader(fis, "UTF8");
370         final BufferedReader breader = new BufferedReader(reader);
371         final String result = breader.readLine();
372         breader.close();
373         return result;
374     }
375     
376     /**
377      * Tests adding license headers.
378      */
379     public void testIt3() throws Exception {
380         final RatCheckMojo mojo = (RatCheckMojo) newRatMojo( "it3", "check", true );
381         setVariableValueToObject( mojo, "addLicenseHeaders", "true" );
382         setVariableValueToObject( mojo, "numUnapprovedLicenses", new Integer(1));
383         mojo.execute();
384         final File ratTxtFile = getRatTxtFile( mojo );
385         checkResult( ratTxtFile, 1, 1 );
386 
387         final File baseDir = new File( getBasedir() );
388         final File sourcesDir = new File( new File( baseDir, "target/it-source" ), "it3" );
389         final String firstLineOrig = getFirstLine(new File(sourcesDir, "src.apt"));
390         assertTrue(firstLineOrig.indexOf("--") != -1);
391         assertTrue(firstLineOrig.indexOf("~~") == -1);
392         final String firstLineModified = getFirstLine(new File(sourcesDir, "src.apt.new"));
393         assertTrue(firstLineModified.indexOf("--") == -1);
394         assertTrue(firstLineModified.indexOf("~~") != -1);
395     }
396 }