1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.shared.test.plugin;
20
21 import java.io.File;
22 import java.io.FileReader;
23 import java.io.FileWriter;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Properties;
29
30 import org.apache.maven.artifact.Artifact;
31 import org.apache.maven.artifact.factory.ArtifactFactory;
32 import org.apache.maven.artifact.handler.ArtifactHandler;
33 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
34 import org.apache.maven.artifact.repository.ArtifactRepository;
35 import org.apache.maven.model.Build;
36 import org.apache.maven.model.Model;
37 import org.apache.maven.model.Plugin;
38 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
39 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
40 import org.apache.maven.project.MavenProject;
41 import org.apache.maven.project.MavenProjectBuilder;
42 import org.apache.maven.project.ProjectBuildingException;
43 import org.apache.maven.project.artifact.ProjectArtifactMetadata;
44 import org.codehaus.plexus.util.IOUtil;
45 import org.codehaus.plexus.util.xml.Xpp3Dom;
46 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
47
48 /**
49 * Testing tool used to read MavenProject instances from pom.xml files, and to create plugin jar
50 * files (package phase of the normal build process) for distribution to a test local repository
51 * directory.
52 *
53 * @plexus.component role="org.apache.maven.shared.test.plugin.ProjectTool" role-hint="default"
54 * @author jdcasey
55 */
56 public class ProjectTool
57 {
58 public static final String ROLE = ProjectTool.class.getName();
59
60 /**
61 * @plexus.requirement role-hint="default"
62 */
63 private BuildTool buildTool;
64
65 /**
66 * @plexus.requirement role-hint="default"
67 */
68 private RepositoryTool repositoryTool;
69
70 /**
71 * @plexus.requirement
72 */
73 private MavenProjectBuilder projectBuilder;
74
75 /**
76 * @plexus.requirement
77 */
78 private ArtifactHandlerManager artifactHandlerManager;
79
80 /**
81 * @plexus.requirement
82 */
83 private ArtifactFactory artifactFactory;
84
85 /**
86 * Construct a MavenProject instance from the specified POM file.
87 */
88 public MavenProject readProject( File pomFile )
89 throws TestToolsException
90 {
91 return readProject( pomFile, repositoryTool.findLocalRepositoryDirectory() );
92 }
93
94 /**
95 * Construct a MavenProject instance from the specified POM file, using the specified local
96 * repository directory to resolve ancestor POMs as needed.
97 */
98 public MavenProject readProject( File pomFile, File localRepositoryBasedir )
99 throws TestToolsException
100 {
101 try
102 {
103 ArtifactRepository localRepository = repositoryTool
104 .createLocalArtifactRepositoryInstance( localRepositoryBasedir );
105
106 return projectBuilder.build( pomFile, localRepository, null );
107 }
108 catch ( ProjectBuildingException e )
109 {
110 throw new TestToolsException( "Error building MavenProject instance from test pom: " + pomFile, e );
111 }
112 }
113
114 /**
115 * Run the plugin's Maven build up to the package phase, in order to produce a jar file for
116 * distribution to a test-time local repository. The testVersion parameter specifies the version
117 * to be used in the <version/> element of the plugin configuration, and also in fully
118 * qualified, unambiguous goal invocations (as in
119 * org.apache.maven.plugins:maven-eclipse-plugin:test:eclipse).
120 *
121 * @param pomFile The plugin's POM
122 * @param testVersion The version to use for testing this plugin. To promote test resiliency,
123 * this version should remain unchanged, regardless of what plugin version is under
124 * development.
125 * @param skipUnitTests In cases where test builds occur during the unit-testing phase (usually
126 * a bad testing smell), the plugin jar must be produced <b>without</b> running unit tests.
127 * Otherwise, the testing process will result in a recursive loop of building a plugin jar and
128 * trying to unit test it during the build. In these cases, set this flag to <code>true</code>.
129 * @return The resulting MavenProject, after the test version and skip flag (for unit tests)
130 * have been appropriately configured.
131 */
132 public MavenProject packageProjectArtifact( File pomFile, String testVersion, boolean skipUnitTests )
133 throws TestToolsException
134 {
135 return packageProjectArtifact( pomFile, testVersion, skipUnitTests, null );
136 }
137
138 /**
139 * Run the plugin's Maven build up to the package phase, in order to produce a jar file for
140 * distribution to a test-time local repository. The testVersion parameter specifies the version
141 * to be used in the <version/> element of the plugin configuration, and also in fully
142 * qualified, unambiguous goal invocations (as in
143 * org.apache.maven.plugins:maven-eclipse-plugin:test:eclipse).
144 *
145 * @param pomFile The plugin's POM
146 * @param testVersion The version to use for testing this plugin. To promote test resiliency,
147 * this version should remain unchanged, regardless of what plugin version is under
148 * development.
149 * @param skipUnitTests In cases where test builds occur during the unit-testing phase (usually
150 * a bad testing smell), the plugin jar must be produced <b>without</b> running unit tests.
151 * Otherwise, the testing process will result in a recursive loop of building a plugin jar and
152 * trying to unit test it during the build. In these cases, set this flag to <code>true</code>.
153 * @param logFile The file to which build output should be logged, in order to allow later
154 * inspection in case this build fails.
155 * @return The resulting MavenProject, after the test version and skip flag (for unit tests)
156 * have been appropriately configured.
157 */
158 public MavenProject packageProjectArtifact( File pomFile, String testVersion, boolean skipUnitTests, File logFile )
159 throws TestToolsException
160 {
161 PomInfo pomInfo = manglePomForTesting( pomFile, testVersion, skipUnitTests );
162
163 Properties properties = new Properties();
164
165 List goals = new ArrayList();
166 goals.add( "package" );
167
168 File buildLog = logFile == null ? pomInfo.getBuildLogFile() : logFile;
169
170 buildTool.executeMaven( pomInfo.getPomFile(), properties, goals, buildLog );
171
172 File artifactFile = new File( pomInfo.getBuildOutputDirectory() + "/" + pomInfo.getFinalName() );
173
174 try
175 {
176 MavenProject project = projectBuilder.build( pomInfo.getPomFile(), repositoryTool
177 .createLocalArtifactRepositoryInstance(), null );
178
179 Artifact artifact = artifactFactory.createArtifact( project.getGroupId(), project.getArtifactId(), project
180 .getVersion(), null, project.getPackaging() );
181
182 artifact.setFile( artifactFile );
183 artifact.addMetadata( new ProjectArtifactMetadata( artifact, project.getFile() ) );
184
185 project.setArtifact( artifact );
186
187 return project;
188 }
189 catch ( ProjectBuildingException e )
190 {
191 throw new TestToolsException(
192 "Error building MavenProject instance from test pom: " + pomInfo.getPomFile(),
193 e );
194 }
195 }
196
197 /**
198 * Inject a special version for testing, to allow tests to unambiguously reference the plugin
199 * currently under test. If test builds will be executed from the unit-testing phase, also inject
200 * <skip>true</skip> into the configuration of the <code>maven-surefire-plugin</code>
201 * to allow production of a test-only version of the plugin jar without running unit tests.
202 *
203 * @param pomFile The plugin POM
204 * @param testVersion The version that allows test builds to reference the plugin under test
205 * @param skipUnitTests If true, configure the surefire plugin to skip unit tests
206 * @return Information about mangled POM, including the temporary file to which it was written.
207 */
208 protected PomInfo manglePomForTesting( File pomFile, String testVersion, boolean skipUnitTests )
209 throws TestToolsException
210 {
211 File input = new File( "pom.xml" );
212
213 File output = new File( "pom-test.xml" );
214 output.deleteOnExit();
215
216 FileReader reader = null;
217 FileWriter writer = null;
218
219 Model model = null;
220 String finalName = null;
221 String buildOutputDirectory = null;
222
223 try
224 {
225 reader = new FileReader( input );
226 writer = new FileWriter( output );
227
228 model = new MavenXpp3Reader().read( reader );
229
230 model.setVersion( testVersion );
231
232 Build build = model.getBuild();
233 if ( build == null )
234 {
235 build = new Build();
236 model.setBuild( build );
237 }
238
239 finalName = build.getFinalName();
240
241 if ( finalName == null )
242 {
243 ArtifactHandler handler = artifactHandlerManager.getArtifactHandler( model.getPackaging() );
244
245 String ext = handler.getExtension();
246
247 finalName = model.getArtifactId() + "-" + model.getVersion() + "." + ext;
248 }
249
250 buildOutputDirectory = build.getOutputDirectory();
251
252 if ( buildOutputDirectory == null )
253 {
254 buildOutputDirectory = "target";
255 }
256
257 if ( skipUnitTests )
258 {
259 List plugins = build.getPlugins();
260 Plugin plugin = null;
261 for ( Iterator iter = plugins.iterator(); iter.hasNext(); )
262 {
263 Plugin plug = (Plugin) iter.next();
264
265 if ( "maven-surefire-plugin".equals( plug.getArtifactId() ) )
266 {
267 plugin = plug;
268 break;
269 }
270 }
271
272 if ( plugin == null )
273 {
274 plugin = new Plugin();
275 plugin.setArtifactId( "maven-surefire-plugin" );
276 build.addPlugin( plugin );
277 }
278
279 Xpp3Dom configDom = (Xpp3Dom) plugin.getConfiguration();
280 if ( configDom == null )
281 {
282 configDom = new Xpp3Dom( "configuration" );
283 plugin.setConfiguration( configDom );
284 }
285
286 Xpp3Dom skipDom = new Xpp3Dom( "skip" );
287 skipDom.setValue( "true" );
288
289 configDom.addChild( skipDom );
290 }
291
292 new MavenXpp3Writer().write( writer, model );
293 }
294 catch ( IOException e )
295 {
296 throw new TestToolsException( "Error creating test-time version of POM for: " + input, e );
297 }
298 catch ( XmlPullParserException e )
299 {
300 throw new TestToolsException( "Error creating test-time version of POM for: " + input, e );
301 }
302 finally
303 {
304 IOUtil.close( reader );
305 IOUtil.close( writer );
306 }
307
308 return new PomInfo( output, model.getGroupId(), model.getArtifactId(), model.getVersion(),
309 buildOutputDirectory, finalName );
310 }
311
312 static final class PomInfo
313 {
314 private final File pomFile;
315
316 private final String groupId;
317
318 private final String artifactId;
319
320 private final String version;
321
322 private final String finalName;
323
324 private final String buildOutputDirectory;
325
326 PomInfo( File pomFile, String groupId, String artifactId, String version, String buildOutputDirectory,
327 String finalName )
328 {
329 this.pomFile = pomFile;
330 this.groupId = groupId;
331 this.artifactId = artifactId;
332 this.version = version;
333 this.buildOutputDirectory = buildOutputDirectory;
334 this.finalName = finalName;
335 }
336
337 File getPomFile()
338 {
339 return pomFile;
340 }
341
342 String getBuildOutputDirectory()
343 {
344 return buildOutputDirectory;
345 }
346
347 String getFinalName()
348 {
349 return finalName;
350 }
351
352 File getBuildLogFile()
353 {
354 return new File( buildOutputDirectory + "/test-build-logs/" + groupId + "_" + artifactId + "_" + version
355 + ".build.log" );
356 }
357
358 }
359
360 }