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