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      * @since 2.6
128      */
129     @Parameter
130     private File[] summaryFiles;
131 
132     /**
133      * Set this to "true" to cause a failure if there are no tests to run. Defaults to "false".
134      *
135      * @since 2.4
136      */
137     @Parameter(property = "failIfNoTests", defaultValue = "false")
138     private boolean failIfNoTests;
139 
140     /**
141      * Set this to a value greater than 0 to fail the whole test set if the cumulative number of flakes reaches
142      * this threshold. Set to 0 to allow an unlimited number of flakes.
143      *
144      * @since 3.0.0-M6
145      */
146     @Parameter(property = "failsafe.failOnFlakeCount", defaultValue = "0")
147     private int failOnFlakeCount;
148 
149     /**
150      * The current build session instance.
151      */
152     @Parameter(defaultValue = "${session}", readonly = true)
153     private MavenSession session;
154 
155     private final Logger logger;
156 
157     private Collection<CommandLineOption> cli;
158 
159     private volatile PluginConsoleLogger consoleLogger;
160 
161     public VerifyMojo() {
162         this.logger = LoggerFactory.getLogger(getClass());
163     }
164 
165     public VerifyMojo(Logger logger) {
166         this.logger = logger;
167     }
168 
169     @Override
170     public void execute() throws MojoExecutionException, MojoFailureException {
171         cli = commandLineOptions();
172         if (verifyParameters()) {
173             logDebugOrCliShowErrors(
174                     capitalizeFirstLetter(getPluginName()) + " report directory: " + getReportsDirectory());
175 
176             try {
177                 RunResult summary = existsSummaryFile() ? readSummary(summaryFile) : noTestsRun();
178 
179                 if (existsSummaryFiles()) {
180                     for (final File summaryFile : summaryFiles) {
181                         summary = summary.aggregate(readSummary(summaryFile));
182                     }
183                 }
184                 reportExecution(this, summary, getConsoleLogger(), getBooterForkException(summary));
185             } catch (IOException e) {
186                 throw new MojoExecutionException(e.getMessage(), e);
187             }
188         }
189     }
190 
191     private Exception getBooterForkException(RunResult summary) {
192         String firstForkExceptionFailureMessage = String.format("%s: ", SurefireBooterForkException.class.getName());
193         if (summary.getFailure() != null && summary.getFailure().contains(firstForkExceptionFailureMessage)) {
194             return new SurefireBooterForkException(
195                     summary.getFailure().substring(firstForkExceptionFailureMessage.length()));
196         }
197         return null;
198     }
199 
200     private PluginConsoleLogger getConsoleLogger() {
201         if (consoleLogger == null) {
202             synchronized (this) {
203                 if (consoleLogger == null) {
204                     consoleLogger = new PluginConsoleLogger(logger);
205                 }
206             }
207         }
208         return consoleLogger;
209     }
210 
211     private RunResult readSummary(File summaryFile) throws IOException {
212         return FailsafeSummaryXmlUtils.toRunResult(summaryFile);
213     }
214 
215     protected boolean verifyParameters() throws MojoFailureException {
216         if (isSkip() || isSkipTests() || isSkipITs() || isSkipExec()) {
217             getConsoleLogger().info("Tests are skipped.");
218             return false;
219         }
220 
221         if (!getTestClassesDirectory().exists()) {
222             if (getFailIfNoTests()) {
223                 throw new MojoFailureException("No tests to run!");
224             }
225         }
226 
227         if (!existsSummary()) {
228             getConsoleLogger().info("No tests to run.");
229             return false;
230         }
231 
232         if (failOnFlakeCount < 0) {
233             throw new MojoFailureException("Parameter \"failOnFlakeCount\" should not be negative.");
234         }
235 
236         return true;
237     }
238 
239     protected String getPluginName() {
240         return "failsafe";
241     }
242 
243     protected String[] getDefaultIncludes() {
244         return null;
245     }
246 
247     @Override
248     public boolean isSkipTests() {
249         return skipTests;
250     }
251 
252     @Override
253     public void setSkipTests(boolean skipTests) {
254         this.skipTests = skipTests;
255     }
256 
257     public boolean isSkipITs() {
258         return skipITs;
259     }
260 
261     public void setSkipITs(boolean skipITs) {
262         this.skipITs = skipITs;
263     }
264 
265     @Override
266     @Deprecated
267     public boolean isSkipExec() {
268         return skipExec;
269     }
270 
271     @Override
272     @Deprecated
273     public void setSkipExec(boolean skipExec) {
274         this.skipExec = skipExec;
275     }
276 
277     @Override
278     public boolean isSkip() {
279         return skip;
280     }
281 
282     @Override
283     public void setSkip(boolean skip) {
284         this.skip = skip;
285     }
286 
287     @Override
288     public boolean isTestFailureIgnore() {
289         return testFailureIgnore;
290     }
291 
292     @Override
293     public void setTestFailureIgnore(boolean testFailureIgnore) {
294         this.testFailureIgnore = testFailureIgnore;
295     }
296 
297     @Override
298     public File getBasedir() {
299         return basedir;
300     }
301 
302     @Override
303     public void setBasedir(File basedir) {
304         this.basedir = basedir;
305     }
306 
307     @Override
308     public File getTestClassesDirectory() {
309         return testClassesDirectory;
310     }
311 
312     @Override
313     public void setTestClassesDirectory(File testClassesDirectory) {
314         this.testClassesDirectory = testClassesDirectory;
315     }
316 
317     @Override
318     public File getReportsDirectory() {
319         return reportsDirectory;
320     }
321 
322     @Override
323     public void setReportsDirectory(File reportsDirectory) {
324         this.reportsDirectory = reportsDirectory;
325     }
326 
327     public File getSummaryFile() {
328         return summaryFile;
329     }
330 
331     public void setSummaryFile(File summaryFile) {
332         this.summaryFile = summaryFile;
333     }
334 
335     @Override
336     public boolean getFailIfNoTests() {
337         return failIfNoTests;
338     }
339 
340     @Override
341     public void setFailIfNoTests(boolean failIfNoTests) {
342         this.failIfNoTests = failIfNoTests;
343     }
344 
345     @Override
346     public int getFailOnFlakeCount() {
347         return failOnFlakeCount;
348     }
349 
350     @Override
351     public void setFailOnFlakeCount(int failOnFlakeCount) {
352         this.failOnFlakeCount = failOnFlakeCount;
353     }
354 
355     public MavenSession getSession() {
356         return session;
357     }
358 
359     public void setSession(MavenSession session) {
360         this.session = session;
361     }
362 
363     private boolean existsSummaryFile() {
364         return summaryFile != null && summaryFile.isFile();
365     }
366 
367     private boolean existsSummaryFiles() {
368         return summaryFiles != null && summaryFiles.length != 0;
369     }
370 
371     private boolean existsSummary() {
372         return existsSummaryFile() || existsSummaryFiles();
373     }
374 
375     private Collection<CommandLineOption> commandLineOptions() {
376         return SurefireHelper.commandLineOptions(session, getConsoleLogger());
377     }
378 
379     private void logDebugOrCliShowErrors(String s) {
380         SurefireHelper.logDebugOrCliShowErrors(s, getConsoleLogger(), cli);
381     }
382 }