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 org.apache.maven.doxia.sink.Sink;
23  import org.apache.maven.reporting.MavenReportException;
24  
25  import java.io.File;
26  import java.text.NumberFormat;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.ListIterator;
30  import java.util.Locale;
31  import java.util.Map;
32  import java.util.ResourceBundle;
33  import java.util.StringTokenizer;
34  
35  /**
36   * @version $Id: SurefireReportGenerator.java 1050534 2010-12-17 23:54:50Z hboutemy $
37   */
38  public class SurefireReportGenerator
39  {
40      private SurefireReportParser report;
41  
42      private List testSuites;
43  
44      private boolean showSuccess;
45  
46      private String xrefLocation;
47  
48      public SurefireReportGenerator( File[] reportsDirectories, Locale locale, boolean showSuccess, String xrefLocation )
49      {
50          report = new SurefireReportParser( reportsDirectories, locale );
51  
52          this.xrefLocation = xrefLocation;
53  
54          this.showSuccess = showSuccess;
55      }
56  
57      public void doGenerateReport( ResourceBundle bundle, Sink sink )
58          throws MavenReportException
59      {
60          testSuites = report.parseXMLReportFiles();
61  
62          sink.head();
63  
64          sink.title();
65          sink.text( bundle.getString( "report.surefire.header" ) );
66          sink.title_();
67  
68          StringBuffer str = new StringBuffer();
69          str.append( "<script type=\"text/javascript\">\n" );
70          str.append( "function toggleDisplay(elementId) {\n" );
71          str.append( " var elm = document.getElementById(elementId + 'error');\n" );
72          str.append( " if (elm && typeof elm.style != \"undefined\") {\n" );
73          str.append( " if (elm.style.display == \"none\") {\n" );
74          str.append( " elm.style.display = \"\";\n" );
75          str.append( " document.getElementById(elementId + 'off').style.display = \"none\";\n" );
76          str.append( " document.getElementById(elementId + 'on').style.display = \"inline\";\n" );
77          str.append( " }" );
78          str.append( " else if (elm.style.display == \"\") {" );
79          str.append( " elm.style.display = \"none\";\n" );
80          str.append( " document.getElementById(elementId + 'off').style.display = \"inline\";\n" );
81          str.append( " document.getElementById(elementId + 'on').style.display = \"none\";\n" );
82          str.append( " } \n" );
83          str.append( " } \n" );
84          str.append( " }\n" );
85          str.append( "</script>" );
86          sink.rawText( str.toString() );
87  
88          sink.head_();
89  
90          sink.body();
91  
92          sink.section1();
93          sink.sectionTitle1();
94          sink.text( bundle.getString( "report.surefire.header" ) );
95          sink.sectionTitle1_();
96          sink.section1_();
97  
98          constructSummarySection( bundle, sink );
99  
100         Map suitePackages = report.getSuitesGroupByPackage( testSuites );
101         if ( !suitePackages.isEmpty() )
102         {
103             constructPackagesSection( bundle, sink, suitePackages );
104         }
105 
106         if ( !testSuites.isEmpty() )
107         {
108             constructTestCasesSection( bundle, sink );
109         }
110 
111         List failureList = report.getFailureDetails( testSuites );
112         if ( !failureList.isEmpty() )
113         {
114             constructFailureDetails( sink, bundle, failureList );
115         }
116 
117         sink.body_();
118 
119         sink.flush();
120 
121         sink.close();
122     }
123 
124     private void constructSummarySection( ResourceBundle bundle, Sink sink )
125     {
126         Map summary = report.getSummary( testSuites );
127 
128         sink.section1();
129         sink.sectionTitle1();
130         sink.text( bundle.getString( "report.surefire.label.summary" ) );
131         sink.sectionTitle1_();
132 
133         sinkAnchor( sink, "Summary" );
134 
135         constructHotLinks( sink, bundle );
136 
137         sinkLineBreak( sink );
138 
139         sink.table();
140 
141         sink.tableRow();
142 
143         sinkHeader( sink, bundle.getString( "report.surefire.label.tests" ) );
144 
145         sinkHeader( sink, bundle.getString( "report.surefire.label.errors" ) );
146 
147         sinkHeader( sink, bundle.getString( "report.surefire.label.failures" ) );
148 
149         sinkHeader( sink, bundle.getString( "report.surefire.label.skipped" ) );
150 
151         sinkHeader( sink, bundle.getString( "report.surefire.label.successrate" ) );
152 
153         sinkHeader( sink, bundle.getString( "report.surefire.label.time" ) );
154 
155         sink.tableRow_();
156 
157         sink.tableRow();
158 
159         sinkCell( sink, (String) summary.get( "totalTests" ) );
160 
161         sinkCell( sink, (String) summary.get( "totalErrors" ) );
162 
163         sinkCell( sink, (String) summary.get( "totalFailures" ) );
164 
165         sinkCell( sink, (String) summary.get( "totalSkipped" ) );
166 
167         sinkCell( sink, summary.get( "totalPercentage" ) + "%" );
168 
169         sinkCell( sink, (String) summary.get( "totalElapsedTime" ) );
170 
171         sink.tableRow_();
172 
173         sink.table_();
174 
175         sink.lineBreak();
176 
177         sink.paragraph();
178         sink.rawText( bundle.getString( "report.surefire.text.note1" ) );
179         sink.paragraph_();
180 
181         sinkLineBreak( sink );
182 
183         sink.section1_();
184     }
185 
186     private void constructPackagesSection( ResourceBundle bundle, Sink sink, Map suitePackages )
187     {
188         NumberFormat numberFormat = report.getNumberFormat();
189 
190         sink.section1();
191         sink.sectionTitle1();
192         sink.text( bundle.getString( "report.surefire.label.packagelist" ) );
193         sink.sectionTitle1_();
194 
195         sinkAnchor( sink, "Package_List" );
196 
197         constructHotLinks( sink, bundle );
198 
199         sinkLineBreak( sink );
200 
201         sink.table();
202 
203         sink.tableRow();
204 
205         sinkHeader( sink, bundle.getString( "report.surefire.label.package" ) );
206 
207         sinkHeader( sink, bundle.getString( "report.surefire.label.tests" ) );
208 
209         sinkHeader( sink, bundle.getString( "report.surefire.label.errors" ) );
210 
211         sinkHeader( sink, bundle.getString( "report.surefire.label.failures" ) );
212 
213         sinkHeader( sink, bundle.getString( "report.surefire.label.skipped" ) );
214 
215         sinkHeader( sink, bundle.getString( "report.surefire.label.successrate" ) );
216 
217         sinkHeader( sink, bundle.getString( "report.surefire.label.time" ) );
218 
219         sink.tableRow_();
220 
221         Iterator packIter = suitePackages.keySet().iterator();
222 
223         while ( packIter.hasNext() )
224         {
225             sink.tableRow();
226 
227             String packageName = (String) packIter.next();
228 
229             List testSuiteList = (List) suitePackages.get( packageName );
230 
231             Map packageSummary = report.getSummary( testSuiteList );
232 
233             sinkCellLink( sink, packageName, "#" + packageName );
234 
235             sinkCell( sink, (String) packageSummary.get( "totalTests" ) );
236 
237             sinkCell( sink, (String) packageSummary.get( "totalErrors" ) );
238 
239             sinkCell( sink, (String) packageSummary.get( "totalFailures" ) );
240 
241             sinkCell( sink, (String) packageSummary.get( "totalSkipped" ) );
242 
243             sinkCell( sink, packageSummary.get( "totalPercentage" ) + "%" );
244 
245             sinkCell( sink, (String) packageSummary.get( "totalElapsedTime" ) );
246 
247             sink.tableRow_();
248         }
249 
250         sink.table_();
251 
252         sink.lineBreak();
253 
254         sink.paragraph();
255         sink.rawText( bundle.getString( "report.surefire.text.note2" ) );
256         sink.paragraph_();
257 
258         packIter = suitePackages.keySet().iterator();
259 
260         while ( packIter.hasNext() )
261         {
262             String packageName = (String) packIter.next();
263 
264             List testSuiteList = (List) suitePackages.get( packageName );
265 
266             Iterator suiteIterator = testSuiteList.iterator();
267 
268             sink.section2();
269             sink.sectionTitle2();
270             sink.text( packageName );
271             sink.sectionTitle2_();
272 
273             sinkAnchor( sink, packageName );
274 
275             sink.table();
276 
277             sink.tableRow();
278 
279             sinkHeader( sink, "" );
280 
281             sinkHeader( sink, bundle.getString( "report.surefire.label.class" ) );
282 
283             sinkHeader( sink, bundle.getString( "report.surefire.label.tests" ) );
284 
285             sinkHeader( sink, bundle.getString( "report.surefire.label.errors" ) );
286 
287             sinkHeader( sink, bundle.getString( "report.surefire.label.failures" ) );
288 
289             sinkHeader( sink, bundle.getString( "report.surefire.label.skipped" ) );
290 
291             sinkHeader( sink, bundle.getString( "report.surefire.label.successrate" ) );
292 
293             sinkHeader( sink, bundle.getString( "report.surefire.label.time" ) );
294 
295             sink.tableRow_();
296 
297             while ( suiteIterator.hasNext() )
298             {
299                 ReportTestSuite suite = (ReportTestSuite) suiteIterator.next();
300 
301                 if ( showSuccess || suite.getNumberOfErrors() != 0 || suite.getNumberOfFailures() != 0 )
302                 {
303 
304                     sink.tableRow();
305 
306                     sink.tableCell();
307 
308                     sink.link( "#" + suite.getPackageName() + suite.getName() );
309 
310                     if ( suite.getNumberOfErrors() > 0 )
311                     {
312                         sinkIcon( "error", sink );
313                     }
314                     else if ( suite.getNumberOfFailures() > 0 )
315                     {
316                         sinkIcon( "junit.framework", sink );
317                     }
318                     else if ( suite.getNumberOfSkipped() > 0 )
319                     {
320                         sinkIcon( "skipped", sink );
321                     }
322                     else
323                     {
324                         sinkIcon( "success", sink );
325                     }
326 
327                     sink.link_();
328 
329                     sink.tableCell_();
330 
331                     sinkCellLink( sink, suite.getName(), "#" + suite.getPackageName() + suite.getName() );
332 
333                     sinkCell( sink, Integer.toString( suite.getNumberOfTests() ) );
334 
335                     sinkCell( sink, Integer.toString( suite.getNumberOfErrors() ) );
336 
337                     sinkCell( sink, Integer.toString( suite.getNumberOfFailures() ) );
338 
339                     sinkCell( sink, Integer.toString( suite.getNumberOfSkipped() ) );
340 
341                     String percentage = report.computePercentage( suite.getNumberOfTests(), suite.getNumberOfErrors(),
342                                                                   suite.getNumberOfFailures(), suite
343                         .getNumberOfSkipped() );
344                     sinkCell( sink, percentage + "%" );
345 
346                     sinkCell( sink, numberFormat.format( suite.getTimeElapsed() ) );
347 
348                     sink.tableRow_();
349                 }
350             }
351 
352             sink.table_();
353 
354             sink.section2_();
355         }
356 
357         sinkLineBreak( sink );
358 
359         sink.section1_();
360     }
361 
362     private void constructTestCasesSection( ResourceBundle bundle, Sink sink )
363     {
364         NumberFormat numberFormat = report.getNumberFormat();
365 
366         sink.section1();
367         sink.sectionTitle1();
368         sink.text( bundle.getString( "report.surefire.label.testcases" ) );
369         sink.sectionTitle1_();
370 
371         sinkAnchor( sink, "Test_Cases" );
372 
373         constructHotLinks( sink, bundle );
374 
375         ListIterator suiteIterator = testSuites.listIterator();
376 
377         while ( suiteIterator.hasNext() )
378         {
379             ReportTestSuite suite = (ReportTestSuite) suiteIterator.next();
380 
381             List testCases = suite.getTestCases();
382 
383             if ( testCases != null && !testCases.isEmpty() )
384             {
385                 ListIterator caseIterator = testCases.listIterator();
386 
387                 sink.section2();
388                 sink.sectionTitle2();
389                 sink.text( suite.getName() );
390                 sink.sectionTitle2_();
391 
392                 sinkAnchor( sink, suite.getPackageName() + suite.getName() );
393 
394                 sink.table();
395 
396                 while ( caseIterator.hasNext() )
397                 {
398                     ReportTestCase testCase = (ReportTestCase) caseIterator.next();
399 
400                     if ( testCase.getFailure() != null || showSuccess )
401                     {
402                         sink.tableRow();
403 
404                         sink.tableCell();
405 
406                         Map failure = testCase.getFailure();
407 
408                         if ( failure != null )
409                         {
410                             sink.link( "#" + testCase.getFullName() );
411 
412                             sinkIcon( (String) failure.get( "type" ), sink );
413 
414                             sink.link_();
415                         }
416                         else
417                         {
418                             sinkIcon( "success", sink );
419                         }
420 
421                         sink.tableCell_();
422 
423                         if ( failure != null )
424                         {
425                             sink.tableCell();
426 
427                             sinkLink( sink, testCase.getName(), "#" + testCase.getFullName() );
428 
429                             sink.rawText( "  <div class=\"detailToggle\" style=\"display:inline\">" );
430 
431                             sink.link( "javascript:toggleDisplay('" + testCase.getName() + "');" );
432 
433                             sink.rawText( "<span style=\"display: inline;\" " + "id=\"" + testCase.getName()
434                                 + "off\">+</span><span id=\"" + testCase.getName() + "on\" "
435                                 + "style=\"display: none;\">-</span> " );
436                             sink.text( "[ Detail ]" );
437                             sink.link_();
438 
439                             sink.rawText( "</div>" );
440 
441                             sink.tableCell_();
442                         }
443                         else
444                         {
445                             sinkCell( sink, testCase.getName() );
446                         }
447 
448                         sinkCell( sink, numberFormat.format( testCase.getTime() ) );
449 
450                         sink.tableRow_();
451 
452                         if ( failure != null )
453                         {
454                             sink.tableRow();
455 
456                             sinkCell( sink, "" );
457                             sinkCell( sink, (String) failure.get( "message" ) );
458                             sinkCell( sink, "" );
459                             sink.tableRow_();
460 
461                             List detail = (List) failure.get( "detail" );
462                             if ( detail != null )
463                             {
464 
465                                 sink.tableRow();
466                                 sinkCell( sink, "" );
467 
468                                 sink.tableCell();
469                                 sink.rawText(
470                                     "  <div id=\"" + testCase.getName() + "error\" style=\"display:none;\">" );
471 
472                                 Iterator it = detail.iterator();
473 
474                                 sink.verbatim( true );
475                                 while ( it.hasNext() )
476                                 {
477                                     sink.text( it.next().toString() );
478                                     sink.lineBreak();
479                                 }
480                                 sink.verbatim_();
481 
482                                 sink.rawText( "</div>" );
483                                 sink.tableCell_();
484 
485                                 sinkCell( sink, "" );
486 
487                                 sink.tableRow_();
488                             }
489                         }
490                     }
491                 }
492 
493                 sink.table_();
494 
495                 sink.section2_();
496             }
497         }
498 
499         sinkLineBreak( sink );
500 
501         sink.section1_();
502     }
503 
504     private void constructFailureDetails( Sink sink, ResourceBundle bundle, List failureList )
505     {
506         Iterator failIter = failureList.iterator();
507 
508         if ( failIter != null )
509         {
510             sink.section1();
511             sink.sectionTitle1();
512             sink.text( bundle.getString( "report.surefire.label.failuredetails" ) );
513             sink.sectionTitle1_();
514 
515             sinkAnchor( sink, "Failure_Details" );
516 
517             constructHotLinks( sink, bundle );
518 
519             sinkLineBreak( sink );
520 
521             sink.table();
522 
523             while ( failIter.hasNext() )
524             {
525                 ReportTestCase tCase = (ReportTestCase) failIter.next();
526 
527                 Map failure = tCase.getFailure();
528 
529                 sink.tableRow();
530 
531                 sink.tableCell();
532 
533                 String type = (String) failure.get( "type" );
534                 sinkIcon( type, sink );
535 
536                 sink.tableCell_();
537 
538                 sinkCellAnchor( sink, tCase.getName(), tCase.getFullName() );
539 
540                 sink.tableRow_();
541 
542                 String message = (String) failure.get( "message" );
543 
544                 sink.tableRow();
545 
546                 sinkCell( sink, "" );
547 
548                 StringBuffer sb = new StringBuffer();
549                 sb.append( type );
550 
551                 if ( message != null )
552                 {
553                     sb.append( ": " );
554                     sb.append( message );
555                 }
556 
557                 sinkCell( sink, sb.toString() );
558 
559                 sink.tableRow_();
560 
561                 List detail = (List) failure.get( "detail" );
562                 if ( detail != null )
563                 {
564                     Iterator it = detail.iterator();
565 
566                     boolean firstLine = true;
567 
568                     String techMessage = "";
569                     while ( it.hasNext() )
570                     {
571                         techMessage = it.next().toString();
572                         if ( firstLine )
573                         {
574                             firstLine = false;
575                         }
576                         else
577                         {
578                             sink.text( "    " );
579                         }
580                     }
581 
582                     sink.tableRow();
583 
584                     sinkCell( sink, "" );
585 
586                     sink.tableCell();
587                     sink.rawText( "  <div id=\"" + tCase.getName() + "error\" >" );
588 
589                     if ( xrefLocation != null )
590                     {
591                         String path = tCase.getFullClassName().replace( '.', '/' );
592 
593                         sink.link( xrefLocation + "/" + path + ".html#" +
594                             getErrorLineNumber( tCase.getFullName(), techMessage ) );
595                     }
596                     sink.text(
597                         tCase.getFullClassName() + ":" + getErrorLineNumber( tCase.getFullName(), techMessage ) );
598 
599                     if ( xrefLocation != null )
600                     {
601                         sink.link_();
602                     }
603                     sink.rawText( "</div>" );
604 
605                     sink.tableCell_();
606 
607                     sink.tableRow_();
608                 }
609             }
610 
611             sink.table_();
612         }
613 
614         sinkLineBreak( sink );
615 
616         sink.section1_();
617     }
618 
619     private String getErrorLineNumber( String className, String source )
620     {
621         StringTokenizer tokenizer = new StringTokenizer( source );
622 
623         String lineNo = "";
624 
625         while ( tokenizer.hasMoreTokens() )
626         {
627             String token = tokenizer.nextToken();
628             if ( token.startsWith( className ) )
629             {
630                 int idx = token.indexOf( ":" );
631                 lineNo = token.substring( idx + 1, token.indexOf( ")" ) );
632                 break;
633             }
634         }
635         return lineNo;
636     }
637 
638     private void constructHotLinks( Sink sink, ResourceBundle bundle )
639     {
640         if ( !testSuites.isEmpty() )
641         {
642             sink.paragraph();
643 
644             sink.text( "[" );
645             sinkLink( sink, bundle.getString( "report.surefire.label.summary" ), "#Summary" );
646             sink.text( "]" );
647 
648             sink.text( " [" );
649             sinkLink( sink, bundle.getString( "report.surefire.label.packagelist" ), "#Package_List" );
650             sink.text( "]" );
651 
652             sink.text( " [" );
653             sinkLink( sink, bundle.getString( "report.surefire.label.testcases" ), "#Test_Cases" );
654             sink.text( "]" );
655 
656             sink.paragraph_();
657         }
658     }
659 
660     private void sinkLineBreak( Sink sink )
661     {
662         sink.lineBreak();
663     }
664 
665     private void sinkIcon( String type, Sink sink )
666     {
667         sink.figure();
668 
669         if ( type.startsWith( "junit.framework" ) || "skipped".equals( type ) )
670         {
671             sink.figureGraphics( "images/icon_warning_sml.gif" );
672         }
673         else if ( type.startsWith( "success" ) )
674         {
675             sink.figureGraphics( "images/icon_success_sml.gif" );
676         }
677         else
678         {
679             sink.figureGraphics( "images/icon_error_sml.gif" );
680         }
681 
682         sink.figure_();
683     }
684 
685     private void sinkHeader( Sink sink, String header )
686     {
687         sink.tableHeaderCell();
688         sink.text( header );
689         sink.tableHeaderCell_();
690     }
691 
692     private void sinkCell( Sink sink, String text )
693     {
694         sink.tableCell();
695         sink.text( text );
696         sink.tableCell_();
697     }
698 
699     private void sinkLink( Sink sink, String text, String link )
700     {
701         sink.link( link );
702         sink.text( text );
703         sink.link_();
704     }
705 
706     private void sinkCellLink( Sink sink, String text, String link )
707     {
708         sink.tableCell();
709         sinkLink( sink, text, link );
710         sink.tableCell_();
711     }
712 
713     private void sinkCellAnchor( Sink sink, String text, String anchor )
714     {
715         sink.tableCell();
716         sinkAnchor( sink, anchor );
717         sink.text( text );
718         sink.tableCell_();
719     }
720 
721     private void sinkAnchor( Sink sink, String anchor )
722     {
723         sink.anchor( anchor );
724         sink.anchor_();
725     }
726 }