View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.lifecycle;
20  
21  import javax.inject.Inject;
22  
23  import java.io.File;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collections;
27  import java.util.HashSet;
28  import java.util.List;
29  
30  import org.apache.maven.AbstractCoreMavenComponentTestCase;
31  import org.apache.maven.api.xml.XmlNode;
32  import org.apache.maven.execution.MavenSession;
33  import org.apache.maven.execution.MojoExecutionEvent;
34  import org.apache.maven.execution.MojoExecutionListener;
35  import org.apache.maven.execution.ProjectDependencyGraph;
36  import org.apache.maven.execution.ProjectExecutionEvent;
37  import org.apache.maven.execution.ProjectExecutionListener;
38  import org.apache.maven.lifecycle.internal.DefaultLifecycleTaskSegmentCalculator;
39  import org.apache.maven.lifecycle.internal.ExecutionPlanItem;
40  import org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator;
41  import org.apache.maven.lifecycle.internal.LifecycleTask;
42  import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
43  import org.apache.maven.lifecycle.internal.TaskSegment;
44  import org.apache.maven.model.Plugin;
45  import org.apache.maven.plugin.MojoExecution;
46  import org.apache.maven.plugin.MojoExecutionException;
47  import org.apache.maven.plugin.MojoNotFoundException;
48  import org.apache.maven.plugin.descriptor.MojoDescriptor;
49  import org.apache.maven.project.MavenProject;
50  import org.junit.jupiter.api.Test;
51  
52  import static org.hamcrest.MatcherAssert.assertThat;
53  import static org.hamcrest.Matchers.hasSize;
54  import static org.junit.jupiter.api.Assertions.assertEquals;
55  import static org.junit.jupiter.api.Assertions.assertNotNull;
56  import static org.junit.jupiter.api.Assertions.assertNull;
57  import static org.junit.jupiter.api.Assertions.assertThrows;
58  
59  class LifecycleExecutorTest extends AbstractCoreMavenComponentTestCase {
60      @Inject
61      private DefaultLifecycleExecutor lifecycleExecutor;
62  
63      @Inject
64      private DefaultLifecycleTaskSegmentCalculator lifeCycleTaskSegmentCalculator;
65  
66      @Inject
67      private LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator;
68  
69      @Inject
70      private MojoDescriptorCreator mojoDescriptorCreator;
71  
72      protected String getProjectsDirectory() {
73          return "src/test/projects/lifecycle-executor";
74      }
75  
76      // -----------------------------------------------------------------------------------------------
77      // Tests which exercise the lifecycle executor when it is dealing with default lifecycle phases.
78      // -----------------------------------------------------------------------------------------------
79  
80      @Test
81      void testCalculationOfBuildPlanWithIndividualTaskWherePluginIsSpecifiedInThePom() throws Exception {
82          // We are doing something like "mvn resources:resources" where no version is specified but this
83          // project we are working on has the version specified in the POM so the version should come from there.
84          File pom = getProject("project-basic");
85          MavenSession session = createMavenSession(pom);
86          assertEquals("project-basic", session.getCurrentProject().getArtifactId());
87          assertEquals("1.0", session.getCurrentProject().getVersion());
88          List<MojoExecution> executionPlan = getExecutions(calculateExecutionPlan(session, "resources:resources"));
89          assertEquals(1, executionPlan.size());
90          MojoExecution mojoExecution = executionPlan.get(0);
91          assertNotNull(mojoExecution);
92          assertEquals(
93                  "org.apache.maven.plugins",
94                  mojoExecution.getMojoDescriptor().getPluginDescriptor().getGroupId());
95          assertEquals(
96                  "maven-resources-plugin",
97                  mojoExecution.getMojoDescriptor().getPluginDescriptor().getArtifactId());
98          assertEquals(
99                  "0.1", mojoExecution.getMojoDescriptor().getPluginDescriptor().getVersion());
100     }
101 
102     @Test
103     void testCalculationOfBuildPlanWithIndividualTaskOfTheCleanLifecycle() throws Exception {
104         // We are doing something like "mvn clean:clean" where no version is specified but this
105         // project we are working on has the version specified in the POM so the version should come from there.
106         File pom = getProject("project-basic");
107         MavenSession session = createMavenSession(pom);
108         assertEquals("project-basic", session.getCurrentProject().getArtifactId());
109         assertEquals("1.0", session.getCurrentProject().getVersion());
110         List<MojoExecution> executionPlan = getExecutions(calculateExecutionPlan(session, "clean"));
111         assertEquals(1, executionPlan.size());
112         MojoExecution mojoExecution = executionPlan.get(0);
113         assertNotNull(mojoExecution);
114         assertEquals(
115                 "org.apache.maven.plugins",
116                 mojoExecution.getMojoDescriptor().getPluginDescriptor().getGroupId());
117         assertEquals(
118                 "maven-clean-plugin",
119                 mojoExecution.getMojoDescriptor().getPluginDescriptor().getArtifactId());
120         assertEquals(
121                 "0.1", mojoExecution.getMojoDescriptor().getPluginDescriptor().getVersion());
122     }
123 
124     @Test
125     void testCalculationOfBuildPlanWithIndividualTaskOfTheCleanCleanGoal() throws Exception {
126         // We are doing something like "mvn clean:clean" where no version is specified but this
127         // project we are working on has the version specified in the POM so the version should come from there.
128         File pom = getProject("project-basic");
129         MavenSession session = createMavenSession(pom);
130         assertEquals("project-basic", session.getCurrentProject().getArtifactId());
131         assertEquals("1.0", session.getCurrentProject().getVersion());
132         List<MojoExecution> executionPlan = getExecutions(calculateExecutionPlan(session, "clean:clean"));
133         assertEquals(1, executionPlan.size());
134         MojoExecution mojoExecution = executionPlan.get(0);
135         assertNotNull(mojoExecution);
136         assertEquals(
137                 "org.apache.maven.plugins",
138                 mojoExecution.getMojoDescriptor().getPluginDescriptor().getGroupId());
139         assertEquals(
140                 "maven-clean-plugin",
141                 mojoExecution.getMojoDescriptor().getPluginDescriptor().getArtifactId());
142         assertEquals(
143                 "0.1", mojoExecution.getMojoDescriptor().getPluginDescriptor().getVersion());
144     }
145 
146     List<MojoExecution> getExecutions(MavenExecutionPlan mavenExecutionPlan) {
147         List<MojoExecution> result = new ArrayList<>();
148         for (ExecutionPlanItem executionPlanItem : mavenExecutionPlan) {
149             result.add(executionPlanItem.getMojoExecution());
150         }
151         return result;
152     }
153 
154     // We need to take in multiple lifecycles
155     public void testCalculationOfBuildPlanTasksOfTheCleanLifecycleAndTheInstallLifecycle() throws Exception {
156         File pom = getProject("project-with-additional-lifecycle-elements");
157         MavenSession session = createMavenSession(pom);
158         assertEquals(
159                 "project-with-additional-lifecycle-elements",
160                 session.getCurrentProject().getArtifactId());
161         assertEquals("1.0", session.getCurrentProject().getVersion());
162         List<MojoExecution> executionPlan = getExecutions(calculateExecutionPlan(session, "clean", "install"));
163 
164         // [01] clean:clean
165         // [02] resources:resources
166         // [03] compiler:compile
167         // [04] it:generate-metadata
168         // [05] resources:testResources
169         // [06] compiler:testCompile
170         // [07] it:generate-test-metadata
171         // [08] surefire:test
172         // [09] jar:jar
173         // [10] install:install
174         //
175         assertEquals(10, executionPlan.size());
176 
177         assertEquals("clean:clean", executionPlan.get(0).getMojoDescriptor().getFullGoalName());
178         assertEquals(
179                 "resources:resources", executionPlan.get(1).getMojoDescriptor().getFullGoalName());
180         assertEquals(
181                 "compiler:compile", executionPlan.get(2).getMojoDescriptor().getFullGoalName());
182         assertEquals(
183                 "it:generate-metadata", executionPlan.get(3).getMojoDescriptor().getFullGoalName());
184         assertEquals(
185                 "resources:testResources",
186                 executionPlan.get(4).getMojoDescriptor().getFullGoalName());
187         assertEquals(
188                 "compiler:testCompile", executionPlan.get(5).getMojoDescriptor().getFullGoalName());
189         assertEquals(
190                 "it:generate-test-metadata",
191                 executionPlan.get(6).getMojoDescriptor().getFullGoalName());
192         assertEquals("surefire:test", executionPlan.get(7).getMojoDescriptor().getFullGoalName());
193         assertEquals("jar:jar", executionPlan.get(8).getMojoDescriptor().getFullGoalName());
194         assertEquals("install:install", executionPlan.get(9).getMojoDescriptor().getFullGoalName());
195     }
196 
197     // We need to take in multiple lifecycles
198     public void testCalculationOfBuildPlanWithMultipleExecutionsOfModello() throws Exception {
199         File pom = getProject("project-with-multiple-executions");
200         MavenSession session = createMavenSession(pom);
201         assertEquals(
202                 "project-with-multiple-executions", session.getCurrentProject().getArtifactId());
203         assertEquals("1.0.1", session.getCurrentProject().getVersion());
204 
205         MavenExecutionPlan plan = calculateExecutionPlan(session, "clean", "install");
206 
207         List<MojoExecution> executions = getExecutions(plan);
208 
209         // [01] clean:clean
210         // [02] modello:xpp3-writer
211         // [03] modello:java
212         // [04] modello:xpp3-reader
213         // [05] modello:xpp3-writer
214         // [06] modello:java
215         // [07] modello:xpp3-reader
216         // [08] plugin:descriptor
217         // [09] resources:resources
218         // [10] compiler:compile
219         // [11] resources:testResources
220         // [12] compiler:testCompile
221         // [13] surefire:test
222         // [14] jar:jar
223         // [15] plugin:addPluginArtifactMetadata
224         // [16] install:install
225         //
226 
227         assertEquals(16, executions.size());
228 
229         assertEquals("clean:clean", executions.get(0).getMojoDescriptor().getFullGoalName());
230         assertEquals("it:xpp3-writer", executions.get(1).getMojoDescriptor().getFullGoalName());
231         assertEquals("it:java", executions.get(2).getMojoDescriptor().getFullGoalName());
232         assertEquals("it:xpp3-reader", executions.get(3).getMojoDescriptor().getFullGoalName());
233         assertEquals("it:xpp3-writer", executions.get(4).getMojoDescriptor().getFullGoalName());
234         assertEquals("it:java", executions.get(5).getMojoDescriptor().getFullGoalName());
235         assertEquals("it:xpp3-reader", executions.get(6).getMojoDescriptor().getFullGoalName());
236         assertEquals(
237                 "resources:resources", executions.get(7).getMojoDescriptor().getFullGoalName());
238         assertEquals("compiler:compile", executions.get(8).getMojoDescriptor().getFullGoalName());
239         assertEquals("plugin:descriptor", executions.get(9).getMojoDescriptor().getFullGoalName());
240         assertEquals(
241                 "resources:testResources",
242                 executions.get(10).getMojoDescriptor().getFullGoalName());
243         assertEquals(
244                 "compiler:testCompile", executions.get(11).getMojoDescriptor().getFullGoalName());
245         assertEquals("surefire:test", executions.get(12).getMojoDescriptor().getFullGoalName());
246         assertEquals("jar:jar", executions.get(13).getMojoDescriptor().getFullGoalName());
247         assertEquals(
248                 "plugin:addPluginArtifactMetadata",
249                 executions.get(14).getMojoDescriptor().getFullGoalName());
250         assertEquals("install:install", executions.get(15).getMojoDescriptor().getFullGoalName());
251 
252         assertEquals(
253                 "src/main/mdo/remote-resources.mdo",
254                 new MojoExecutionXPathContainer(executions.get(1)).getValue("configuration/models[1]/model"));
255         assertEquals(
256                 "src/main/mdo/supplemental-model.mdo",
257                 new MojoExecutionXPathContainer(executions.get(4)).getValue("configuration/models[1]/model"));
258     }
259 
260     @Test
261     void testLifecycleQueryingUsingADefaultLifecyclePhase() throws Exception {
262         File pom = getProject("project-with-additional-lifecycle-elements");
263         MavenSession session = createMavenSession(pom);
264         assertEquals(
265                 "project-with-additional-lifecycle-elements",
266                 session.getCurrentProject().getArtifactId());
267         assertEquals("1.0", session.getCurrentProject().getVersion());
268         List<MojoExecution> executionPlan = getExecutions(calculateExecutionPlan(session, "package"));
269 
270         // [01] resources:resources
271         // [02] compiler:compile
272         // [03] it:generate-metadata
273         // [04] resources:testResources
274         // [05] compiler:testCompile
275         // [06] plexus-component-metadata:generate-test-metadata
276         // [07] surefire:test
277         // [08] jar:jar
278         //
279         assertEquals(8, executionPlan.size());
280 
281         assertEquals(
282                 "resources:resources", executionPlan.get(0).getMojoDescriptor().getFullGoalName());
283         assertEquals(
284                 "compiler:compile", executionPlan.get(1).getMojoDescriptor().getFullGoalName());
285         assertEquals(
286                 "it:generate-metadata", executionPlan.get(2).getMojoDescriptor().getFullGoalName());
287         assertEquals(
288                 "resources:testResources",
289                 executionPlan.get(3).getMojoDescriptor().getFullGoalName());
290         assertEquals(
291                 "compiler:testCompile", executionPlan.get(4).getMojoDescriptor().getFullGoalName());
292         assertEquals(
293                 "it:generate-test-metadata",
294                 executionPlan.get(5).getMojoDescriptor().getFullGoalName());
295         assertEquals("surefire:test", executionPlan.get(6).getMojoDescriptor().getFullGoalName());
296         assertEquals("jar:jar", executionPlan.get(7).getMojoDescriptor().getFullGoalName());
297     }
298 
299     @Test
300     void testLifecyclePluginsRetrievalForDefaultLifecycle() throws Exception {
301         List<Plugin> plugins = new ArrayList<>(lifecycleExecutor.getPluginsBoundByDefaultToAllLifecycles("jar"));
302 
303         assertThat(plugins.toString(), plugins, hasSize(9));
304     }
305 
306     @Test
307     void testPluginConfigurationCreation() throws Exception {
308         File pom = getProject("project-with-additional-lifecycle-elements");
309         MavenSession session = createMavenSession(pom);
310         MojoDescriptor mojoDescriptor = mojoDescriptorCreator.getMojoDescriptor(
311                 "org.apache.maven.its.plugins:maven-it-plugin:0.1:java", session, session.getCurrentProject());
312         XmlNode dom = MojoDescriptorCreator.convert(mojoDescriptor).getDom();
313         System.out.println(dom);
314     }
315 
316     MavenExecutionPlan calculateExecutionPlan(MavenSession session, String... tasks) throws Exception {
317         List<TaskSegment> taskSegments =
318                 lifeCycleTaskSegmentCalculator.calculateTaskSegments(session, Arrays.asList(tasks));
319 
320         TaskSegment mergedSegment = new TaskSegment(false);
321 
322         for (TaskSegment taskSegment : taskSegments) {
323             mergedSegment.getTasks().addAll(taskSegment.getTasks());
324         }
325 
326         return lifeCycleExecutionPlanCalculator.calculateExecutionPlan(
327                 session, session.getCurrentProject(), mergedSegment.getTasks());
328     }
329 
330     @Test
331     void testInvalidGoalName() throws Exception {
332         File pom = getProject("project-basic");
333         MavenSession session = createMavenSession(pom);
334         MojoNotFoundException e = assertThrows(
335                 MojoNotFoundException.class,
336                 () -> getExecutions(calculateExecutionPlan(session, "resources:")),
337                 "expected a MojoNotFoundException");
338         assertEquals("", e.getGoal());
339 
340         e = assertThrows(
341                 MojoNotFoundException.class,
342                 () -> getExecutions(calculateExecutionPlan(
343                         session, "org.apache.maven.plugins:maven-resources-plugin:0.1:resources:toomany")),
344                 "expected a MojoNotFoundException");
345         assertEquals("resources:toomany", e.getGoal());
346     }
347 
348     @Test
349     void testPluginPrefixRetrieval() throws Exception {
350         File pom = getProject("project-basic");
351         MavenSession session = createMavenSession(pom);
352         Plugin plugin = mojoDescriptorCreator.findPluginForPrefix("resources", session);
353         assertEquals("org.apache.maven.plugins", plugin.getGroupId());
354         assertEquals("maven-resources-plugin", plugin.getArtifactId());
355     }
356 
357     // Prefixes
358 
359     @Test
360     void testFindingPluginPrefixForCleanClean() throws Exception {
361         File pom = getProject("project-basic");
362         MavenSession session = createMavenSession(pom);
363         Plugin plugin = mojoDescriptorCreator.findPluginForPrefix("clean", session);
364         assertNotNull(plugin);
365     }
366 
367     @Test
368     void testSetupMojoExecution() throws Exception {
369         File pom = getProject("mojo-configuration");
370 
371         MavenSession session = createMavenSession(pom);
372 
373         LifecycleTask task = new LifecycleTask("generate-sources");
374         MavenExecutionPlan executionPlan = lifeCycleExecutionPlanCalculator.calculateExecutionPlan(
375                 session, session.getCurrentProject(), Arrays.asList(task), false);
376 
377         MojoExecution execution = executionPlan.getMojoExecutions().get(0);
378         assertEquals("maven-it-plugin", execution.getArtifactId(), execution.toString());
379         assertNull(execution.getConfiguration());
380 
381         lifeCycleExecutionPlanCalculator.setupMojoExecution(
382                 session, session.getCurrentProject(), execution, new HashSet<>());
383         assertNotNull(execution.getConfiguration());
384         assertEquals("1.0", execution.getConfiguration().getChild("version").getAttribute("default-value"));
385     }
386 
387     @Test
388     void testExecutionListeners() throws Exception {
389         final File pom = getProject("project-basic");
390         final MavenSession session = createMavenSession(pom);
391         session.setProjectDependencyGraph(new ProjectDependencyGraph() {
392             @Override
393             public List<MavenProject> getUpstreamProjects(MavenProject project, boolean transitive) {
394                 return Collections.emptyList();
395             }
396 
397             @Override
398             public List<MavenProject> getAllProjects() {
399                 return session.getAllProjects();
400             }
401 
402             @Override
403             public List<MavenProject> getSortedProjects() {
404                 return Collections.singletonList(session.getCurrentProject());
405             }
406 
407             @Override
408             public List<MavenProject> getDownstreamProjects(MavenProject project, boolean transitive) {
409                 return Collections.emptyList();
410             }
411         });
412 
413         final List<String> log = new ArrayList<>();
414 
415         MojoExecutionListener mojoListener = new MojoExecutionListener() {
416             public void beforeMojoExecution(MojoExecutionEvent event) throws MojoExecutionException {
417                 assertNotNull(event.getSession());
418                 assertNotNull(event.getProject());
419                 assertNotNull(event.getExecution());
420                 assertNotNull(event.getMojo());
421                 assertNull(event.getCause());
422 
423                 log.add("beforeMojoExecution " + event.getProject().getArtifactId() + ":"
424                         + event.getExecution().getExecutionId());
425             }
426 
427             public void afterMojoExecutionSuccess(MojoExecutionEvent event) throws MojoExecutionException {
428                 assertNotNull(event.getSession());
429                 assertNotNull(event.getProject());
430                 assertNotNull(event.getExecution());
431                 assertNotNull(event.getMojo());
432                 assertNull(event.getCause());
433 
434                 log.add("afterMojoExecutionSuccess " + event.getProject().getArtifactId() + ":"
435                         + event.getExecution().getExecutionId());
436             }
437 
438             public void afterExecutionFailure(MojoExecutionEvent event) {
439                 assertNotNull(event.getSession());
440                 assertNotNull(event.getProject());
441                 assertNotNull(event.getExecution());
442                 assertNotNull(event.getMojo());
443                 assertNotNull(event.getCause());
444 
445                 log.add("afterExecutionFailure " + event.getProject().getArtifactId() + ":"
446                         + event.getExecution().getExecutionId());
447             }
448         };
449         ProjectExecutionListener projectListener = new ProjectExecutionListener() {
450             public void beforeProjectExecution(ProjectExecutionEvent event) throws LifecycleExecutionException {
451                 assertNotNull(event.getSession());
452                 assertNotNull(event.getProject());
453                 assertNull(event.getExecutionPlan());
454                 assertNull(event.getCause());
455 
456                 log.add("beforeProjectExecution " + event.getProject().getArtifactId());
457             }
458 
459             public void beforeProjectLifecycleExecution(ProjectExecutionEvent event)
460                     throws LifecycleExecutionException {
461                 assertNotNull(event.getSession());
462                 assertNotNull(event.getProject());
463                 assertNotNull(event.getExecutionPlan());
464                 assertNull(event.getCause());
465 
466                 log.add("beforeProjectLifecycleExecution " + event.getProject().getArtifactId());
467             }
468 
469             public void afterProjectExecutionSuccess(ProjectExecutionEvent event) throws LifecycleExecutionException {
470                 assertNotNull(event.getSession());
471                 assertNotNull(event.getProject());
472                 assertNotNull(event.getExecutionPlan());
473                 assertNull(event.getCause());
474 
475                 log.add("afterProjectExecutionSuccess " + event.getProject().getArtifactId());
476             }
477 
478             public void afterProjectExecutionFailure(ProjectExecutionEvent event) {
479                 assertNotNull(event.getSession());
480                 assertNotNull(event.getProject());
481                 assertNull(event.getExecutionPlan());
482                 assertNotNull(event.getCause());
483 
484                 log.add("afterProjectExecutionFailure " + event.getProject().getArtifactId());
485             }
486         };
487         getContainer().lookup(DelegatingProjectExecutionListener.class).addProjectExecutionListener(projectListener);
488         getContainer().lookup(DelegatingMojoExecutionListener.class).addMojoExecutionListener(mojoListener);
489 
490         try {
491             lifecycleExecutor.execute(session);
492         } finally {
493             getContainer()
494                     .lookup(DelegatingProjectExecutionListener.class)
495                     .removeProjectExecutionListener(projectListener);
496             getContainer().lookup(DelegatingMojoExecutionListener.class).removeMojoExecutionListener(mojoListener);
497         }
498 
499         List<String> expectedLog = Arrays.asList(
500                 "beforeProjectExecution project-basic", //
501                 "beforeProjectLifecycleExecution project-basic", //
502                 "beforeMojoExecution project-basic:default-resources", //
503                 "afterMojoExecutionSuccess project-basic:default-resources", //
504                 "beforeMojoExecution project-basic:default-compile", //
505                 "afterMojoExecutionSuccess project-basic:default-compile", //
506                 "beforeMojoExecution project-basic:default-testResources", //
507                 "afterMojoExecutionSuccess project-basic:default-testResources", //
508                 "beforeMojoExecution project-basic:default-testCompile", //
509                 "afterMojoExecutionSuccess project-basic:default-testCompile", //
510                 "beforeMojoExecution project-basic:default-test", //
511                 "afterMojoExecutionSuccess project-basic:default-test", //
512                 "beforeMojoExecution project-basic:default-jar", //
513                 "afterMojoExecutionSuccess project-basic:default-jar", //
514                 "afterProjectExecutionSuccess project-basic" //
515                 );
516 
517         assertEventLog(expectedLog, log);
518     }
519 
520     private static void assertEventLog(List<String> expectedList, List<String> actualList) {
521         assertEquals(toString(expectedList), toString(actualList));
522     }
523 
524     private static String toString(List<String> lines) {
525         StringBuilder sb = new StringBuilder();
526         for (String line : lines) {
527             sb.append(line).append('\n');
528         }
529         return sb.toString();
530     }
531 }