View Javadoc
1   package org.apache.maven.it;
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.maven.it.util.ResourceExtractor;
23  
24  import java.io.File;
25  import java.io.IOException;
26  import java.util.List;
27  
28  /**
29   * This is a collection of test cases for <a href="https://issues.apache.org/jira/browse/MNG-5760">MNG-5760</a>,
30   * <code>--resume</code> / <code>-r</code> in case of build failures.
31   *
32   * The test uses a multi-module project with three modules:
33   * <ul>
34   *     <li>module-a</li>
35   *     <li>module-b</li>
36   *     <li>module-c (depends on module-b)</li>
37   * </ul>
38   *
39   * @author Maarten Mulders
40   * @author Martin Kanters
41   */
42  public class MavenITmng5760ResumeFeatureTest extends AbstractMavenIntegrationTestCase {
43      private final File parentDependentTestDir;
44      private final File parentIndependentTestDir;
45      private final File noProjectTestDir;
46      private final File fourModulesTestDir;
47  
48      public MavenITmng5760ResumeFeatureTest() throws IOException {
49          super( "[4.0.0-alpha-1,)" );
50          this.parentDependentTestDir = ResourceExtractor.simpleExtractResources( getClass(),
51                  "/mng-5760-resume-feature/parent-dependent" );
52          this.parentIndependentTestDir = ResourceExtractor.simpleExtractResources( getClass(),
53                  "/mng-5760-resume-feature/parent-independent" );
54          this.noProjectTestDir = ResourceExtractor.simpleExtractResources( getClass(),
55                  "/mng-5760-resume-feature/no-project" );
56          this.fourModulesTestDir = ResourceExtractor.simpleExtractResources( getClass(),
57                  "/mng-5760-resume-feature/four-modules" );
58      }
59  
60      /**
61       * Tests that the hint at the end of a failed build mentions <code>--resume</code> instead of <code>--resume-from</code>.
62       *
63       * @throws Exception in case of failure
64       */
65      public void testShouldSuggestToResumeWithoutArgs() throws Exception
66      {
67          Verifier verifier = newVerifier( parentDependentTestDir.getAbsolutePath() );
68          verifier.addCliOption( "-Dmodule-b.fail=true" );
69  
70          try
71          {
72              verifier.executeGoal( "test" );
73              fail( "Expected this invocation to fail" );
74          }
75          catch ( final VerificationException ve )
76          {
77              verifier.verifyTextInLog( "mvn [args] -r" );
78              verifyTextNotInLog( verifier, "mvn [args] -rf :module-b" );
79          }
80          finally
81          {
82              verifier.resetStreams();
83          }
84  
85          // New build with -r should resume the build from module-b, skipping module-a since it has succeeded already.
86          verifier = newVerifier( parentDependentTestDir.getAbsolutePath() );
87          verifier.addCliOption( "-r" );
88          verifier.executeGoal( "test" );
89          verifyTextNotInLog( verifier, "Building module-a 1.0" );
90          verifier.verifyTextInLog( "Building module-b 1.0" );
91          verifier.verifyTextInLog( "Building module-c 1.0" );
92          verifier.resetStreams();
93      }
94  
95      public void testShouldSkipSuccessfulProjects() throws Exception
96      {
97          Verifier verifier = newVerifier( parentDependentTestDir.getAbsolutePath() );
98          verifier.addCliOption( "-Dmodule-a.fail=true" );
99          verifier.addCliOption( "--fail-at-end");
100 
101         try
102         {
103             verifier.executeGoal( "test" );
104             fail( "Expected this invocation to fail" );
105         }
106         catch ( final VerificationException ve )
107         {
108             // Expected to fail.
109         }
110         finally
111         {
112             verifier.resetStreams();
113         }
114 
115         // Let module-b and module-c fail, if they would have been built...
116         verifier = newVerifier( parentDependentTestDir.getAbsolutePath() );
117         verifier.addCliOption( "-Dmodule-b.fail=true" );
118         verifier.addCliOption( "-Dmodule-c.fail=true" );
119         // ... but adding -r should exclude those two from the build because the previous Maven invocation
120         // marked them as successfully built.
121         verifier.addCliOption( "-r" );
122         try
123         {
124             verifier.executeGoal( "test" );
125         }
126         finally
127         {
128             verifier.resetStreams();
129         }
130     }
131 
132     public void testShouldSkipSuccessfulModulesWhenTheFirstModuleFailed() throws Exception
133     {
134         // In this multi-module project, the submodules are not dependent on the parent.
135         // This results in the parent to be built last, and module-a to be built first.
136         // This enables us to let the first module in the reactor (module-a) fail.
137         Verifier verifier = newVerifier( parentIndependentTestDir.getAbsolutePath() );
138         verifier.addCliOption( "-Dmodule-a.fail=true" );
139         verifier.addCliOption( "--fail-at-end");
140 
141         try
142         {
143             verifier.executeGoal( "test" );
144             fail( "Expected this invocation to fail" );
145         }
146         catch ( final VerificationException ve )
147         {
148             verifier.verifyTextInLog( "mvn [args] -r" );
149         }
150         finally
151         {
152             verifier.resetStreams();
153         }
154 
155         verifier = newVerifier( parentIndependentTestDir.getAbsolutePath() );
156         verifier.addCliOption( "-r" );
157         verifier.executeGoal( "test" );
158         verifier.verifyTextInLog( "Building module-a 1.0" );
159         verifyTextNotInLog( verifier, "Building module-b 1.0" );
160         verifier.resetStreams();
161     }
162 
163     public void testShouldNotCrashWithoutProject() throws Exception
164     {
165         // There is no Maven project available in the test directory.
166         // As reported in JIRA this would previously break with a NullPointerException.
167         // (see https://issues.apache.org/jira/browse/MNG-5760?focusedCommentId=17143795&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-17143795)
168         final Verifier verifier = newVerifier( noProjectTestDir.getAbsolutePath() );
169         try
170         {
171             verifier.executeGoal( "resources:resources" );
172         }
173         catch ( final VerificationException ve )
174         {
175             verifier.verifyTextInLog( "Goal requires a project to execute but there is no POM in this directory" );
176         }
177         finally
178         {
179             verifier.resetStreams();
180         }
181     }
182 
183     public void testFailureWithParallelBuild() throws Exception
184     {
185         // four modules: a, b, c, d
186         // c depends on b, d depends on a
187 
188         // Let's do a first pass with a and c failing.  The build is parallel,
189         // so we have a first thread with a and d, and the second one with b and c
190         // The result should be:
191         //   a : failure (slow, so b and c will be built in the mean time)
192         //   b : success
193         //   c : failure
194         //   d : skipped
195         Verifier verifier = newVerifier( fourModulesTestDir.getAbsolutePath() );
196         verifier.addCliOption( "-T2" );
197         verifier.addCliOption( "-Dmodule-a.delay=1000" );
198         verifier.addCliOption( "-Dmodule-a.fail=true" );
199         verifier.addCliOption( "-Dmodule-c.fail=true" );
200         try
201         {
202             verifier.executeGoal( "verify" );
203             fail( "Expected this invocation to fail" );
204         }
205         catch ( final VerificationException ve )
206         {
207             // Expected to fail.
208         }
209         finally
210         {
211             verifier.resetStreams();
212         }
213 
214         // Let module-b fail, if it would have been built...
215         verifier = newVerifier( fourModulesTestDir.getAbsolutePath() );
216         verifier.addCliOption( "-T2" );
217         verifier.addCliOption( "-Dmodule-b.fail=true" );
218         // ... but adding -r should exclude it from the build because the previous Maven invocation
219         // marked it as successfully built.
220         verifier.addCliOption( "-r" );
221         // The result should be:
222         //   a : success
223         //   c : success
224         //   d : success
225         try
226         {
227             verifier.executeGoal( "verify" );
228         }
229         finally
230         {
231             verifier.resetStreams();
232         }
233     }
234 
235     public void testFailureAfterSkipWithParallelBuild() throws Exception
236     {
237         // four modules: a, b, c, d
238         // c depends on b, d depends on a
239 
240         // Let's do a first pass with a and c failing.  The build is parallel,
241         // so we have a first thread with a and d, and the second one with b and c
242         // The result should be:
243         //   a : success
244         //   b : success, slow
245         //   c : skipped
246         //   d : failure
247         Verifier verifier = newVerifier( fourModulesTestDir.getAbsolutePath() );
248         verifier.addCliOption( "-T2" );
249         verifier.addCliOption( "-Dmodule-b.delay=2000" );
250         verifier.addCliOption( "-Dmodule-d.fail=true" );
251         try
252         {
253             verifier.executeGoal( "verify" );
254             fail( "Expected this invocation to fail" );
255         }
256         catch ( final VerificationException ve )
257         {
258             // Expected to fail.
259         }
260         finally
261         {
262             verifier.resetStreams();
263         }
264 
265         // Let module-a and module-b fail, if they would have been built...
266         verifier = newVerifier( fourModulesTestDir.getAbsolutePath() );
267         verifier.addCliOption( "-T2" );
268         verifier.addCliOption( "-Dmodule-a.fail=true" );
269         verifier.addCliOption( "-Dmodule-b.fail=true" );
270         // ... but adding -r should exclude those two from the build because the previous Maven invocation
271         // marked them as successfully built.
272         verifier.addCliOption( "-r" );
273         try
274         {
275             // The result should be:
276             //   c : success
277             //   d : success
278             verifier.executeGoal( "verify" );
279         }
280         finally
281         {
282             verifier.resetStreams();
283         }
284     }
285 
286     /**
287      * Throws an exception if the text <strong>is</strong> present in the log.
288      *
289      * @param verifier the verifier to use
290      * @param text the text to assert present
291      * @throws VerificationException if text is not found in log
292      */
293     private void verifyTextNotInLog( Verifier verifier, String text )
294             throws VerificationException
295     {
296         List<String> lines = verifier.loadFile( verifier.getBasedir(), verifier.getLogFileName(), false );
297 
298         for ( String line : lines )
299         {
300             if ( Verifier.stripAnsi( line ).contains( text ) )
301             {
302                 throw new VerificationException( "Text found in log: " + text );
303             }
304         }
305     }
306 }