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.plugin.failsafe;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.Collection;
24  
25  import org.apache.maven.execution.MavenSession;
26  import org.apache.maven.plugin.AbstractMojo;
27  import org.apache.maven.plugin.MojoExecutionException;
28  import org.apache.maven.plugin.MojoFailureException;
29  import org.apache.maven.plugin.failsafe.util.FailsafeSummaryXmlUtils;
30  import org.apache.maven.plugin.surefire.SurefireHelper;
31  import org.apache.maven.plugin.surefire.SurefireReportParameters;
32  import org.apache.maven.plugin.surefire.log.PluginConsoleLogger;
33  import org.apache.maven.plugins.annotations.LifecyclePhase;
34  import org.apache.maven.plugins.annotations.Mojo;
35  import org.apache.maven.plugins.annotations.Parameter;
36  import org.apache.maven.surefire.api.cli.CommandLineOption;
37  import org.apache.maven.surefire.api.suite.RunResult;
38  import org.apache.maven.surefire.booter.SurefireBooterForkException;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  import static org.apache.maven.plugin.surefire.SurefireHelper.reportExecution;
43  import static org.apache.maven.surefire.api.suite.RunResult.noTestsRun;
44  import static org.apache.maven.surefire.shared.utils.StringUtils.capitalizeFirstLetter;
45  
46  /**
47   * Verify integration tests ran using Failsafe.
48   *
49   * @author Stephen Connolly
50   * @author Jason van Zyl
51   */
52  @SuppressWarnings("unused")
53  @Mojo(name = "verify", defaultPhase = LifecyclePhase.VERIFY, requiresProject = true, threadSafe = true)
54  public class VerifyMojo extends AbstractMojo implements SurefireReportParameters {
55  
56      /**
57       * Set this to 'true' to skip running tests, but still compile them. Its use is NOT RECOMMENDED, but quite
58       * convenient on occasion.
59       *
60       * @since 2.4
61       */
62      @Parameter(property = "skipTests")
63      private boolean skipTests;
64  
65      /**
66       * Set this to 'true' to skip running integration tests, but still compile them. Its use is NOT RECOMMENDED, but
67       * quite convenient on occasion.
68       *
69       * @since 2.4.3-alpha-2
70       */
71      @Parameter(property = "skipITs")
72      private boolean skipITs;
73  
74      /**
75       * This old parameter is just like skipTests, but bound to the old property maven.test.skip.exec.
76       *
77       * @since 2.3
78       * @deprecated use -DskipTests instead
79       */
80      @Deprecated
81      @Parameter(property = "maven.test.skip.exec")
82      private boolean skipExec;
83  
84      /**
85       * Set this to 'true' to bypass unit tests entirely. Its use is NOT RECOMMENDED, especially if you
86       * enable it using the "maven.test.skip" property, because maven.test.skip disables both running the
87       * tests and compiling the tests.  Consider using the skipTests parameter instead.
88       */
89      @Parameter(property = "maven.test.skip", defaultValue = "false")
90      private boolean skip;
91  
92      /**
93       * Set this to true to ignore a failure during testing. Its use is NOT RECOMMENDED, but quite convenient on
94       * occasion.
95       */
96      @Parameter(property = "maven.test.failure.ignore", defaultValue = "false")
97      private boolean testFailureIgnore;
98  
99      /**
100      * The base directory of the project being tested. This can be obtained in your unit test by
101      * System.getProperty("basedir").
102      */
103     @Parameter(defaultValue = "${basedir}")
104     private File basedir;
105 
106     /**
107      * The directory containing generated test classes of the project being tested.
108      * This will be included at the beginning the test classpath.
109      */
110     @Parameter(defaultValue = "${project.build.testOutputDirectory}")
111     private File testClassesDirectory;
112 
113     /**
114      * Base directory where all reports are written to.
115      */
116     @Parameter(defaultValue = "${project.build.directory}/failsafe-reports")
117     private File reportsDirectory;
118 
119     /**
120      * The summary file to read integration test results from.
121      */
122     @Parameter(defaultValue = "${project.build.directory}/failsafe-reports/failsafe-summary.xml", required = true)
123     private File summaryFile;
124 
125     /**
126      * Additional summary files to read integration test results from.
127      *
128      * @since 2.6
129      */
130     @Parameter
131     private File[] summaryFiles;
132 
133     /**
134      * Set this to "true" to cause a failure if there are no tests to run. Defaults to "false".
135      *
136      * @since 2.4
137      */
138     @Parameter(property = "failIfNoTests", defaultValue = "false")
139     private boolean failIfNoTests;
140 
141     /**
142      * Set this to a value greater than 0 to fail the whole test set if the cumulative number of flakes reaches
143      * this threshold. Set to 0 to allow an unlimited number of flakes.
144      *
145      * @since 3.0.0-M6
146      */
147     @Parameter(property = "failsafe.failOnFlakeCount", defaultValue = "0")
148     private int failOnFlakeCount;
149 
150     /**
151      * The current build session instance.
152      */
153     @Parameter(defaultValue = "${session}", readonly = true)
154     private MavenSession session;
155 
156     private Logger logger = LoggerFactory.getLogger(getClass());
157 
158     private Collection<CommandLineOption> cli;
159 
160     private volatile PluginConsoleLogger consoleLogger = new PluginConsoleLogger(logger);
161 
162     public VerifyMojo() {
163         //
164     }
165 
166     public VerifyMojo(Logger logger) {
167         this.logger = logger;
168     }
169 
170     @Override
171     public void execute() throws MojoExecutionException, MojoFailureException {
172         cli = commandLineOptions();
173         if (verifyParameters()) {
174             logDebugOrCliShowErrors(
175                     capitalizeFirstLetter(getPluginName()) + " report directory: " + getReportsDirectory());
176 
177             try {
178                 RunResult summary = existsSummaryFile() ? readSummary(summaryFile) : noTestsRun();
179 
180                 if (existsSummaryFiles()) {
181                     for (final File summaryFile : summaryFiles) {
182                         summary = summary.aggregate(readSummary(summaryFile));
183                     }
184                 }
185                 reportExecution(this, summary, getConsoleLogger(), getBooterForkException(summary));
186             } catch (IOException e) {
187                 throw new MojoExecutionException(e.getMessage(), e);
188             }
189         }
190     }
191 
192     private Exception getBooterForkException(RunResult summary) {
193         String firstForkExceptionFailureMessage = String.format("%s: ", SurefireBooterForkException.class.getName());
194         if (summary.getFailure() != null && summary.getFailure().contains(firstForkExceptionFailureMessage)) {
195             return new SurefireBooterForkException(
196                     summary.getFailure().substring(firstForkExceptionFailureMessage.length()));
197         }
198         return null;
199     }
200 
201     private PluginConsoleLogger getConsoleLogger() {
202         if (consoleLogger == null) {
203             synchronized (this) {
204                 if (consoleLogger == null) {
205                     consoleLogger = new PluginConsoleLogger(logger);
206                 }
207             }
208         }
209         return consoleLogger;
210     }
211 
212     private RunResult readSummary(File summaryFile) throws IOException {
213         return FailsafeSummaryXmlUtils.toRunResult(summaryFile);
214     }
215 
216     protected boolean verifyParameters() throws MojoFailureException {
217         if (isSkip() || isSkipTests() || isSkipITs() || isSkipExec()) {
218             getConsoleLogger().info("Tests are skipped.");
219             return false;
220         }
221 
222         if (!getTestClassesDirectory().exists()) {
223             if (getFailIfNoTests()) {
224                 throw new MojoFailureException("No tests to run!");
225             }
226         }
227 
228         if (!existsSummary()) {
229             getConsoleLogger().info("No tests to run.");
230             return false;
231         }
232 
233         if (failOnFlakeCount < 0) {
234             throw new MojoFailureException("Parameter \"failOnFlakeCount\" should not be negative.");
235         }
236 
237         return true;
238     }
239 
240     protected String getPluginName() {
241         return "failsafe";
242     }
243 
244     protected String[] getDefaultIncludes() {
245         return null;
246     }
247 
248     @Override
249     public boolean isSkipTests() {
250         return skipTests;
251     }
252 
253     @Override
254     public void setSkipTests(boolean skipTests) {
255         this.skipTests = skipTests;
256     }
257 
258     public boolean isSkipITs() {
259         return skipITs;
260     }
261 
262     public void setSkipITs(boolean skipITs) {
263         this.skipITs = skipITs;
264     }
265 
266     @Override
267     @Deprecated
268     public boolean isSkipExec() {
269         return skipExec;
270     }
271 
272     @Override
273     @Deprecated
274     public void setSkipExec(boolean skipExec) {
275         this.skipExec = skipExec;
276     }
277 
278     @Override
279     public boolean isSkip() {
280         return skip;
281     }
282 
283     @Override
284     public void setSkip(boolean skip) {
285         this.skip = skip;
286     }
287 
288     @Override
289     public boolean isTestFailureIgnore() {
290         return testFailureIgnore;
291     }
292 
293     @Override
294     public void setTestFailureIgnore(boolean testFailureIgnore) {
295         this.testFailureIgnore = testFailureIgnore;
296     }
297 
298     @Override
299     public File getBasedir() {
300         return basedir;
301     }
302 
303     @Override
304     public void setBasedir(File basedir) {
305         this.basedir = basedir;
306     }
307 
308     @Override
309     public File getTestClassesDirectory() {
310         return testClassesDirectory;
311     }
312 
313     @Override
314     public void setTestClassesDirectory(File testClassesDirectory) {
315         this.testClassesDirectory = testClassesDirectory;
316     }
317 
318     @Override
319     public File getReportsDirectory() {
320         return reportsDirectory;
321     }
322 
323     @Override
324     public void setReportsDirectory(File reportsDirectory) {
325         this.reportsDirectory = reportsDirectory;
326     }
327 
328     public File getSummaryFile() {
329         return summaryFile;
330     }
331 
332     public void setSummaryFile(File summaryFile) {
333         this.summaryFile = summaryFile;
334     }
335 
336     @Override
337     public boolean getFailIfNoTests() {
338         return failIfNoTests;
339     }
340 
341     @Override
342     public void setFailIfNoTests(boolean failIfNoTests) {
343         this.failIfNoTests = failIfNoTests;
344     }
345 
346     @Override
347     public int getFailOnFlakeCount() {
348         return failOnFlakeCount;
349     }
350 
351     @Override
352     public void setFailOnFlakeCount(int failOnFlakeCount) {
353         this.failOnFlakeCount = failOnFlakeCount;
354     }
355 
356     public MavenSession getSession() {
357         return session;
358     }
359 
360     public void setSession(MavenSession session) {
361         this.session = session;
362     }
363 
364     private boolean existsSummaryFile() {
365         return summaryFile != null && summaryFile.isFile();
366     }
367 
368     private boolean existsSummaryFiles() {
369         return summaryFiles != null && summaryFiles.length != 0;
370     }
371 
372     private boolean existsSummary() {
373         return existsSummaryFile() || existsSummaryFiles();
374     }
375 
376     private Collection<CommandLineOption> commandLineOptions() {
377         return SurefireHelper.commandLineOptions(session, getConsoleLogger());
378     }
379 
380     private void logDebugOrCliShowErrors(String s) {
381         SurefireHelper.logDebugOrCliShowErrors(s, getConsoleLogger(), cli);
382     }
383 }