1 package org.apache.maven.archetype.mojos;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 public class IntegrationTestMojo
71 extends AbstractMojo
72 {
73
74 private ArchetypeGenerator archetypeGenerator;
75
76
77 private Invoker invoker;
78
79
80
81
82
83
84
85
86 private MavenProject project;
87
88
89
90
91
92
93
94 private boolean skip = false;
95
96 public void execute()
97 throws MojoExecutionException, MojoFailureException
98 {
99 if ( skip )
100 {
101 return;
102 }
103
104 File projectsDirectory = new File( project.getBasedir(), "target/test-classes/projects" );
105
106 if ( !projectsDirectory.exists() )
107 {
108 getLog().warn( "No Archetype IT projects: root 'projects' directory not found." );
109
110 return;
111 }
112
113 File archetypeFile = project.getArtifact().getFile();
114
115 if ( archetypeFile == null )
116 {
117 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 List<File> projectsGoalFiles = FileUtils.getFiles( projectsDirectory, "*/goal.txt", "" );
127
128 if ( projectsGoalFiles.size() == 0 )
129 {
130 getLog().warn( "No Archetype IT projects: no directory with goal.txt found." );
131
132 return;
133 }
134
135 StringWriter errorWriter = new StringWriter();
136 for ( File goalFile : projectsGoalFiles )
137 {
138 try
139 {
140 processIntegrationTest( goalFile, archetypeFile );
141 }
142 catch ( IntegrationTestFailure ex )
143 {
144 errorWriter.write( "\nArchetype IT '" + goalFile.getParentFile().getName() + "' failed: " );
145 errorWriter.write( ex.getMessage() );
146 }
147 }
148
149 String errors = errorWriter.toString();
150 if ( !StringUtils.isEmpty( errors ) )
151 {
152 throw new MojoExecutionException( errors );
153 }
154 }
155 catch ( IOException ex )
156 {
157 throw new MojoFailureException( ex, ex.getMessage(), ex.getMessage() );
158 }
159 }
160
161
162
163
164
165
166
167
168 private void assertDirectoryEquals( File reference, File actual )
169 throws IntegrationTestFailure, IOException
170 {
171 @SuppressWarnings( "unchecked" )
172 List<String> referenceFiles = FileUtils.getFileAndDirectoryNames( reference, "**", null, false, true, true, true );
173 getLog().debug( "reference content: " + referenceFiles );
174
175 @SuppressWarnings( "unchecked" )
176 List<String> actualFiles = FileUtils.getFileAndDirectoryNames( actual, "**", null, false, true, true, true );
177 getLog().debug( "actual content: " + referenceFiles );
178
179 boolean fileNamesEquals = CollectionUtils.isEqualCollection( referenceFiles, actualFiles );
180
181 if ( !fileNamesEquals )
182 {
183 getLog().debug( "Actual list of files is not the same as reference:" );
184 int missing = 0;
185 for ( String ref : referenceFiles )
186 {
187 if ( actualFiles.contains( ref ) )
188 {
189 actualFiles.remove( ref );
190 getLog().debug( "Contained " + ref );
191 }
192 else
193 {
194 missing++;
195 getLog().error( "Not contained " + ref );
196 }
197 }
198 getLog().error( "Remains " + actualFiles );
199
200 throw new IntegrationTestFailure( "Reference and generated project differs (missing: " + missing
201 + ", unexpected: " + actualFiles.size() + ")" );
202 }
203
204 boolean contentEquals = true;
205
206 for ( String file : referenceFiles )
207 {
208 File referenceFile = new File( reference, file );
209 File actualFile = new File( actual, file );
210
211 if ( referenceFile.isDirectory() )
212 {
213 if ( actualFile.isFile() )
214 {
215 getLog().warn( "File " + file + " is a directory in the reference but a file in actual" );
216 contentEquals = false;
217 }
218 }
219 else if ( actualFile.isDirectory() )
220 {
221 if ( referenceFile.isFile() )
222 {
223 getLog().warn( "File " + file + " is a file in the reference but a directory in actual" );
224 contentEquals = false;
225 }
226 }
227 else if ( !FileUtils.contentEquals( referenceFile, actualFile ) )
228 {
229 getLog().warn( "Contents of file " + file + " are not equal" );
230 contentEquals = false;
231 }
232 }
233 if ( !contentEquals )
234 {
235 throw new IntegrationTestFailure( "Some content are not equals" );
236 }
237 }
238
239 private Properties loadProperties( final File propertiesFile )
240 throws IOException, FileNotFoundException
241 {
242 Properties properties = new Properties();
243
244 InputStream in = null;
245 try
246 {
247 in = new FileInputStream( propertiesFile );
248
249 properties.load( in );
250 }
251 finally
252 {
253 IOUtil.close( in );
254 }
255
256 return properties;
257 }
258
259 private void processIntegrationTest( File goalFile, File archetypeFile )
260 throws IntegrationTestFailure
261 {
262 getLog().info( "Processing Archetype IT project: " + goalFile.getParentFile().getName() );
263
264 try
265 {
266 Properties properties = getProperties( goalFile );
267
268 String basedir = goalFile.getParentFile().getPath() + "/project";
269
270 FileUtils.deleteDirectory( basedir );
271
272 FileUtils.mkdir( basedir );
273
274 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 ArchetypeGenerationResult result = new ArchetypeGenerationResult();
286
287 archetypeGenerator.generateArchetype( request, archetypeFile, result );
288
289 if ( result.getCause() != null )
290 {
291 if ( result.getCause() instanceof ArchetypeNotConfigured )
292 {
293 ArchetypeNotConfigured anc = (ArchetypeNotConfigured) result.getCause();
294
295 throw new IntegrationTestFailure( "Missing required properties in archetype.properties: "
296 + StringUtils.join( anc.getMissingProperties().iterator(), ", " ), anc );
297 }
298
299 throw new IntegrationTestFailure( result.getCause().getMessage(), result.getCause() );
300 }
301
302 File reference = new File( goalFile.getParentFile(), "reference" );
303
304 if ( reference.exists() )
305 {
306
307 getLog().info( "Comparing generated project with reference content: " + reference );
308
309 assertDirectoryEquals( reference, new File( basedir, request.getArtifactId() ) );
310 }
311
312 String goals = FileUtils.fileRead( goalFile );
313
314 invokePostArchetypeGenerationGoals( goals, new File( basedir, request.getArtifactId() ) );
315 }
316 catch ( IOException ioe )
317 {
318 throw new IntegrationTestFailure( ioe );
319 }
320 }
321
322 private Properties getProperties( File goalFile )
323 throws IOException
324 {
325 File propertiesFile = new File( goalFile.getParentFile(), "archetype.properties" );
326
327 return loadProperties( propertiesFile );
328 }
329
330 private void invokePostArchetypeGenerationGoals( String goals, File basedir )
331 throws IntegrationTestFailure
332 {
333 if ( StringUtils.isBlank( goals ) )
334 {
335 getLog().info( "No post-archetype-generation goals to invoke." );
336
337 return;
338 }
339
340 getLog().info( "Invoking post-archetype-generation goals: " + goals );
341
342 InvocationRequest request = new DefaultInvocationRequest()
343 .setBaseDirectory( basedir )
344 .setGoals( Arrays.asList( StringUtils.split( goals, "," ) ) );
345
346 try
347 {
348 InvocationResult result = invoker.execute( request );
349
350 getLog().info( "Post-archetype-generation invoker exit code: " + result.getExitCode() );
351
352 if ( result.getExitCode() != 0 )
353 {
354 throw new IntegrationTestFailure( "Execution failure: exit code = " + result.getExitCode(),
355 result.getExecutionException() );
356 }
357 }
358 catch ( MavenInvocationException e )
359 {
360 throw new IntegrationTestFailure( "Cannot run additions goals.", e );
361 }
362 }
363
364 class IntegrationTestFailure
365 extends Exception
366 {
367 IntegrationTestFailure()
368 {
369 super();
370 }
371
372 IntegrationTestFailure( String message )
373 {
374 super( message );
375 }
376
377 IntegrationTestFailure( Throwable cause )
378 {
379 super( cause );
380 }
381
382 IntegrationTestFailure( String message, Throwable cause )
383 {
384 super( message, cause );
385 }
386 }
387 }