Coverage Report - org.apache.maven.archetype.mojos.IntegrationTestMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
IntegrationTestMojo
0 %
0/110
0 %
0/42
4,9
IntegrationTestMojo$IntegrationTestFailure
0 %
0/12
N/A
4,9
 
 1  
 package org.apache.maven.archetype.mojos;
 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.commons.collections.CollectionUtils;
 23  
 import org.apache.maven.archetype.ArchetypeGenerationRequest;
 24  
 import org.apache.maven.archetype.ArchetypeGenerationResult;
 25  
 import org.apache.maven.archetype.common.Constants;
 26  
 import org.apache.maven.archetype.exception.ArchetypeNotConfigured;
 27  
 import org.apache.maven.archetype.generator.ArchetypeGenerator;
 28  
 import org.apache.maven.plugin.AbstractMojo;
 29  
 import org.apache.maven.plugin.MojoExecutionException;
 30  
 import org.apache.maven.plugin.MojoFailureException;
 31  
 import org.apache.maven.project.MavenProject;
 32  
 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
 33  
 import org.apache.maven.shared.invoker.InvocationRequest;
 34  
 import org.apache.maven.shared.invoker.InvocationResult;
 35  
 import org.apache.maven.shared.invoker.Invoker;
 36  
 import org.apache.maven.shared.invoker.MavenInvocationException;
 37  
 import org.codehaus.plexus.util.FileUtils;
 38  
 import org.codehaus.plexus.util.IOUtil;
 39  
 import org.codehaus.plexus.util.StringUtils;
 40  
 
 41  
 import java.io.File;
 42  
 import java.io.FileInputStream;
 43  
 import java.io.FileNotFoundException;
 44  
 import java.io.IOException;
 45  
 import java.io.InputStream;
 46  
 import java.io.StringWriter;
 47  
 import java.util.Arrays;
 48  
 import java.util.List;
 49  
 import java.util.Properties;
 50  
 
 51  
 /**
 52  
  * <p>Execute the archetype integration tests, consisting in generating projects from the current archetype and
 53  
  * optionally comparing generated projects with reference copy.</p>
 54  
  * 
 55  
  * <p>Each IT consists of a sub-directory in <code>src/test/resources/projects</code> containing:</p>
 56  
  * <ul>
 57  
  * <li>a <code>goal.txt</code> file, containing a list of goals to run against the generated project (can be empty,
 58  
  * content ignored before maven-archetype-plugin 2.1),</li>
 59  
  * <li>an <code>archetype.properties</code> file, containing properties for project generation,</li>
 60  
  * <li>an optional <code>reference/</code> directory containing a reference copy of the expected project created from the IT.</li>
 61  
  * </ul>
 62  
  *
 63  
  * Notice that it is expected to be run as part as of a build after the <code>package</code> phase and not directly
 64  
  * as a goal from CLI.
 65  
  *
 66  
  * @author rafale
 67  
  * @requiresProject true
 68  
  * @goal integration-test
 69  
  */
 70  0
 public class IntegrationTestMojo
 71  
     extends AbstractMojo
 72  
 {
 73  
     /** @component */
 74  
     private ArchetypeGenerator archetypeGenerator;
 75  
 
 76  
     /** @component */
 77  
     private Invoker invoker;
 78  
 
 79  
     /**
 80  
      * The archetype project to execute the integration tests on.
 81  
      *
 82  
      * @parameter expression="${project}"
 83  
      * @required
 84  
      * @readonly
 85  
      */
 86  
     private MavenProject project;
 87  
 
 88  
     /**
 89  
      * Skip the integration test.
 90  
      *
 91  
      * @parameter expression="${archetype.test.skip}"
 92  
      * @readonly
 93  
      */
 94  0
     private boolean skip = false;
 95  
 
 96  
     public void execute()
 97  
         throws MojoExecutionException, MojoFailureException
 98  
     {
 99  0
         if ( skip )
 100  
         {
 101  0
             return;
 102  
         }
 103  
 
 104  0
         File projectsDirectory = new File( project.getBasedir(), "target/test-classes/projects" );
 105  
 
 106  0
         if ( !projectsDirectory.exists() )
 107  
         {
 108  0
             getLog().warn( "No Archetype IT projects: root 'projects' directory not found." );
 109  
 
 110  0
             return;
 111  
         }
 112  
 
 113  0
         File archetypeFile = project.getArtifact().getFile();
 114  
 
 115  0
         if ( archetypeFile == null )
 116  
         {
 117  0
             throw new MojoFailureException(
 118  
                                             "Unable to get the archetypes' artifact which should have just been built:"
 119  
                                                 + " you probably launched 'mvn archetype:integration-test' instead of"
 120  
                                                 + " 'mvn integration-test'." );
 121  
         }
 122  
 
 123  
         try
 124  
         {
 125  
             @SuppressWarnings( "unchecked" )
 126  0
             List<File> projectsGoalFiles = FileUtils.getFiles( projectsDirectory, "*/goal.txt", "" );
 127  
 
 128  0
             if ( projectsGoalFiles.size() == 0 )
 129  
             {
 130  0
                 getLog().warn( "No Archetype IT projects: no directory with goal.txt found." );
 131  
 
 132  0
                 return;
 133  
             }
 134  
 
 135  0
             StringWriter errorWriter = new StringWriter();
 136  0
             for ( File goalFile : projectsGoalFiles )
 137  
             {
 138  
                 try
 139  
                 {
 140  0
                     processIntegrationTest( goalFile, archetypeFile );
 141  
                 }
 142  0
                 catch ( IntegrationTestFailure ex )
 143  
                 {
 144  0
                     errorWriter.write( "\nArchetype IT '" + goalFile.getParentFile().getName() + "' failed: " );
 145  0
                     errorWriter.write( ex.getMessage() );
 146  0
                 }
 147  
             }
 148  
 
 149  0
             String errors = errorWriter.toString();
 150  0
             if ( !StringUtils.isEmpty( errors ) )
 151  
             {
 152  0
                 throw new MojoExecutionException( errors );
 153  
             }
 154  
         }
 155  0
         catch ( IOException ex )
 156  
         {
 157  0
             throw new MojoFailureException( ex, ex.getMessage(), ex.getMessage() );
 158  0
         }
 159  0
     }
 160  
 
 161  
     /**
 162  
      * Checks that actual directory content is the same as reference.
 163  
      *
 164  
      * @param reference the reference directory
 165  
      * @param actual the actual directory to compare with the reference
 166  
      * @throws IntegrationTestFailure if content differs
 167  
      */
 168  
     private void assertDirectoryEquals( File reference, File actual )
 169  
         throws IntegrationTestFailure, IOException
 170  
     {
 171  
         @SuppressWarnings( "unchecked" )
 172  0
         List<String> referenceFiles = FileUtils.getFileAndDirectoryNames( reference, "**", null, false, true, true, true );
 173  0
         getLog().debug( "reference content: " + referenceFiles );
 174  
 
 175  
         @SuppressWarnings( "unchecked" )
 176  0
         List<String> actualFiles = FileUtils.getFileAndDirectoryNames( actual, "**", null, false, true, true, true );
 177  0
         getLog().debug( "actual content: " + referenceFiles );
 178  
 
 179  0
         boolean fileNamesEquals = CollectionUtils.isEqualCollection( referenceFiles, actualFiles );
 180  
 
 181  0
         if ( !fileNamesEquals )
 182  
         {
 183  0
             getLog().debug( "Actual list of files is not the same as reference:" );
 184  0
             int missing = 0;
 185  0
             for ( String ref : referenceFiles )
 186  
             {
 187  0
                 if ( actualFiles.contains( ref ) )
 188  
                 {
 189  0
                     actualFiles.remove( ref );
 190  0
                     getLog().debug( "Contained " + ref );
 191  
                 }
 192  
                 else
 193  
                 {
 194  0
                     missing++;
 195  0
                     getLog().error( "Not contained " + ref );
 196  
                 }
 197  
             }
 198  0
             getLog().error( "Remains " + actualFiles );
 199  
 
 200  0
             throw new IntegrationTestFailure( "Reference and generated project differs (missing: " + missing
 201  
                 + ", unexpected: " + actualFiles.size() + ")" );
 202  
         }
 203  
 
 204  0
         boolean contentEquals = true;
 205  
 
 206  0
         for ( String file : referenceFiles )
 207  
         {
 208  0
             File referenceFile = new File( reference, file );
 209  0
             File actualFile = new File( actual, file );
 210  
 
 211  0
             if ( referenceFile.isDirectory() )
 212  
             {
 213  0
                 if ( actualFile.isFile() )
 214  
                 {
 215  0
                     getLog().warn( "File " + file + " is a directory in the reference but a file in actual" );
 216  0
                     contentEquals = false;
 217  
                 }
 218  
             }
 219  0
             else if ( actualFile.isDirectory() )
 220  
             {
 221  0
                 if ( referenceFile.isFile() )
 222  
                 {
 223  0
                     getLog().warn( "File " + file + " is a file in the reference but a directory in actual" );
 224  0
                     contentEquals = false;
 225  
                 }
 226  
             }
 227  0
             else if ( !FileUtils.contentEquals( referenceFile, actualFile ) )
 228  
             {
 229  0
                 getLog().warn( "Contents of file " + file + " are not equal" );
 230  0
                 contentEquals = false;
 231  
             }
 232  0
         }
 233  0
         if ( !contentEquals )
 234  
         {
 235  0
             throw new IntegrationTestFailure( "Some content are not equals" );
 236  
         }
 237  0
     }
 238  
 
 239  
     private Properties loadProperties( final File propertiesFile )
 240  
         throws IOException, FileNotFoundException
 241  
     {
 242  0
         Properties properties = new Properties();
 243  
 
 244  0
         InputStream in = null;
 245  
         try
 246  
         {
 247  0
             in = new FileInputStream( propertiesFile );
 248  
 
 249  0
             properties.load( in );
 250  
         }
 251  
         finally
 252  
         {
 253  0
             IOUtil.close( in );
 254  0
         }
 255  
 
 256  0
         return properties;
 257  
     }
 258  
 
 259  
     private void processIntegrationTest( File goalFile, File archetypeFile )
 260  
         throws IntegrationTestFailure
 261  
     {
 262  0
         getLog().info( "Processing Archetype IT project: " + goalFile.getParentFile().getName() );
 263  
 
 264  
         try
 265  
         {
 266  0
             Properties properties = getProperties( goalFile );
 267  
 
 268  0
             String basedir = goalFile.getParentFile().getPath() + "/project";
 269  
 
 270  0
             FileUtils.deleteDirectory( basedir );
 271  
 
 272  0
             FileUtils.mkdir( basedir );
 273  
 
 274  0
             ArchetypeGenerationRequest request = new ArchetypeGenerationRequest()
 275  
                 .setArchetypeGroupId( project.getGroupId() )
 276  
                 .setArchetypeArtifactId( project.getArtifactId() )
 277  
                 .setArchetypeVersion( project.getVersion() )
 278  
                 .setGroupId( properties.getProperty( Constants.GROUP_ID ) )
 279  
                 .setArtifactId( properties.getProperty( Constants.ARTIFACT_ID ) )
 280  
                 .setVersion( properties.getProperty( Constants.VERSION ) )
 281  
                 .setPackage( properties.getProperty( Constants.PACKAGE ) )
 282  
                 .setOutputDirectory( basedir )
 283  
                 .setProperties( properties );
 284  
 
 285  0
             ArchetypeGenerationResult result = new ArchetypeGenerationResult();
 286  
 
 287  0
             archetypeGenerator.generateArchetype( request, archetypeFile, result );
 288  
 
 289  0
             if ( result.getCause() != null )
 290  
             {
 291  0
                 if ( result.getCause() instanceof ArchetypeNotConfigured )
 292  
                 {
 293  0
                     ArchetypeNotConfigured anc = (ArchetypeNotConfigured) result.getCause();
 294  
 
 295  0
                     throw new IntegrationTestFailure( "Missing required properties in archetype.properties: "
 296  
                         + StringUtils.join( anc.getMissingProperties().iterator(), ", " ), anc );
 297  
                 }
 298  
 
 299  0
                 throw new IntegrationTestFailure( result.getCause().getMessage(), result.getCause() );
 300  
             }
 301  
 
 302  0
             File reference = new File( goalFile.getParentFile(), "reference" );
 303  
 
 304  0
             if ( reference.exists() )
 305  
             {
 306  
                 // compare generated project with reference
 307  0
                 getLog().info( "Comparing generated project with reference content: " + reference );
 308  
 
 309  0
                 assertDirectoryEquals( reference, new File( basedir, request.getArtifactId() ) );
 310  
             }
 311  
 
 312  0
             String goals = FileUtils.fileRead( goalFile );
 313  
 
 314  0
             invokePostArchetypeGenerationGoals( goals, new File( basedir, request.getArtifactId() ) );
 315  
         }
 316  0
         catch ( IOException ioe )
 317  
         {
 318  0
             throw new IntegrationTestFailure( ioe );
 319  0
         }
 320  0
     }
 321  
 
 322  
     private Properties getProperties( File goalFile )
 323  
         throws IOException
 324  
     {
 325  0
         File propertiesFile = new File( goalFile.getParentFile(), "archetype.properties" );
 326  
 
 327  0
         return loadProperties( propertiesFile );
 328  
     }
 329  
 
 330  
     private void invokePostArchetypeGenerationGoals( String goals, File basedir )
 331  
         throws IntegrationTestFailure
 332  
     {
 333  0
         if ( StringUtils.isBlank( goals ) )
 334  
         {
 335  0
             getLog().info( "No post-archetype-generation goals to invoke." );
 336  
 
 337  0
             return;
 338  
         }
 339  
 
 340  0
         getLog().info( "Invoking post-archetype-generation goals: " + goals );
 341  
 
 342  0
         InvocationRequest request = new DefaultInvocationRequest()
 343  
             .setBaseDirectory( basedir )
 344  
             .setGoals( Arrays.asList( StringUtils.split( goals, "," ) ) );
 345  
 
 346  
         try
 347  
         {
 348  0
             InvocationResult result = invoker.execute( request );
 349  
 
 350  0
             getLog().info( "Post-archetype-generation invoker exit code: " + result.getExitCode() );
 351  
 
 352  0
             if ( result.getExitCode() != 0 )
 353  
             {
 354  0
                 throw new IntegrationTestFailure( "Execution failure: exit code = " + result.getExitCode(),
 355  
                                                   result.getExecutionException() );
 356  
             }
 357  
         }
 358  0
         catch ( MavenInvocationException e )
 359  
         {
 360  0
             throw new IntegrationTestFailure( "Cannot run additions goals.", e );
 361  0
         }
 362  0
     }
 363  
 
 364  0
     class IntegrationTestFailure
 365  
         extends Exception
 366  
     {
 367  
         IntegrationTestFailure()
 368  0
         {
 369  0
             super();
 370  0
         }
 371  
 
 372  
         IntegrationTestFailure( String message )
 373  0
         {
 374  0
             super( message );
 375  0
         }
 376  
 
 377  
         IntegrationTestFailure( Throwable cause )
 378  0
         {
 379  0
             super( cause );
 380  0
         }
 381  
 
 382  
         IntegrationTestFailure( String message, Throwable cause )
 383  0
         {
 384  0
             super( message, cause );
 385  0
         }
 386  
     }
 387  
 }