View Javadoc

1   /*
2    * Copyright 2010-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.testdoc.maven;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.OutputStream;
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.Comparator;
24  import java.util.List;
25  import java.util.Locale;
26  import java.util.Set;
27  
28  import javax.xml.stream.XMLOutputFactory;
29  import javax.xml.stream.XMLStreamException;
30  
31  import org.apache.commons.io.FileUtils;
32  import org.apache.commons.io.IOUtils;
33  import org.apache.maven.doxia.sink.Sink;
34  import org.apache.maven.reporting.MavenReportException;
35  
36  import de.smartics.maven.util.PathUtils;
37  import de.smartics.testdoc.maven.export.sink.SinkExporter;
38  import de.smartics.testdoc.maven.export.sink.SinkOutputManager;
39  import de.smartics.testdoc.maven.export.xml.XmlExporter;
40  import de.smartics.testdoc.maven.export.xml.XmlOutputManager;
41  import de.smartics.testdoc.core.doc.OutputManager;
42  import de.smartics.testdoc.core.doc.UnitTestDoc;
43  import de.smartics.testdoc.core.doc.UnitTestDocIndex;
44  import de.smartics.testdoc.core.export.UnitTestDocIndexExporter;
45  import de.smartics.testdoc.report.export.doc.IndexProvider;
46  import de.smartics.testdoc.report.export.doc.InformationFilter;
47  import de.smartics.testdoc.report.export.doc.ReportConfig;
48  import de.smartics.testdoc.report.export.doc.SelectionFilter;
49  import de.smartics.testdoc.report.export.doc.TestDocHelper;
50  import de.smartics.testdoc.report.index.ExportIndex;
51  
52  /**
53   * Generates XDoc reports.
54   *
55   * @goal test-stories-report
56   * @execute phase="test" lifecycle="testdoc"
57   * @requiresProject
58   * @requiresDependencyResolution test
59   * @description Generates XDoc reports.
60   * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a>
61   * @version $Revision:591 $
62   */
63  public class TestDocReportMojo extends AbstractSourceReportMojo
64  {
65    // ********************************* Fields *********************************
66  
67    // --- constants ------------------------------------------------------------
68  
69    // --- members --------------------------------------------------------------
70  
71    /**
72     * The output directory to write intermediate serialized files to.
73     *
74     * @parameter expression="${project.build.directory}/testdoc/ser"
75     * @since 1.0
76     */
77    private String serOutputDirectory;
78  
79    /**
80     * Helper methods to access test documentation information.
81     */
82    private TestDocHelper testDocHelper;
83  
84    /**
85     * The name of categories of test scenarios that should be excluded. If a
86     * scenario is tagged by at least one of the mentioned filters, it will not be
87     * displayed in the report.
88     * <p>
89     * The category is specified as a JUnit <code>Category</code>. The name to
90     * specify here is the full qualified type specified in the category
91     * annotation.
92     * </p>
93     * <p>
94     * Per default, if no filter category is specified, no category is excluded.
95     * </p>
96     *
97     * @parameter
98     * @since 1.0
99     */
100   private List<String> excludeCategories;
101 
102   /**
103    * The name of categories of test scenarios that should be included. If a
104    * scenario is tagged by at least one of the mentioned filters, it will not be
105    * displayed in the report.
106    * <p>
107    * The category is specified as a JUnit <code>Category</code>. The name to
108    * specify here is the full qualified type specified in the category
109    * annotation.
110    * </p>
111    * <p>
112    * Per default, if no filter category is specified, all categories are
113    * included.
114    * </p>
115    *
116    * @parameter
117    * @since 1.0
118    */
119   private List<String> includeCategories;
120 
121   /**
122    * The class names of implementations of {@link ExportIndex}. The
123    * implementations are required to provide a no-arg constructor.
124    *
125    * @parameter
126    * @since 1.0
127    */
128   private List<String> indices;
129 
130   /**
131    * The name of the comparator to compare {@link UnitTestDoc} instances. The
132    * default implementation compares the simple name of the UUT type. An
133    * alternative implementation, sorting by the qualified name of the UUT, is
134    * <code>de.smartics.testdoc.core.doc.UnitTestDocTypeSimpleNameComparator</code>.
135    * The implementation is required to provide a no-arg constructor.
136    *
137    * @parameter default-value=
138    *            "de.smartics.testdoc.core.doc.UnitTestDocTypeSimpleNameComparator"
139    * @since 1.0
140    */
141   private String unitTestDocComparator;
142 
143   /**
144    * The names of report formats to export to. Currently only <code>site</code>
145    * and <code>xml</code> are supported. If no format is configured,
146    * <code>site</code> is assumed.
147    *
148    * @parameter
149    * @since 1.0
150    */
151   private Set<String> formats;
152 
153   /**
154    * The directory to write a pure XML report to. This report contains the same
155    * information as the site report. To generate the report, the
156    * <code>formats</code> have to provide the format <code>xml</code>.
157    *
158    * @parameter
159    *            default-value="${project.build.directory}/testdoc-reports"
160    */
161   private File xmlReportDir;
162 
163   /**
164    * The flag that tells whether (<code>true</code>) or not (<code>false</code>)
165    * the index over all indices should be shown.
166    *
167    * @parameter default-value="true"
168    * @since 1.0
169    */
170   private boolean showIndexOfIndices;
171 
172   /**
173    * The flag that tells whether (<code>true</code>) or not (<code>false</code>)
174    * the number column should be shown when enumerating the test cases.
175    *
176    * @parameter default-value="true"
177    * @since 1.0
178    */
179   private boolean showNumbering;
180 
181   /**
182    * The flag that tells whether (<code>true</code>) or not (<code>false</code>)
183    * the test status column should be shown when enumerating the test cases.
184    *
185    * @parameter default-value="true"
186    * @since 1.0
187    */
188   private boolean showTestStatus;
189 
190   /**
191    * The flag that tells whether (<code>true</code>) or not (<code>false</code>)
192    * the categories should be shown when enumerating the test cases.
193    *
194    * @parameter default-value="true"
195    * @since 1.0
196    */
197   private boolean showCategories;
198 
199   /**
200    * The flag that tells whether (<code>true</code>) or not (<code>false</code>)
201    * the test case name should be shown.
202    *
203    * @parameter default-value="true"
204    * @since 1.0
205    */
206   private boolean showTestCase;
207 
208   // ****************************** Initializer *******************************
209 
210   // ****************************** Constructors ******************************
211 
212   // ****************************** Inner Classes *****************************
213 
214   // ********************************* Methods ********************************
215 
216   // --- init -----------------------------------------------------------------
217 
218   // --- get&set --------------------------------------------------------------
219 
220   // --- business -------------------------------------------------------------
221 
222   /**
223    * {@inheritDoc}
224    */
225   @Override
226   protected void executeReport(final Locale locale) throws MavenReportException
227   {
228     if (canGenerateReport())
229     {
230       super.executeReport(locale);
231       final IndexProvider indexChecker = new IndexProvider(serOutputDirectory);
232       final ReportConfig reportConfig = createReportConfig();
233       testDocHelper = indexChecker.createHelper(reportConfig);
234       if (testDocHelper.isIndexProvided())
235       {
236         exportIndex(locale);
237       }
238       else
239       {
240         getLog().info(
241             "No test doc information found, no report will be generated.");
242       }
243     }
244     else
245     {
246       getLog().debug("Skipping testdoc report.");
247     }
248   }
249 
250   protected void exportIndex(final Locale locale) throws MavenReportException
251   {
252     try
253     {
254       final List<ExportIndex> exportIndices = createExportIndices();
255       final UnitTestDocIndex index = testDocHelper.getIndex();
256 
257       if (formats == null || formats.contains("site"))
258       {
259         exportSinkReport(exportIndices, index);
260       }
261 
262       if (formats != null && formats.contains("xml"))
263       {
264         exportXmlReport(exportIndices, index);
265       }
266     }
267     catch (IOException e)
268     {
269       throw new MavenReportException("Cannot generate testdoc XML files.", e);
270     }
271   }
272 
273   protected void exportSinkReport(final List<ExportIndex> exportIndices,
274       final UnitTestDocIndex index) throws IOException
275   {
276     final Sink sink = getSink();
277     final SinkExporter exporter =
278         new SinkExporter(messages, testDocHelper, sink);
279     final OutputManager manager =
280         new SinkOutputManager(outputName, messages, exporter, exportIndices);
281     final UnitTestDocIndexExporter indexExporter =
282         new UnitTestDocIndexExporter(manager);
283     indexExporter.export(index);
284   }
285 
286   protected void exportXmlReport(final List<ExportIndex> exportIndices,
287       final UnitTestDocIndex index) throws IOException
288   {
289     OutputStream output = null;
290     try
291     {
292       final XMLOutputFactory xof = XMLOutputFactory.newInstance();
293       try
294       {
295         final File xmlReportFile = new File(xmlReportDir, outputName + ".xml");
296         output = FileUtils.openOutputStream(xmlReportFile);
297         final XmlExporter exporter =
298             new XmlExporter(messages, testDocHelper, xof.createXMLStreamWriter(output, "UTF-8"));
299         final OutputManager manager =
300             new XmlOutputManager(outputName, messages, exporter, exportIndices);
301         final UnitTestDocIndexExporter indexExporter =
302             new UnitTestDocIndexExporter(manager);
303         indexExporter.export(index);
304       }
305       catch (final XMLStreamException e)
306       {
307         throw new IOException("Cannot create XML writer.", e);
308       }
309 
310     }
311     finally
312     {
313       IOUtils.closeQuietly(output);
314     }
315   }
316 
317   @SuppressWarnings("unchecked")
318   private List<ExportIndex> createExportIndices() throws MavenReportException
319   {
320     if (indices != null)
321     {
322       final List<ExportIndex> list = new ArrayList<ExportIndex>(indices.size());
323       for (final String exportIndexClassName : indices)
324       {
325         try
326         {
327           final Class<? extends ExportIndex> type =
328               (Class<? extends ExportIndex>) Class
329                   .forName(exportIndexClassName);
330           final ExportIndex index = type.newInstance();
331           list.add(index);
332         }
333         catch (final ClassNotFoundException e)
334         {
335           throw new MavenReportException("Unknown export index type '"
336                                          + exportIndexClassName + "'.", e);
337         }
338         catch (final InstantiationException e)
339         {
340           throw new MavenReportException(
341               "Cannot instantiate export index type '" + exportIndexClassName
342                   + "'.", e);
343         }
344         catch (final IllegalAccessException e)
345         {
346           throw new MavenReportException("Cannot access export index type '"
347                                          + exportIndexClassName + "'.", e);
348         }
349       }
350 
351       return list;
352     }
353     return Collections.emptyList();
354   }
355 
356   private ReportConfig createReportConfig() throws MavenReportException
357   {
358     final InformationFilter.Builder builder = new InformationFilter.Builder();
359     builder.setShowIndexOfIndices(showIndexOfIndices);
360     builder.setShowNumbering(showNumbering);
361     builder.setShowCategories(showCategories);
362     builder.setShowTestCase(showTestCase);
363     builder.setShowTestStatus(showTestStatus);
364     final InformationFilter filter = builder.build();
365 
366     final SelectionFilter selectionFilter =
367         new SelectionFilter(includeCategories, excludeCategories);
368     final Comparator<UnitTestDoc> comparator = createComparator();
369     final ReportConfig config =
370         new ReportConfig(selectionFilter, filter, junitManager, imageHelper,
371             reports, comparator);
372     return config;
373   }
374 
375   @SuppressWarnings("unchecked")
376   private Comparator<UnitTestDoc> createComparator()
377     throws MavenReportException
378   {
379     try
380     {
381       final Class<Comparator<UnitTestDoc>> type =
382           (Class<Comparator<UnitTestDoc>>) Class.forName(unitTestDocComparator);
383       return type.newInstance();
384     }
385     catch (final ClassNotFoundException e)
386     {
387       throw new MavenReportException(
388           "Unknown comparator type for unit test doc instances '"
389               + unitTestDocComparator + "'.", e);
390     }
391     catch (final InstantiationException e)
392     {
393       throw new MavenReportException(
394           "Cannot instantiate unit test doc comparator of type '"
395               + unitTestDocComparator + "'.", e);
396     }
397     catch (final IllegalAccessException e)
398     {
399       throw new MavenReportException(
400           "Cannot access unit test doc comparator of type '"
401               + unitTestDocComparator + "'.", e);
402     }
403   }
404 
405   /**
406    * {@inheritDoc}
407    *
408    * @see org.apache.maven.reporting.AbstractMavenReport#canGenerateReport()
409    */
410   @Override
411   @SuppressWarnings("unchecked")
412   public boolean canGenerateReport()
413   {
414     final String packaging = project.getPackaging();
415     return super.canGenerateReport()
416            && !("pom".equals(packaging) || !PathUtils.exists(project
417                .getTestCompileSourceRoots()));
418   }
419 
420   // --- object basics --------------------------------------------------------
421 
422 }