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