View Javadoc

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