View Javadoc
1   package org.apache.maven.plugins.surefire.report;
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 java.io.File;
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Locale;
27  import org.apache.maven.model.ReportPlugin;
28  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
29  import org.apache.maven.plugins.annotations.Parameter;
30  import org.apache.maven.project.MavenProject;
31  import org.apache.maven.reporting.AbstractMavenReport;
32  import org.apache.maven.reporting.MavenReportException;
33  import org.apache.maven.shared.utils.PathTool;
34  
35  import static java.util.Collections.addAll;
36  import static org.apache.maven.plugins.surefire.report.SurefireReportParser.hasReportFiles;
37  import static org.apache.maven.shared.utils.StringUtils.isEmpty;
38  
39  /**
40   * Abstract base class for reporting test results using Surefire.
41   *
42   * @author Stephen Connolly
43   */
44  public abstract class AbstractSurefireReportMojo
45      extends AbstractMavenReport
46  {
47  
48      /**
49       * If set to false, only failures are shown.
50       */
51      @Parameter( defaultValue = "true", required = true, property = "showSuccess" )
52      private boolean showSuccess;
53  
54      /**
55       * Directories containing the XML Report files that will be parsed and rendered to HTML format.
56       */
57      @Parameter
58      private File[] reportsDirectories;
59  
60      /**
61       * (Deprecated, use reportsDirectories) This directory contains the XML Report files that will be parsed and
62       * rendered to HTML format.
63       */
64      @Deprecated
65      @Parameter
66      private File reportsDirectory;
67  
68      /**
69       * The projects in the reactor for aggregation report.
70       */
71      @Parameter( defaultValue = "${reactorProjects}", readonly = true )
72      private List<MavenProject> reactorProjects;
73  
74      /**
75       * Location of the Xrefs to link.
76       */
77      @Parameter( defaultValue = "${project.reporting.outputDirectory}/xref-test" )
78      private File xrefLocation;
79  
80      /**
81       * Whether to link the XRef if found.
82       */
83      @Parameter( defaultValue = "true", property = "linkXRef" )
84      private boolean linkXRef;
85  
86      /**
87       * Whether to build an aggregated report at the root, or build individual reports.
88       */
89      @Parameter( defaultValue = "false", property = "aggregate" )
90      private boolean aggregate;
91  
92      private List<File> resolvedReportsDirectories;
93  
94      /**
95       * Whether the report should be generated or not.
96       *
97       * @return {@code true} if and only if the report should be generated.
98       * @since 2.11
99       */
100     protected boolean isSkipped()
101     {
102         return false;
103     }
104 
105     /**
106      * Whether the report should be generated when there are no test results.
107      *
108      * @return {@code true} if and only if the report should be generated when there are no result files at all.
109      * @since 2.11
110      */
111     protected boolean isGeneratedWhenNoResults()
112     {
113         return false;
114     }
115 
116     public abstract void setTitle( String title );
117 
118     public abstract String getTitle();
119 
120     public abstract void setDescription( String description );
121 
122     public abstract String getDescription();
123 
124     /**
125      * {@inheritDoc}
126      */
127     @Override
128     public void executeReport( Locale locale )
129         throws MavenReportException
130     {
131         if ( !hasReportDirectories() )
132         {
133             return;
134         }
135 
136         new SurefireReportGenerator( getReportsDirectories(), locale, showSuccess, determineXrefLocation(),
137                                            getConsoleLogger() )
138                 .doGenerateReport( getBundle( locale ), getSink() );
139     }
140 
141     @Override
142     public boolean canGenerateReport()
143     {
144         return hasReportDirectories() && super.canGenerateReport();
145     }
146 
147     private boolean hasReportDirectories()
148     {
149         if ( isSkipped() )
150         {
151             return false;
152         }
153 
154         final List<File> reportsDirectories = getReportsDirectories();
155 
156         if ( reportsDirectories == null )
157         {
158             return false;
159         }
160 
161         if ( !isGeneratedWhenNoResults() )
162         {
163             boolean atLeastOneDirectoryExists = false;
164             for ( Iterator<File> i = reportsDirectories.iterator(); i.hasNext() && !atLeastOneDirectoryExists; )
165             {
166                 atLeastOneDirectoryExists = hasReportFiles( i.next() );
167             }
168             if ( !atLeastOneDirectoryExists )
169             {
170                 return false;
171             }
172         }
173         return true;
174     }
175 
176     private List<File> getReportsDirectories()
177     {
178         if ( resolvedReportsDirectories != null )
179         {
180             return resolvedReportsDirectories;
181         }
182 
183         resolvedReportsDirectories = new ArrayList<>();
184 
185         if ( this.reportsDirectories != null )
186         {
187             addAll( resolvedReportsDirectories, this.reportsDirectories );
188         }
189         //noinspection deprecation
190         if ( reportsDirectory != null )
191         {
192             //noinspection deprecation
193             resolvedReportsDirectories.add( reportsDirectory );
194         }
195         if ( aggregate )
196         {
197             if ( !project.isExecutionRoot() )
198             {
199                 return null;
200             }
201             if ( this.reportsDirectories == null )
202             {
203                 if ( reactorProjects.size() > 1 )
204                 {
205                     for ( MavenProject mavenProject : getProjectsWithoutRoot() )
206                     {
207                         resolvedReportsDirectories.add( getSurefireReportsDirectory( mavenProject ) );
208                     }
209                 }
210                 else
211                 {
212                     resolvedReportsDirectories.add( getSurefireReportsDirectory( project ) );
213                 }
214             }
215             else
216             {
217                 // Multiple report directories are configured.
218                 // Let's see if those directories exist in each sub-module to fix SUREFIRE-570
219                 String parentBaseDir = getProject().getBasedir().getAbsolutePath();
220                 for ( MavenProject subProject : getProjectsWithoutRoot() )
221                 {
222                     String moduleBaseDir = subProject.getBasedir().getAbsolutePath();
223                     for ( File reportsDirectory1 : this.reportsDirectories )
224                     {
225                         String reportDir = reportsDirectory1.getPath();
226                         if ( reportDir.startsWith( parentBaseDir ) )
227                         {
228                             reportDir = reportDir.substring( parentBaseDir.length() );
229                         }
230                         File reportsDirectory = new File( moduleBaseDir, reportDir );
231                         if ( reportsDirectory.exists() && reportsDirectory.isDirectory() )
232                         {
233                             getConsoleLogger().debug( "Adding report dir : " + moduleBaseDir + reportDir );
234                             resolvedReportsDirectories.add( reportsDirectory );
235                         }
236                     }
237                 }
238             }
239         }
240         else
241         {
242             if ( resolvedReportsDirectories.isEmpty() )
243             {
244 
245                 resolvedReportsDirectories.add( getSurefireReportsDirectory( project ) );
246             }
247         }
248         return resolvedReportsDirectories;
249     }
250 
251     /**
252      * Gets the default surefire reports directory for the specified project.
253      *
254      * @param subProject the project to query.
255      * @return the default surefire reports directory for the specified project.
256      */
257     protected abstract File getSurefireReportsDirectory( MavenProject subProject );
258 
259     private List<MavenProject> getProjectsWithoutRoot()
260     {
261         List<MavenProject> result = new ArrayList<>();
262         for ( MavenProject subProject : reactorProjects )
263         {
264             if ( !project.equals( subProject ) )
265             {
266                 result.add( subProject );
267             }
268         }
269         return result;
270 
271     }
272 
273     private String determineXrefLocation()
274     {
275         String location = null;
276 
277         if ( linkXRef )
278         {
279             String relativePath = PathTool.getRelativePath( getOutputDirectory(), xrefLocation.getAbsolutePath() );
280             if ( isEmpty( relativePath ) )
281             {
282                 relativePath = ".";
283             }
284             relativePath = relativePath + "/" + xrefLocation.getName();
285             if ( xrefLocation.exists() )
286             {
287                 // XRef was already generated by manual execution of a lifecycle binding
288                 location = relativePath;
289             }
290             else
291             {
292                 // Not yet generated - check if the report is on its way
293                 for ( Object o : project.getReportPlugins() )
294                 {
295                     ReportPlugin report = (ReportPlugin) o;
296 
297                     String artifactId = report.getArtifactId();
298                     if ( "maven-jxr-plugin".equals( artifactId ) || "jxr-maven-plugin".equals( artifactId ) )
299                     {
300                         location = relativePath;
301                     }
302                 }
303             }
304 
305             if ( location == null )
306             {
307                 getConsoleLogger().warning( "Unable to locate Test Source XRef to link to - DISABLED" );
308             }
309         }
310         return location;
311     }
312 
313     /**
314      * {@inheritDoc}
315      */
316     @Override
317     public String getName( Locale locale )
318     {
319         return getBundle( locale ).getReportName();
320     }
321 
322     /**
323      * {@inheritDoc}
324      */
325     @Override
326     public String getDescription( Locale locale )
327     {
328         return getBundle( locale ).getReportDescription();
329     }
330 
331     /**
332      * {@inheritDoc}
333      */
334     @Override
335     public abstract String getOutputName();
336 
337     protected abstract LocalizedProperties getBundle( Locale locale, ClassLoader resourceBundleClassLoader );
338 
339     protected final ConsoleLogger getConsoleLogger()
340     {
341         return new PluginConsoleLogger( getLog() );
342     }
343 
344     final LocalizedProperties getBundle( Locale locale )
345     {
346         return getBundle( locale, getClass().getClassLoader() );
347     }
348 
349     @Override
350     protected MavenProject getProject()
351     {
352         return project;
353     }
354 }