This is an automated email from the ASF dual-hosted git repository.

michaelo pushed a commit to branch use-renderer
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git

commit 7b26907a6c666b17ef8422beb8d20631ad10c7ed
Author: Michael Osipov <micha...@apache.org>
AuthorDate: Mon May 1 01:04:33 2023 +0200

    [SUREFIRE-2153] Replace SurefireReportGenerator with a new 
SurefireReportRenderer
    
    This closes #625
---
 .../report/AbstractSurefireReportMojo.java         |  12 +-
 .../surefire/report/SurefireReportGenerator.java   | 697 ---------------------
 .../surefire/report/SurefireReportRenderer.java    | 600 ++++++++++++++++++
 .../src/main/resources/surefire-report.properties  |   2 +-
 .../plugins/surefire/report/Surefire597Test.java   |  63 +-
 .../surefire/report/SurefireReportMojoTest.java    | 247 ++++----
 .../resources/unit/surefire-1183/plugin-config.xml |   3 +-
 .../jiras/Surefire260TestWithIdenticalNamesIT.java |   5 +-
 .../surefire/report/SurefireReportParser.java      |  47 +-
 .../surefire/report/SurefireReportParserTest.java  |  41 +-
 10 files changed, 789 insertions(+), 928 deletions(-)

diff --git 
a/maven-surefire-report-plugin/src/main/java/org/apache/maven/plugins/surefire/report/AbstractSurefireReportMojo.java
 
b/maven-surefire-report-plugin/src/main/java/org/apache/maven/plugins/surefire/report/AbstractSurefireReportMojo.java
index eaaf294f1..44f107b01 100644
--- 
a/maven-surefire-report-plugin/src/main/java/org/apache/maven/plugins/surefire/report/AbstractSurefireReportMojo.java
+++ 
b/maven-surefire-report-plugin/src/main/java/org/apache/maven/plugins/surefire/report/AbstractSurefireReportMojo.java
@@ -126,9 +126,15 @@ public abstract class AbstractSurefireReportMojo extends 
AbstractMavenReport {
             return;
         }
 
-        new SurefireReportGenerator(
-                        getReportsDirectories(), locale, showSuccess, 
determineXrefLocation(), getConsoleLogger())
-                .doGenerateReport(getBundle(locale), getSink());
+        SurefireReportRenderer r = new SurefireReportRenderer(
+                getSink(),
+                locale,
+                getBundle(locale),
+                getConsoleLogger(),
+                showSuccess,
+                getReportsDirectories(),
+                determineXrefLocation());
+        r.render();
     }
 
     @Override
diff --git 
a/maven-surefire-report-plugin/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportGenerator.java
 
b/maven-surefire-report-plugin/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportGenerator.java
deleted file mode 100644
index 09667a06a..000000000
--- 
a/maven-surefire-report-plugin/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportGenerator.java
+++ /dev/null
@@ -1,697 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.maven.plugins.surefire.report;
-
-import java.io.File;
-import java.text.NumberFormat;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-import org.apache.maven.doxia.markup.HtmlMarkup;
-import org.apache.maven.doxia.sink.Sink;
-import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
-import org.apache.maven.doxia.util.DoxiaUtils;
-import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
-import org.apache.maven.reporting.MavenReportException;
-
-import static org.apache.maven.doxia.markup.HtmlMarkup.A;
-import static org.apache.maven.doxia.sink.Sink.JUSTIFY_LEFT;
-import static org.apache.maven.doxia.sink.SinkEventAttributes.CLASS;
-import static org.apache.maven.doxia.sink.SinkEventAttributes.HREF;
-import static org.apache.maven.doxia.sink.SinkEventAttributes.ID;
-import static org.apache.maven.doxia.sink.SinkEventAttributes.STYLE;
-import static org.apache.maven.doxia.sink.SinkEventAttributes.TYPE;
-
-/**
- * This generator creates HTML Report from Surefire and Failsafe XML Report.
- */
-public final class SurefireReportGenerator {
-    private static final int LEFT = JUSTIFY_LEFT;
-    private static final Object[] TAG_TYPE_START = {HtmlMarkup.TAG_TYPE_START};
-    private static final Object[] TAG_TYPE_END = {HtmlMarkup.TAG_TYPE_END};
-
-    private final SurefireReportParser report;
-    private final boolean showSuccess;
-    private final String xrefLocation;
-    private List<ReportTestSuite> testSuites;
-
-    public SurefireReportGenerator(
-            List<File> reportsDirectories,
-            Locale locale,
-            boolean showSuccess,
-            String xrefLocation,
-            ConsoleLogger consoleLogger) {
-        report = new SurefireReportParser(reportsDirectories, locale, 
consoleLogger);
-        this.showSuccess = showSuccess;
-        this.xrefLocation = xrefLocation;
-    }
-
-    public void doGenerateReport(LocalizedProperties bundle, Sink sink) throws 
MavenReportException {
-        testSuites = report.parseXMLReportFiles();
-
-        sink.head();
-
-        sink.title();
-        sink.text(bundle.getReportHeader());
-        sink.title_();
-
-        sink.head_();
-
-        sink.body();
-
-        SinkEventAttributeSet atts = new SinkEventAttributeSet();
-        atts.addAttribute(TYPE, "application/javascript");
-        sink.unknown("script", new Object[] {HtmlMarkup.TAG_TYPE_START}, atts);
-        sink.unknown("cdata", new Object[] {HtmlMarkup.CDATA_TYPE, 
javascriptToggleDisplayCode()}, null);
-        sink.unknown("script", new Object[] {HtmlMarkup.TAG_TYPE_END}, null);
-
-        sink.section1();
-        sink.sectionTitle1();
-        sink.text(bundle.getReportHeader());
-        sink.sectionTitle1_();
-        sink.section1_();
-
-        constructSummarySection(bundle, sink);
-
-        Map<String, List<ReportTestSuite>> suitePackages = 
report.getSuitesGroupByPackage(testSuites);
-        if (!suitePackages.isEmpty()) {
-            constructPackagesSection(bundle, sink, suitePackages);
-        }
-
-        if (!testSuites.isEmpty()) {
-            constructTestCasesSection(bundle, sink);
-        }
-
-        List<ReportTestCase> failureList = 
report.getFailureDetails(testSuites);
-        if (!failureList.isEmpty()) {
-            constructFailureDetails(sink, bundle, failureList);
-        }
-
-        sink.body_();
-
-        sink.flush();
-
-        sink.close();
-    }
-
-    private void constructSummarySection(LocalizedProperties bundle, Sink 
sink) {
-        Map<String, String> summary = report.getSummary(testSuites);
-
-        sink.section1();
-        sinkAnchor(sink, "Summary");
-        sink.sectionTitle1();
-        sink.text(bundle.getReportLabelSummary());
-        sink.sectionTitle1_();
-
-        constructHotLinks(sink, bundle);
-
-        sinkLineBreak(sink);
-
-        sink.table();
-
-        sink.tableRows(new int[] {LEFT, LEFT, LEFT, LEFT, LEFT, LEFT}, false);
-
-        sink.tableRow();
-
-        sinkHeader(sink, bundle.getReportLabelTests());
-
-        sinkHeader(sink, bundle.getReportLabelErrors());
-
-        sinkHeader(sink, bundle.getReportLabelFailures());
-
-        sinkHeader(sink, bundle.getReportLabelSkipped());
-
-        sinkHeader(sink, bundle.getReportLabelSuccessRate());
-
-        sinkHeader(sink, bundle.getReportLabelTime());
-
-        sink.tableRow_();
-
-        sink.tableRow();
-
-        sinkCell(sink, summary.get("totalTests"));
-
-        sinkCell(sink, summary.get("totalErrors"));
-
-        sinkCell(sink, summary.get("totalFailures"));
-
-        sinkCell(sink, summary.get("totalSkipped"));
-
-        sinkCell(sink, summary.get("totalPercentage") + "%");
-
-        sinkCell(sink, summary.get("totalElapsedTime"));
-
-        sink.tableRow_();
-
-        sink.tableRows_();
-
-        sink.table_();
-
-        sink.lineBreak();
-
-        sink.paragraph();
-        sink.text(bundle.getReportTextNode1());
-        sink.paragraph_();
-
-        sinkLineBreak(sink);
-
-        sink.section1_();
-    }
-
-    private void constructPackagesSection(
-            LocalizedProperties bundle, Sink sink, Map<String, 
List<ReportTestSuite>> suitePackages) {
-        NumberFormat numberFormat = report.getNumberFormat();
-
-        sink.section1();
-        sinkAnchor(sink, "Package_List");
-        sink.sectionTitle1();
-        sink.text(bundle.getReportLabelPackageList());
-        sink.sectionTitle1_();
-
-        constructHotLinks(sink, bundle);
-
-        sinkLineBreak(sink);
-
-        sink.table();
-
-        sink.tableRows(new int[] {LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT}, 
false);
-
-        sink.tableRow();
-
-        sinkHeader(sink, bundle.getReportLabelPackage());
-
-        sinkHeader(sink, bundle.getReportLabelTests());
-
-        sinkHeader(sink, bundle.getReportLabelErrors());
-
-        sinkHeader(sink, bundle.getReportLabelFailures());
-
-        sinkHeader(sink, bundle.getReportLabelSkipped());
-
-        sinkHeader(sink, bundle.getReportLabelSuccessRate());
-
-        sinkHeader(sink, bundle.getReportLabelTime());
-
-        sink.tableRow_();
-
-        for (Map.Entry<String, List<ReportTestSuite>> entry : 
suitePackages.entrySet()) {
-            sink.tableRow();
-
-            String packageName = entry.getKey();
-
-            List<ReportTestSuite> testSuiteList = entry.getValue();
-
-            Map<String, String> packageSummary = 
report.getSummary(testSuiteList);
-
-            sinkCellLink(sink, packageName, "#" + packageName);
-
-            sinkCell(sink, packageSummary.get("totalTests"));
-
-            sinkCell(sink, packageSummary.get("totalErrors"));
-
-            sinkCell(sink, packageSummary.get("totalFailures"));
-
-            sinkCell(sink, packageSummary.get("totalSkipped"));
-
-            sinkCell(sink, packageSummary.get("totalPercentage") + "%");
-
-            sinkCell(sink, packageSummary.get("totalElapsedTime"));
-
-            sink.tableRow_();
-        }
-
-        sink.tableRows_();
-
-        sink.table_();
-
-        sink.lineBreak();
-
-        sink.paragraph();
-        sink.text(bundle.getReportTextNode2());
-        sink.paragraph_();
-
-        for (Map.Entry<String, List<ReportTestSuite>> entry : 
suitePackages.entrySet()) {
-            String packageName = entry.getKey();
-
-            List<ReportTestSuite> testSuiteList = entry.getValue();
-
-            sink.section2();
-            sinkAnchor(sink, packageName);
-            sink.sectionTitle2();
-            sink.text(packageName);
-            sink.sectionTitle2_();
-
-            boolean showTable = false;
-
-            for (ReportTestSuite suite : testSuiteList) {
-                if (showSuccess || suite.getNumberOfErrors() != 0 || 
suite.getNumberOfFailures() != 0) {
-                    showTable = true;
-
-                    break;
-                }
-            }
-
-            if (showTable) {
-                sink.table();
-
-                sink.tableRows(new int[] {LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, 
LEFT, LEFT}, false);
-
-                sink.tableRow();
-
-                sinkHeader(sink, "");
-
-                sinkHeader(sink, bundle.getReportLabelClass());
-
-                sinkHeader(sink, bundle.getReportLabelTests());
-
-                sinkHeader(sink, bundle.getReportLabelErrors());
-
-                sinkHeader(sink, bundle.getReportLabelFailures());
-
-                sinkHeader(sink, bundle.getReportLabelSkipped());
-
-                sinkHeader(sink, bundle.getReportLabelSuccessRate());
-
-                sinkHeader(sink, bundle.getReportLabelTime());
-
-                sink.tableRow_();
-
-                for (ReportTestSuite suite : testSuiteList) {
-                    if (showSuccess || suite.getNumberOfErrors() != 0 || 
suite.getNumberOfFailures() != 0) {
-                        constructTestSuiteSection(sink, numberFormat, suite);
-                    }
-                }
-
-                sink.tableRows_();
-
-                sink.table_();
-            }
-
-            sink.section2_();
-        }
-
-        sinkLineBreak(sink);
-
-        sink.section1_();
-    }
-
-    private void constructTestSuiteSection(Sink sink, NumberFormat 
numberFormat, ReportTestSuite suite) {
-        sink.tableRow();
-
-        sink.tableCell();
-
-        sink.link("#" + suite.getPackageName() + '.' + suite.getName());
-
-        if (suite.getNumberOfErrors() > 0) {
-            sinkIcon("error", sink);
-        } else if (suite.getNumberOfFailures() > 0) {
-            sinkIcon("junit.framework", sink);
-        } else if (suite.getNumberOfSkipped() > 0) {
-            sinkIcon("skipped", sink);
-        } else {
-            sinkIcon("success", sink);
-        }
-
-        sink.link_();
-
-        sink.tableCell_();
-
-        sinkCellLink(sink, suite.getName(), "#" + suite.getPackageName() + '.' 
+ suite.getName());
-
-        sinkCell(sink, Integer.toString(suite.getNumberOfTests()));
-
-        sinkCell(sink, Integer.toString(suite.getNumberOfErrors()));
-
-        sinkCell(sink, Integer.toString(suite.getNumberOfFailures()));
-
-        sinkCell(sink, Integer.toString(suite.getNumberOfSkipped()));
-
-        String percentage = report.computePercentage(
-                suite.getNumberOfTests(), suite.getNumberOfErrors(),
-                suite.getNumberOfFailures(), suite.getNumberOfSkipped());
-        sinkCell(sink, percentage + "%");
-
-        sinkCell(sink, numberFormat.format(suite.getTimeElapsed()));
-
-        sink.tableRow_();
-    }
-
-    private void constructTestCasesSection(LocalizedProperties bundle, Sink 
sink) {
-        NumberFormat numberFormat = report.getNumberFormat();
-
-        sink.section1();
-        sinkAnchor(sink, "Test_Cases");
-        sink.sectionTitle1();
-        sink.text(bundle.getReportLabelTestCases());
-        sink.sectionTitle1_();
-
-        constructHotLinks(sink, bundle);
-
-        for (ReportTestSuite suite : testSuites) {
-            List<ReportTestCase> testCases = suite.getTestCases();
-
-            if (!testCases.isEmpty()) {
-                sink.section2();
-                sinkAnchor(sink, suite.getPackageName() + '.' + 
suite.getName());
-                sink.sectionTitle2();
-                sink.text(suite.getName());
-                sink.sectionTitle2_();
-
-                boolean showTable = false;
-
-                for (ReportTestCase testCase : testCases) {
-                    if (!testCase.isSuccessful() || showSuccess) {
-                        showTable = true;
-
-                        break;
-                    }
-                }
-
-                if (showTable) {
-                    sink.table();
-
-                    sink.tableRows(new int[] {LEFT, LEFT, LEFT}, false);
-
-                    for (ReportTestCase testCase : testCases) {
-                        if (!testCase.isSuccessful() || showSuccess) {
-                            constructTestCaseSection(sink, numberFormat, 
testCase);
-                        }
-                    }
-
-                    sink.tableRows_();
-
-                    sink.table_();
-                }
-
-                sink.section2_();
-            }
-        }
-
-        sinkLineBreak(sink);
-
-        sink.section1_();
-    }
-
-    private static void constructTestCaseSection(Sink sink, NumberFormat 
numberFormat, ReportTestCase testCase) {
-        sink.tableRow();
-
-        sink.tableCell();
-
-        if (testCase.getFailureType() != null) {
-            sink.link("#" + toHtmlId(testCase.getFullName()));
-
-            sinkIcon(testCase.getFailureType(), sink);
-
-            sink.link_();
-        } else {
-            sinkIcon("success", sink);
-        }
-
-        sink.tableCell_();
-
-        if (!testCase.isSuccessful()) {
-            sink.tableCell();
-            sinkAnchor(sink, "TC_" + toHtmlId(testCase.getFullName()));
-
-            sinkLink(sink, testCase.getName(), "#" + 
toHtmlId(testCase.getFullName()));
-
-            SinkEventAttributeSet atts = new SinkEventAttributeSet();
-            atts.addAttribute(CLASS, "detailToggle");
-            atts.addAttribute(STYLE, "display:inline");
-            sink.unknown("div", TAG_TYPE_START, atts);
-
-            sinkLink(sink, "javascript:toggleDisplay('" + 
toHtmlId(testCase.getFullName()) + "');");
-
-            atts = new SinkEventAttributeSet();
-            atts.addAttribute(STYLE, "display:inline;");
-            atts.addAttribute(ID, toHtmlId(testCase.getFullName()) + "-off");
-            sink.unknown("span", TAG_TYPE_START, atts);
-            sink.text(" + ");
-            sink.unknown("span", TAG_TYPE_END, null);
-
-            atts = new SinkEventAttributeSet();
-            atts.addAttribute(STYLE, "display:none;");
-            atts.addAttribute(ID, toHtmlId(testCase.getFullName()) + "-on");
-            sink.unknown("span", TAG_TYPE_START, atts);
-            sink.text(" - ");
-            sink.unknown("span", TAG_TYPE_END, null);
-
-            sink.text("[ Detail ]");
-            sinkLink_(sink);
-
-            sink.unknown("div", TAG_TYPE_END, null);
-
-            sink.tableCell_();
-        } else {
-            sinkCellAnchor(sink, testCase.getName(), "TC_" + 
toHtmlId(testCase.getFullName()));
-        }
-
-        sinkCell(sink, numberFormat.format(testCase.getTime()));
-
-        sink.tableRow_();
-
-        if (!testCase.isSuccessful()) {
-            sink.tableRow();
-
-            sinkCell(sink, "");
-            sinkCell(sink, testCase.getFailureMessage());
-            sinkCell(sink, "");
-            sink.tableRow_();
-
-            String detail = testCase.getFailureDetail();
-            if (detail != null) {
-                sink.tableRow();
-                sinkCell(sink, "");
-
-                sink.tableCell();
-                SinkEventAttributeSet atts = new SinkEventAttributeSet();
-                atts.addAttribute(ID, toHtmlId(testCase.getFullName()) + 
toHtmlIdFailure(testCase));
-                atts.addAttribute(STYLE, "display:none;");
-                sink.unknown("div", TAG_TYPE_START, atts);
-
-                sink.verbatim(null);
-                sink.text(detail);
-                sink.verbatim_();
-
-                sink.unknown("div", TAG_TYPE_END, null);
-                sink.tableCell_();
-
-                sinkCell(sink, "");
-
-                sink.tableRow_();
-            }
-        }
-    }
-
-    private static String toHtmlId(String id) {
-        return DoxiaUtils.isValidId(id) ? id : DoxiaUtils.encodeId(id, true);
-    }
-
-    private void constructFailureDetails(Sink sink, LocalizedProperties 
bundle, List<ReportTestCase> failures) {
-        sink.section1();
-        sinkAnchor(sink, "Failure_Details");
-        sink.sectionTitle1();
-        sink.text(bundle.getReportLabelFailureDetails());
-        sink.sectionTitle1_();
-
-        constructHotLinks(sink, bundle);
-
-        sinkLineBreak(sink);
-
-        sink.table();
-
-        sink.tableRows(new int[] {LEFT, LEFT}, false);
-
-        for (ReportTestCase tCase : failures) {
-            sink.tableRow();
-
-            sink.tableCell();
-
-            String type = tCase.getFailureType();
-
-            sinkIcon(type, sink);
-
-            sink.tableCell_();
-
-            sinkCellAnchor(sink, tCase.getName(), 
toHtmlId(tCase.getFullName()));
-
-            sink.tableRow_();
-
-            String message = tCase.getFailureMessage();
-
-            sink.tableRow();
-
-            sinkCell(sink, "");
-
-            sinkCell(sink, message == null ? type : type + ": " + message);
-
-            sink.tableRow_();
-
-            String detail = tCase.getFailureDetail();
-            if (detail != null) {
-                sink.tableRow();
-
-                sinkCell(sink, "");
-
-                sink.tableCell();
-                SinkEventAttributeSet atts = new SinkEventAttributeSet();
-                atts.addAttribute(ID, tCase.getName() + 
toHtmlIdFailure(tCase));
-                sink.unknown("div", TAG_TYPE_START, atts);
-
-                String fullClassName = tCase.getFullClassName();
-                String errorLineNumber = tCase.getFailureErrorLine();
-                if (xrefLocation != null) {
-                    String path = fullClassName.replace('.', '/');
-                    sink.link(xrefLocation + "/" + path + ".html#L" + 
errorLineNumber);
-                }
-                sink.text(fullClassName + ":" + errorLineNumber);
-
-                if (xrefLocation != null) {
-                    sink.link_();
-                }
-                sink.unknown("div", TAG_TYPE_END, null);
-
-                sink.tableCell_();
-
-                sink.tableRow_();
-            }
-        }
-
-        sink.tableRows_();
-
-        sink.table_();
-
-        sinkLineBreak(sink);
-
-        sink.section1_();
-    }
-
-    private void constructHotLinks(Sink sink, LocalizedProperties bundle) {
-        if (!testSuites.isEmpty()) {
-            sink.paragraph();
-
-            sink.text("[");
-            sinkLink(sink, bundle.getReportLabelSummary(), "#Summary");
-            sink.text("]");
-
-            sink.text(" [");
-            sinkLink(sink, bundle.getReportLabelPackageList(), 
"#Package_List");
-            sink.text("]");
-
-            sink.text(" [");
-            sinkLink(sink, bundle.getReportLabelTestCases(), "#Test_Cases");
-            sink.text("]");
-
-            sink.paragraph_();
-        }
-    }
-
-    private static String toHtmlIdFailure(ReportTestCase tCase) {
-        return tCase.hasError() ? "-error" : "-failure";
-    }
-
-    private static void sinkLineBreak(Sink sink) {
-        sink.lineBreak();
-    }
-
-    private static void sinkIcon(String type, Sink sink) {
-        if (type.startsWith("junit.framework") || "skipped".equals(type)) {
-            sink.figureGraphics("images/icon_warning_sml.gif");
-        } else if (type.startsWith("success")) {
-            sink.figureGraphics("images/icon_success_sml.gif");
-        } else {
-            sink.figureGraphics("images/icon_error_sml.gif");
-        }
-    }
-
-    private static void sinkHeader(Sink sink, String header) {
-        sink.tableHeaderCell();
-        sink.text(header);
-        sink.tableHeaderCell_();
-    }
-
-    private static void sinkCell(Sink sink, String text) {
-        sink.tableCell();
-        sink.text(text);
-        sink.tableCell_();
-    }
-
-    private static void sinkLink(Sink sink, String text, String link) {
-        sink.link(link);
-        sink.text(text);
-        sink.link_();
-    }
-
-    private static void sinkCellLink(Sink sink, String text, String link) {
-        sink.tableCell();
-        sinkLink(sink, text, link);
-        sink.tableCell_();
-    }
-
-    private static void sinkCellAnchor(Sink sink, String text, String anchor) {
-        sink.tableCell();
-        sinkAnchor(sink, anchor);
-        sink.text(text);
-        sink.tableCell_();
-    }
-
-    private static void sinkAnchor(Sink sink, String anchor) {
-        // Dollar '$' for nested classes is not valid character in 
sink.anchor() and therefore it is ignored
-        // https://issues.apache.org/jira/browse/SUREFIRE-1443
-        sink.unknown(A.toString(), TAG_TYPE_START, new 
SinkEventAttributeSet(ID, anchor));
-        sink.unknown(A.toString(), TAG_TYPE_END, null);
-    }
-
-    private static void sinkLink(Sink sink, String href) {
-        // The "'" argument in this JavaScript function would be escaped to 
"&apos;"
-        // sink.link( "javascript:toggleDisplay('" + toHtmlId( 
testCase.getFullName() ) + "');" );
-        sink.unknown(A.toString(), TAG_TYPE_START, new 
SinkEventAttributeSet(HREF, href));
-    }
-
-    @SuppressWarnings("checkstyle:methodname")
-    private static void sinkLink_(Sink sink) {
-        sink.unknown(A.toString(), TAG_TYPE_END, null);
-    }
-
-    private static String javascriptToggleDisplayCode() {
-
-        // the javascript code is emitted within a commented CDATA section
-        // so we have to start with a newline and comment the CDATA closing in 
the end
-
-        return "\n" + "function toggleDisplay(elementId) {\n"
-                + " var elm = document.getElementById(elementId + '-error');\n"
-                + " if (elm == null) {\n"
-                + "  elm = document.getElementById(elementId + '-failure');\n"
-                + " }\n"
-                + " if (elm && typeof elm.style != \"undefined\") {\n"
-                + "  if (elm.style.display == \"none\") {\n"
-                + "   elm.style.display = \"\";\n"
-                + "   document.getElementById(elementId + 
'-off').style.display = \"none\";\n"
-                + "   document.getElementById(elementId + '-on').style.display 
= \"inline\";\n"
-                + "  } else if (elm.style.display == \"\") {"
-                + "   elm.style.display = \"none\";\n"
-                + "   document.getElementById(elementId + 
'-off').style.display = \"inline\";\n"
-                + "   document.getElementById(elementId + '-on').style.display 
= \"none\";\n"
-                + "  } \n"
-                + " } \n"
-                + " }\n"
-                + "//";
-    }
-}
diff --git 
a/maven-surefire-report-plugin/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportRenderer.java
 
b/maven-surefire-report-plugin/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportRenderer.java
new file mode 100644
index 000000000..f7684c551
--- /dev/null
+++ 
b/maven-surefire-report-plugin/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportRenderer.java
@@ -0,0 +1,600 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugins.surefire.report;
+
+import java.io.File;
+import java.text.NumberFormat;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.maven.doxia.markup.HtmlMarkup;
+import org.apache.maven.doxia.markup.Markup;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
+import org.apache.maven.doxia.util.DoxiaUtils;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.reporting.AbstractMavenReportRenderer;
+
+import static org.apache.maven.doxia.markup.HtmlMarkup.A;
+import static org.apache.maven.doxia.sink.SinkEventAttributes.CLASS;
+import static org.apache.maven.doxia.sink.SinkEventAttributes.HREF;
+import static org.apache.maven.doxia.sink.SinkEventAttributes.ID;
+import static org.apache.maven.doxia.sink.SinkEventAttributes.STYLE;
+
+/**
+ * This generator creates HTML Report from Surefire and Failsafe XML Report.
+ */
+public class SurefireReportRenderer extends AbstractMavenReportRenderer {
+    private static final Object[] TAG_TYPE_START = {HtmlMarkup.TAG_TYPE_START};
+    private static final Object[] TAG_TYPE_END = {HtmlMarkup.TAG_TYPE_END};
+
+    // Not used at the moment
+    private final Locale locale;
+    private final LocalizedProperties bundle;
+
+    private final SurefireReportParser parser;
+    private final boolean showSuccess;
+    private final String xrefLocation;
+    private final List<ReportTestSuite> testSuites;
+
+    public SurefireReportRenderer(
+            Sink sink,
+            Locale locale,
+            LocalizedProperties bundle,
+            ConsoleLogger consoleLogger,
+            boolean showSuccess,
+            List<File> reportsDirectories,
+            String xrefLocation) {
+        super(sink);
+        this.locale = locale;
+        this.bundle = bundle;
+        parser = new SurefireReportParser(reportsDirectories, consoleLogger);
+        testSuites = parser.parseXMLReportFiles();
+        this.showSuccess = showSuccess;
+        this.xrefLocation = xrefLocation;
+    }
+
+    @Override
+    public String getTitle() {
+        return bundle.getReportHeader();
+    }
+
+    public void renderBody() {
+        javaScript(javascriptToggleDisplayCode());
+
+        sink.section1();
+        sink.sectionTitle1();
+        sink.text(getTitle());
+        sink.sectionTitle1_();
+        sink.section1_();
+
+        renderSectionSummary();
+
+        renderSectionPackages();
+
+        renderSectionTestCases();
+
+        renderSectionFailureDetails();
+    }
+
+    private void renderSectionSummary() {
+        Map<String, Object> summary = parser.getSummary(testSuites);
+        NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
+        NumberFormat percentFormat = NumberFormat.getPercentInstance(locale);
+        percentFormat.setMinimumFractionDigits(1);
+
+        sink.section1();
+        sinkAnchor("Summary");
+        sink.sectionTitle1();
+        sink.text(bundle.getReportLabelSummary());
+        sink.sectionTitle1_();
+
+        constructHotLinks();
+
+        sink.lineBreak();
+
+        startTable();
+
+        tableHeader(new String[] {
+            bundle.getReportLabelTests(),
+            bundle.getReportLabelErrors(),
+            bundle.getReportLabelFailures(),
+            bundle.getReportLabelSkipped(),
+            bundle.getReportLabelSuccessRate(),
+            bundle.getReportLabelTime()
+        });
+
+        tableRow(new String[] {
+            String.valueOf(summary.get("totalTests")),
+            String.valueOf(summary.get("totalErrors")),
+            String.valueOf(summary.get("totalFailures")),
+            String.valueOf(summary.get("totalSkipped")),
+            percentFormat.format(summary.get("totalPercentage")),
+            numberFormat.format(summary.get("totalElapsedTime")) + " s"
+        });
+
+        endTable();
+
+        sink.lineBreak();
+
+        paragraph(bundle.getReportTextNode1());
+
+        sink.lineBreak();
+
+        sink.section1_();
+    }
+
+    private void renderSectionPackages() {
+        Map<String, List<ReportTestSuite>> suitePackages = 
parser.getSuitesGroupByPackage(testSuites);
+        if (suitePackages.isEmpty()) {
+            return;
+        }
+
+        NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
+        NumberFormat percentFormat = NumberFormat.getPercentInstance(locale);
+        percentFormat.setMinimumFractionDigits(1);
+
+        sink.section1();
+        sinkAnchor("Package_List");
+        sink.sectionTitle1();
+        sink.text(bundle.getReportLabelPackageList());
+        sink.sectionTitle1_();
+
+        constructHotLinks();
+
+        sink.lineBreak();
+
+        startTable();
+
+        tableHeader(new String[] {
+            bundle.getReportLabelPackage(),
+            bundle.getReportLabelTests(),
+            bundle.getReportLabelErrors(),
+            bundle.getReportLabelFailures(),
+            bundle.getReportLabelSkipped(),
+            bundle.getReportLabelSuccessRate(),
+            bundle.getReportLabelTime()
+        });
+
+        for (Map.Entry<String, List<ReportTestSuite>> entry : 
suitePackages.entrySet()) {
+            String packageName = entry.getKey();
+
+            List<ReportTestSuite> testSuiteList = entry.getValue();
+
+            Map<String, Object> packageSummary = 
parser.getSummary(testSuiteList);
+
+            tableRow(new String[] {
+                '{' + packageName + ", #" + packageName + '}',
+                String.valueOf(packageSummary.get("totalTests")),
+                String.valueOf(packageSummary.get("totalErrors")),
+                String.valueOf(packageSummary.get("totalFailures")),
+                String.valueOf(packageSummary.get("totalSkipped")),
+                percentFormat.format(packageSummary.get("totalPercentage")),
+                numberFormat.format(packageSummary.get("totalElapsedTime")) + 
" s"
+            });
+        }
+
+        endTable();
+        sink.lineBreak();
+
+        paragraph(bundle.getReportTextNode2());
+
+        for (Map.Entry<String, List<ReportTestSuite>> entry : 
suitePackages.entrySet()) {
+            String packageName = entry.getKey();
+
+            List<ReportTestSuite> testSuiteList = entry.getValue();
+
+            sink.section2();
+            sinkAnchor(packageName);
+            sink.sectionTitle2();
+            sink.text(packageName);
+            sink.sectionTitle2_();
+
+            boolean showTable = false;
+
+            for (ReportTestSuite suite : testSuiteList) {
+                if (showSuccess || suite.getNumberOfErrors() != 0 || 
suite.getNumberOfFailures() != 0) {
+                    showTable = true;
+
+                    break;
+                }
+            }
+
+            if (showTable) {
+                startTable();
+
+                tableHeader(new String[] {
+                    "",
+                    bundle.getReportLabelClass(),
+                    bundle.getReportLabelTests(),
+                    bundle.getReportLabelErrors(),
+                    bundle.getReportLabelFailures(),
+                    bundle.getReportLabelSkipped(),
+                    bundle.getReportLabelSuccessRate(),
+                    bundle.getReportLabelTime()
+                });
+
+                for (ReportTestSuite suite : testSuiteList) {
+                    if (showSuccess || suite.getNumberOfErrors() != 0 || 
suite.getNumberOfFailures() != 0) {
+                        renderSectionTestSuite(suite);
+                    }
+                }
+
+                endTable();
+            }
+
+            sink.section2_();
+        }
+
+        sink.lineBreak();
+
+        sink.section1_();
+    }
+
+    private void renderSectionTestSuite(ReportTestSuite suite) {
+        NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
+        NumberFormat percentFormat = NumberFormat.getPercentInstance(locale);
+        percentFormat.setMinimumFractionDigits(1);
+
+        sink.tableRow();
+
+        sink.tableCell();
+
+        sink.link("#" + suite.getPackageName() + '.' + suite.getName());
+
+        if (suite.getNumberOfErrors() > 0) {
+            sinkIcon("error");
+        } else if (suite.getNumberOfFailures() > 0) {
+            sinkIcon("junit.framework");
+        } else if (suite.getNumberOfSkipped() > 0) {
+            sinkIcon("skipped");
+        } else {
+            sinkIcon("success");
+        }
+
+        sink.link_();
+
+        sink.tableCell_();
+
+        tableCell('{' + suite.getName() + ", #" + suite.getPackageName() + '.' 
+ suite.getName() + '}');
+
+        tableCell(Integer.toString(suite.getNumberOfTests()));
+
+        tableCell(Integer.toString(suite.getNumberOfErrors()));
+
+        tableCell(Integer.toString(suite.getNumberOfFailures()));
+
+        tableCell(Integer.toString(suite.getNumberOfSkipped()));
+
+        float percentage = parser.computePercentage(
+                suite.getNumberOfTests(), suite.getNumberOfErrors(),
+                suite.getNumberOfFailures(), suite.getNumberOfSkipped());
+        tableCell(percentFormat.format(percentage));
+
+        tableCell(numberFormat.format(suite.getTimeElapsed()) + " s");
+
+        sink.tableRow_();
+    }
+
+    private void renderSectionTestCases() {
+        if (testSuites.isEmpty()) {
+            return;
+        }
+
+        sink.section1();
+        sinkAnchor("Test_Cases");
+        sink.sectionTitle1();
+        sink.text(bundle.getReportLabelTestCases());
+        sink.sectionTitle1_();
+
+        constructHotLinks();
+
+        for (ReportTestSuite suite : testSuites) {
+            List<ReportTestCase> testCases = suite.getTestCases();
+
+            if (!testCases.isEmpty()) {
+                sink.section2();
+                sinkAnchor(suite.getPackageName() + '.' + suite.getName());
+                sink.sectionTitle2();
+                sink.text(suite.getName());
+                sink.sectionTitle2_();
+
+                boolean showTable = false;
+
+                for (ReportTestCase testCase : testCases) {
+                    if (!testCase.isSuccessful() || showSuccess) {
+                        showTable = true;
+
+                        break;
+                    }
+                }
+
+                if (showTable) {
+                    startTable();
+
+                    for (ReportTestCase testCase : testCases) {
+                        if (!testCase.isSuccessful() || showSuccess) {
+                            constructTestCaseSection(testCase);
+                        }
+                    }
+
+                    endTable();
+                }
+
+                sink.section2_();
+            }
+        }
+
+        sink.lineBreak();
+
+        sink.section1_();
+    }
+
+    private void constructTestCaseSection(ReportTestCase testCase) {
+        NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
+
+        sink.tableRow();
+
+        sink.tableCell();
+
+        if (testCase.getFailureType() != null) {
+            sink.link("#" + toHtmlId(testCase.getFullName()));
+
+            sinkIcon(testCase.getFailureType());
+
+            sink.link_();
+        } else {
+            sinkIcon("success");
+        }
+
+        sink.tableCell_();
+
+        if (!testCase.isSuccessful()) {
+            sink.tableCell();
+            sinkAnchor("TC_" + toHtmlId(testCase.getFullName()));
+
+            link("#" + toHtmlId(testCase.getFullName()), testCase.getName());
+
+            SinkEventAttributeSet atts = new SinkEventAttributeSet();
+            atts.addAttribute(CLASS, "detailToggle");
+            atts.addAttribute(STYLE, "display:inline");
+            sink.unknown("div", TAG_TYPE_START, atts);
+
+            sinkLink("javascript:toggleDisplay('" + 
toHtmlId(testCase.getFullName()) + "');");
+
+            atts = new SinkEventAttributeSet();
+            atts.addAttribute(STYLE, "display:inline;");
+            atts.addAttribute(ID, toHtmlId(testCase.getFullName()) + "-off");
+            sink.unknown("span", TAG_TYPE_START, atts);
+            sink.text(" + ");
+            sink.unknown("span", TAG_TYPE_END, null);
+
+            atts = new SinkEventAttributeSet();
+            atts.addAttribute(STYLE, "display:none;");
+            atts.addAttribute(ID, toHtmlId(testCase.getFullName()) + "-on");
+            sink.unknown("span", TAG_TYPE_START, atts);
+            sink.text(" - ");
+            sink.unknown("span", TAG_TYPE_END, null);
+
+            sink.text("[ Detail ]");
+            sinkLink_();
+
+            sink.unknown("div", TAG_TYPE_END, null);
+
+            sink.tableCell_();
+        } else {
+            sinkCellAnchor(testCase.getName(), "TC_" + 
toHtmlId(testCase.getFullName()));
+        }
+
+        tableCell(numberFormat.format(testCase.getTime()) + " s");
+
+        sink.tableRow_();
+
+        if (!testCase.isSuccessful()) {
+            String message = testCase.getFailureMessage();
+            if (message != null) {
+                tableRow(new String[] {"", message, ""});
+            }
+
+            String detail = testCase.getFailureDetail();
+            if (detail != null) {
+                SinkEventAttributeSet atts = new SinkEventAttributeSet();
+                atts.addAttribute(ID, toHtmlId(testCase.getFullName()) + 
toHtmlIdFailure(testCase));
+                atts.addAttribute(STYLE, "display:none;");
+                sink.tableRow(atts);
+
+                tableCell("");
+
+                sink.tableCell();
+
+                verbatimText(detail);
+
+                sink.tableCell_();
+
+                tableCell("");
+
+                sink.tableRow_();
+            }
+        }
+    }
+
+    private String toHtmlId(String id) {
+        return DoxiaUtils.isValidId(id) ? id : DoxiaUtils.encodeId(id, true);
+    }
+
+    private void renderSectionFailureDetails() {
+        List<ReportTestCase> failures = parser.getFailureDetails(testSuites);
+        if (failures.isEmpty()) {
+            return;
+        }
+
+        sink.section1();
+        sinkAnchor("Failure_Details");
+        sink.sectionTitle1();
+        sink.text(bundle.getReportLabelFailureDetails());
+        sink.sectionTitle1_();
+
+        constructHotLinks();
+
+        sink.lineBreak();
+
+        startTable();
+
+        for (ReportTestCase testCase : failures) {
+            sink.tableRow();
+
+            sink.tableCell();
+
+            String type = testCase.getFailureType();
+
+            sinkIcon(type);
+
+            sink.tableCell_();
+
+            sinkCellAnchor(testCase.getName(), 
toHtmlId(testCase.getFullName()));
+
+            sink.tableRow_();
+
+            String message = testCase.getFailureMessage();
+
+            sink.tableRow();
+
+            tableCell("");
+
+            tableCell(message == null ? type : type + ": " + message);
+
+            sink.tableRow_();
+
+            String detail = testCase.getFailureDetail();
+            if (detail != null) {
+                sink.tableRow();
+
+                tableCell("");
+
+                sink.tableCell();
+                SinkEventAttributeSet atts = new SinkEventAttributeSet();
+                atts.addAttribute(ID, testCase.getName() + 
toHtmlIdFailure(testCase));
+                sink.unknown("div", TAG_TYPE_START, atts);
+
+                String fullClassName = testCase.getFullClassName();
+                String errorLineNumber = testCase.getFailureErrorLine();
+                if (xrefLocation != null) {
+                    String path = fullClassName.replace('.', '/');
+                    sink.link(xrefLocation + "/" + path + ".html#L" + 
errorLineNumber);
+                }
+                sink.text(fullClassName + ":" + errorLineNumber);
+
+                if (xrefLocation != null) {
+                    sink.link_();
+                }
+                sink.unknown("div", TAG_TYPE_END, null);
+
+                sink.tableCell_();
+
+                sink.tableRow_();
+            }
+        }
+
+        endTable();
+
+        sink.lineBreak();
+
+        sink.section1_();
+    }
+
+    private void constructHotLinks() {
+        if (!testSuites.isEmpty()) {
+            sink.paragraph();
+
+            sink.text("[");
+            link("#Summary", bundle.getReportLabelSummary());
+            sink.text("]");
+
+            sink.text(" [");
+            link("#Package_List", bundle.getReportLabelPackageList());
+            sink.text("]");
+
+            sink.text(" [");
+            link("#Test_Cases", bundle.getReportLabelTestCases());
+            sink.text("]");
+
+            sink.paragraph_();
+        }
+    }
+
+    private String toHtmlIdFailure(ReportTestCase testCase) {
+        return testCase.hasError() ? "-error" : "-failure";
+    }
+
+    private void sinkIcon(String type) {
+        if (type.startsWith("junit.framework") || "skipped".equals(type)) {
+            sink.figureGraphics("images/icon_warning_sml.gif");
+        } else if (type.startsWith("success")) {
+            sink.figureGraphics("images/icon_success_sml.gif");
+        } else {
+            sink.figureGraphics("images/icon_error_sml.gif");
+        }
+    }
+
+    private void sinkCellAnchor(String text, String anchor) {
+        sink.tableCell();
+        sinkAnchor(anchor);
+        sink.text(text);
+        sink.tableCell_();
+    }
+
+    private void sinkAnchor(String anchor) {
+        // Dollar '$' for nested classes is not valid character in 
sink.anchor() and therefore it is ignored
+        // https://issues.apache.org/jira/browse/SUREFIRE-1443
+        sink.unknown(A.toString(), TAG_TYPE_START, new 
SinkEventAttributeSet(ID, anchor));
+        sink.unknown(A.toString(), TAG_TYPE_END, null);
+    }
+
+    private void sinkLink(String href) {
+        // The "'" argument in this JavaScript function would be escaped to 
"&apos;"
+        // sink.link( "javascript:toggleDisplay('" + toHtmlId( 
testCase.getFullName() ) + "');" );
+        sink.unknown(A.toString(), TAG_TYPE_START, new 
SinkEventAttributeSet(HREF, href));
+    }
+
+    @SuppressWarnings("checkstyle:methodname")
+    private void sinkLink_() {
+        sink.unknown(A.toString(), TAG_TYPE_END, null);
+    }
+
+    private String javascriptToggleDisplayCode() {
+        return "function toggleDisplay(elementId) {" + Markup.EOL
+                + " var elm = document.getElementById(elementId + '-error');" 
+ Markup.EOL
+                + " if (elm == null) {" + Markup.EOL
+                + "  elm = document.getElementById(elementId + '-failure');" + 
Markup.EOL
+                + " }" + Markup.EOL
+                + " if (elm && typeof elm.style != \"undefined\") {" + 
Markup.EOL
+                + "  if (elm.style.display == \"none\") {" + Markup.EOL
+                + "   elm.style.display = \"\";" + Markup.EOL
+                + "   document.getElementById(elementId + 
'-off').style.display = \"none\";" + Markup.EOL
+                + "   document.getElementById(elementId + '-on').style.display 
= \"inline\";" + Markup.EOL
+                + "  } else if (elm.style.display == \"\") {"
+                + "   elm.style.display = \"none\";" + Markup.EOL
+                + "   document.getElementById(elementId + 
'-off').style.display = \"inline\";" + Markup.EOL
+                + "   document.getElementById(elementId + '-on').style.display 
= \"none\";" + Markup.EOL
+                + "  }" + Markup.EOL
+                + " }" + Markup.EOL
+                + " }";
+    }
+}
diff --git 
a/maven-surefire-report-plugin/src/main/resources/surefire-report.properties 
b/maven-surefire-report-plugin/src/main/resources/surefire-report.properties
index 8aa256e88..8632cf8fa 100644
--- a/maven-surefire-report-plugin/src/main/resources/surefire-report.properties
+++ b/maven-surefire-report-plugin/src/main/resources/surefire-report.properties
@@ -37,4 +37,4 @@ report.surefire.text.note2=Note: package statistics are not 
computed recursively
 
 report.failsafe.name=Failsafe Report
 report.failsafe.description=Report on the integration test results of the 
project.
-report.failsafe.header=Failsafe Report
+report.failsafe.title=Failsafe Report
diff --git 
a/maven-surefire-report-plugin/src/test/java/org/apache/maven/plugins/surefire/report/Surefire597Test.java
 
b/maven-surefire-report-plugin/src/test/java/org/apache/maven/plugins/surefire/report/Surefire597Test.java
index e3274f5b7..d1598a2ee 100644
--- 
a/maven-surefire-report-plugin/src/test/java/org/apache/maven/plugins/surefire/report/Surefire597Test.java
+++ 
b/maven-surefire-report-plugin/src/test/java/org/apache/maven/plugins/surefire/report/Surefire597Test.java
@@ -24,11 +24,11 @@ import java.io.StringWriter;
 import junit.framework.TestCase;
 import org.apache.maven.doxia.module.xhtml5.Xhtml5Sink;
 import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.tools.SiteTool;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
 
 import static java.util.Collections.singletonList;
-import static java.util.Locale.ENGLISH;
 import static org.apache.maven.plugins.surefire.report.Utils.toSystemNewLine;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -41,11 +41,18 @@ public class Surefire597Test extends TestCase {
     public void testCorruptedTestCaseFailureWithMissingErrorTypeAndMessage() 
throws Exception {
         File basedir = new File(".").getCanonicalFile();
         File report = new File(basedir, "target/test-classes/surefire-597");
-        ConsoleLogger log = new NullConsoleLogger();
-        SurefireReportGenerator gen = new 
SurefireReportGenerator(singletonList(report), ENGLISH, true, null, log);
         StringWriter writer = new StringWriter();
         Sink sink = new Xhtml5Sink(writer) {};
-        gen.doGenerateReport(new SurefireReportMojo().getBundle(ENGLISH), 
sink);
+        ConsoleLogger consoleLogger = new NullConsoleLogger();
+        SurefireReportRenderer r = new SurefireReportRenderer(
+                sink,
+                SiteTool.DEFAULT_LOCALE,
+                new SurefireReportMojo().getBundle(SiteTool.DEFAULT_LOCALE),
+                consoleLogger,
+                true,
+                singletonList(report),
+                null);
+        r.render();
         String xml = writer.toString();
         assertThat(
                 xml,
@@ -59,11 +66,11 @@ public class Surefire597Test extends TestCase {
                         + "<th>Time</th></tr>\n"
                         + "<tr class=\"b\">\n"
                         + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td>"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td>"
                         + "</tr>"
                         + "</table>")));
         assertThat(
@@ -79,17 +86,17 @@ public class Surefire597Test extends TestCase {
                         + "<th>Time</th></tr>\n"
                         + "<tr class=\"b\">\n"
                         + "<td align=\"left\"><a 
href=\"#surefire\">surefire</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr></table>")));
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr></table>")));
         assertThat(
                 xml,
                 containsString(toSystemNewLine("<table border=\"0\" 
class=\"bodyTable\">\n"
                         + "<tr class=\"a\">\n"
-                        + "<th></th>\n"
+                        + "<th>-</th>\n"
                         + "<th>Class</th>\n"
                         + "<th>Tests</th>\n"
                         + "<th>Errors</th>\n"
@@ -99,25 +106,25 @@ public class Surefire597Test extends TestCase {
                         + "<th>Time</th></tr>\n"
                         + "<tr class=\"b\">\n"
                         + "<td align=\"left\"><a 
href=\"#surefire.MyTest\"><img src=\"images/icon_error_sml.gif\" alt=\"\" 
/></a></td>\n"
-                        + "<td align=\"left\"><a 
href=\"#surefire.MyTest\">MyTest</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr></table>")));
+                        + "<td><a href=\"#surefire.MyTest\">MyTest</a></td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr></table>")));
         assertThat(
                 xml,
                 containsString(toSystemNewLine("<table border=\"0\" 
class=\"bodyTable\">\n"
                         + "<tr class=\"a\">\n"
                         + "<td align=\"left\"><img 
src=\"images/icon_error_sml.gif\" alt=\"\" /></td>\n"
-                        + "<td align=\"left\"><a 
id=\"surefire.MyTest.test\"></a>test</td></tr>\n"
+                        + "<td><a 
id=\"surefire.MyTest.test\"></a>test</td></tr>\n"
                         + "<tr class=\"b\">\n"
-                        + "<td align=\"left\"></td>\n"
-                        + "<td align=\"left\">java.lang.RuntimeException: 
java.lang.IndexOutOfBoundsException: msg</td></tr>\n"
+                        + "<td align=\"left\">-</td>\n"
+                        + "<td>java.lang.RuntimeException: 
java.lang.IndexOutOfBoundsException: msg</td></tr>\n"
                         + "<tr class=\"a\">\n"
-                        + "<td align=\"left\"></td>\n"
-                        + "<td align=\"left\">\n"
+                        + "<td align=\"left\">-</td>\n"
+                        + "<td>\n"
                         + "<div 
id=\"test-error\">surefire.MyTest:13</div></td></tr></table>")));
     }
 }
diff --git 
a/maven-surefire-report-plugin/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportMojoTest.java
 
b/maven-surefire-report-plugin/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportMojoTest.java
index 14c1992da..4dfb98a51 100644
--- 
a/maven-surefire-report-plugin/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportMojoTest.java
+++ 
b/maven-surefire-report-plugin/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportMojoTest.java
@@ -19,26 +19,17 @@
 package org.apache.maven.plugins.surefire.report;
 
 import java.io.File;
-import java.io.IOException;
 import java.io.UnsupportedEncodingException;
-import java.io.Writer;
 import java.net.URL;
 import java.net.URLDecoder;
 import java.util.Locale;
 
-import org.apache.maven.doxia.site.decoration.DecorationModel;
-import org.apache.maven.doxia.siterenderer.Renderer;
-import org.apache.maven.doxia.siterenderer.RendererException;
-import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
-import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
 import org.apache.maven.plugin.LegacySupport;
 import org.apache.maven.plugin.testing.AbstractMojoTestCase;
 import org.apache.maven.plugin.testing.ArtifactStubFactory;
 import org.apache.maven.plugin.testing.stubs.MavenProjectStub;
 import 
org.apache.maven.plugins.surefire.report.stubs.DependencyArtifactStubFactory;
-import org.apache.maven.shared.utils.WriterFactory;
 import org.apache.maven.shared.utils.io.FileUtils;
-import org.apache.maven.shared.utils.io.IOUtil;
 import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
 import org.eclipse.aether.repository.LocalRepository;
@@ -54,15 +45,23 @@ import static org.hamcrest.MatcherAssert.assertThat;
 public class SurefireReportMojoTest extends AbstractMojoTestCase {
     private ArtifactStubFactory artifactStubFactory;
 
-    private Renderer renderer;
+    // Can be removed with Doxia 2.0.0
+    private Locale origLocale;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        renderer = (Renderer) lookup(Renderer.ROLE);
-
         artifactStubFactory = new 
DependencyArtifactStubFactory(getTestFile("target"), true, false);
         artifactStubFactory.getWorkingDir().mkdirs();
+
+        origLocale = Locale.getDefault();
+        Locale.setDefault(Locale.ROOT);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        Locale.setDefault(origLocale);
+        super.tearDown();
     }
 
     protected SurefireReportMojo createReportMojo(File pluginXmlFile) throws 
Exception {
@@ -105,7 +104,6 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
 
         mojo.execute();
         File report = new File(getBasedir(), 
"target/site/unit/basic-surefire-report-test/surefire-report.html");
-        renderer(mojo, report);
         assertTrue(report.exists());
         String htmlContent = FileUtils.fileRead(report);
 
@@ -127,7 +125,6 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
         mojo.execute();
         File report =
                 new File(getBasedir(), 
"target/site/unit/basic-surefire-report-success-false/surefire-report.html");
-        renderer(mojo, report);
         assertTrue(report.exists());
         String htmlContent = FileUtils.fileRead(report);
 
@@ -142,8 +139,7 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
         assertFalse(linkXRef);
         mojo.execute();
         File report =
-                new File(getBasedir(), 
"target/site/unit/basic-surefire-report-success-false/surefire-report.html");
-        renderer(mojo, report);
+                new File(getBasedir(), 
"target/site/unit/basic-surefire-report-linkxref-false/surefire-report.html");
         assertTrue(report.exists());
         String htmlContent = FileUtils.fileRead(report);
 
@@ -157,7 +153,6 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
         mojo.execute();
         File report =
                 new File(getBasedir(), 
"target/site/unit/basic-surefire-report-reporting-null/surefire-report.html");
-        renderer(mojo, report);
         assertTrue(report.exists());
         String htmlContent = FileUtils.fileRead(report);
 
@@ -172,14 +167,13 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
         mojo.execute();
         File report =
                 new File(getBasedir(), 
"target/site/unit/basic-surefire-report-anchor-test-cases/surefire-report.html");
-        renderer(mojo, report);
         assertTrue(report.exists());
         String htmlContent = FileUtils.fileRead(report);
 
-        int idx = htmlContent.indexOf("<td align=\"left\"><a 
id=\"TC_com.shape.CircleTest.testX\"></a>testX</td>");
+        int idx = htmlContent.indexOf("<td><a 
id=\"TC_com.shape.CircleTest.testX\"></a>testX</td>");
         assertTrue(idx > 0);
 
-        idx = htmlContent.indexOf("<td align=\"left\"><a 
id=\"TC_com.shape.CircleTest.testRadius\"></a>"
+        idx = htmlContent.indexOf("<td><a 
id=\"TC_com.shape.CircleTest.testRadius\"></a>"
                 + "<a 
href=\"#com.shape.CircleTest.testRadius\">testRadius</a>");
         assertTrue(idx > 0);
     }
@@ -189,7 +183,6 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
         SurefireReportMojo mojo = createReportMojo(testPom);
         mojo.execute();
         File report = new File(getBasedir(), 
"target/site/unit/surefire-report-single-error/surefire-report.html");
-        renderer(mojo, report);
         assertTrue(report.exists());
         String htmlContent = FileUtils.fileRead(report);
 
@@ -197,22 +190,22 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
                         + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td>")));
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td>")));
 
         assertThat(
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
                         + "<td align=\"left\"><a 
href=\"#surefire\">surefire</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr>")));
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr>")));
         assertThat(
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
@@ -221,13 +214,13 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
                         + "<img src=\"images/icon_error_sml.gif\" alt=\"\" />"
                         + "</a>"
                         + "</td>\n"
-                        + "<td align=\"left\"><a 
href=\"#surefire.MyTest\">MyTest</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr>")));
+                        + "<td><a href=\"#surefire.MyTest\">MyTest</a></td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr>")));
 
         assertThat(htmlContent, containsString(">surefire.MyTest:13</a>"));
 
@@ -278,7 +271,6 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
         mojo.execute();
         File report = new File(
                 getBasedir(), 
"target/site/unit/surefire-report-nestedClass-trimStackTrace/surefire-report.html");
-        renderer(mojo, report);
         assertTrue(report.exists());
         String htmlContent = FileUtils.fileRead(report);
 
@@ -286,22 +278,22 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
                         + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td>")));
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td>")));
 
         assertThat(
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
                         + "<td align=\"left\"><a 
href=\"#surefire\">surefire</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr>")));
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr>")));
         assertThat(
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
@@ -310,13 +302,13 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
                         + "<img src=\"images/icon_error_sml.gif\" alt=\"\" />"
                         + "</a>"
                         + "</td>\n"
-                        + "<td align=\"left\"><a 
href=\"#surefire.MyTest\">MyTest</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr>")));
+                        + "<td><a href=\"#surefire.MyTest\">MyTest</a></td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr>")));
         assertThat(htmlContent, containsString(">surefire.MyTest:13</a>"));
 
         assertThat(htmlContent, 
containsString("./xref-test/surefire/MyTest.html#L13"));
@@ -342,7 +334,6 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
         SurefireReportMojo mojo = createReportMojo(testPom);
         mojo.execute();
         File report = new File(getBasedir(), 
"target/site/unit/surefire-report-nestedClass/surefire-report.html");
-        renderer(mojo, report);
         assertTrue(report.exists());
         String htmlContent = FileUtils.fileRead(report);
 
@@ -350,22 +341,22 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
                         + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td>")));
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td>")));
 
         assertThat(
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
                         + "<td align=\"left\"><a 
href=\"#surefire\">surefire</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr>")));
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr>")));
         assertThat(
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
@@ -374,13 +365,13 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
                         + "<img src=\"images/icon_error_sml.gif\" alt=\"\" />"
                         + "</a>"
                         + "</td>\n"
-                        + "<td align=\"left\"><a 
href=\"#surefire.MyTest\">MyTest</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr>")));
+                        + "<td><a href=\"#surefire.MyTest\">MyTest</a></td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr>")));
         assertThat(htmlContent, containsString(">surefire.MyTest:13</a>"));
 
         assertThat(htmlContent, 
containsString("./xref-test/surefire/MyTest.html#L13"));
@@ -431,7 +422,6 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
         mojo.execute();
         File report =
                 new File(getBasedir(), 
"target/site/unit/surefire-report-enclosed-trimStackTrace/surefire-report.html");
-        renderer(mojo, report);
         assertTrue(report.exists());
         String htmlContent = FileUtils.fileRead(report);
 
@@ -439,22 +429,22 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
                         + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td>")));
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td>")));
 
         assertThat(
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
                         + "<td align=\"left\"><a 
href=\"#surefire\">surefire</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr>")));
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr>")));
         assertThat(
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
@@ -463,13 +453,13 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
                         + "<img src=\"images/icon_error_sml.gif\" alt=\"\" />"
                         + "</a>"
                         + "</td>\n"
-                        + "<td align=\"left\"><a 
href=\"#surefire.MyTest$A\">MyTest$A</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr>")));
+                        + "<td><a 
href=\"#surefire.MyTest$A\">MyTest$A</a></td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr>")));
 
         assertThat(htmlContent, containsString(">surefire.MyTest$A:45</a>"));
 
@@ -495,7 +485,6 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
         SurefireReportMojo mojo = createReportMojo(testPom);
         mojo.execute();
         File report = new File(getBasedir(), 
"target/site/unit/surefire-report-enclosed/surefire-report.html");
-        renderer(mojo, report);
         assertTrue(report.exists());
         String htmlContent = FileUtils.fileRead(report);
 
@@ -503,22 +492,22 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
                         + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td>")));
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td>")));
 
         assertThat(
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
                         + "<td align=\"left\"><a 
href=\"#surefire\">surefire</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr>")));
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr>")));
         assertThat(
                 htmlContent,
                 containsString(toSystemNewLine("<tr class=\"b\">\n"
@@ -527,13 +516,13 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
                         + "<img src=\"images/icon_error_sml.gif\" alt=\"\" />"
                         + "</a>"
                         + "</td>\n"
-                        + "<td align=\"left\"><a 
href=\"#surefire.MyTest$A\">MyTest$A</a></td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">1</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0</td>\n"
-                        + "<td align=\"left\">0%</td>\n"
-                        + "<td align=\"left\">0</td></tr>")));
+                        + "<td><a 
href=\"#surefire.MyTest$A\">MyTest$A</a></td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>1</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0</td>\n"
+                        + "<td>0.0%</td>\n"
+                        + "<td>0 s</td></tr>")));
 
         assertThat(htmlContent, containsString(">surefire.MyTest$A:45</a>"));
 
@@ -609,37 +598,11 @@ public class SurefireReportMojoTest extends 
AbstractMojoTestCase {
 
         mojo.execute();
 
-        File report = new File(getBasedir(), 
"target/site/acceptance-test-report.html");
-        renderer(mojo, report);
+        File report = new File(getBasedir(), 
"target/site/unit/surefire-1183/acceptance-test-report.html");
 
         assertTrue(report.exists());
 
         String htmlContent = FileUtils.fileRead(report);
         assertTrue(htmlContent.contains("<h2><a 
name=\"Acceptance_Test\"></a>Acceptance Test</h2></section>"));
     }
-
-    /**
-     * Renderer the sink from the report mojo.
-     *
-     * @param mojo       not null
-     * @param outputHtml not null
-     * @throws RendererException if any
-     * @throws IOException       if any
-     */
-    private void renderer(SurefireReportMojo mojo, File outputHtml) throws 
RendererException, IOException {
-        Writer writer = null;
-        SiteRenderingContext context = new SiteRenderingContext();
-        context.setDecoration(new DecorationModel());
-        
context.setTemplateName("org/apache/maven/doxia/siterenderer/resources/default-site.vm");
-        context.setLocale(Locale.ENGLISH);
-
-        try {
-            outputHtml.getParentFile().mkdirs();
-            writer = WriterFactory.newXmlWriter(outputHtml);
-
-            renderer.generateDocument(writer, (SiteRendererSink) 
mojo.getSink(), context);
-        } finally {
-            IOUtil.close(writer);
-        }
-    }
 }
diff --git 
a/maven-surefire-report-plugin/src/test/resources/unit/surefire-1183/plugin-config.xml
 
b/maven-surefire-report-plugin/src/test/resources/unit/surefire-1183/plugin-config.xml
index 3166e7896..75ad4abd9 100644
--- 
a/maven-surefire-report-plugin/src/test/resources/unit/surefire-1183/plugin-config.xml
+++ 
b/maven-surefire-report-plugin/src/test/resources/unit/surefire-1183/plugin-config.xml
@@ -31,8 +31,7 @@
                     <outputName>acceptance-test-report</outputName>
                     <localRepository>${localRepository}</localRepository>
                     
<outputDirectory>${basedir}/target/site/unit/surefire-1183</outputDirectory>
-                    
<reportsDirectory>${basedir}/src/test/resources/unit/surefire-1183/acceptancetest-reports
-                    </reportsDirectory>
+                    
<reportsDirectory>${basedir}/src/test/resources/unit/surefire-1183/acceptancetest-reports</reportsDirectory>
                 </configuration>
             </plugin>
         </plugins>
diff --git 
a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire260TestWithIdenticalNamesIT.java
 
b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire260TestWithIdenticalNamesIT.java
index aca227760..5c473af91 100644
--- 
a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire260TestWithIdenticalNamesIT.java
+++ 
b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire260TestWithIdenticalNamesIT.java
@@ -23,8 +23,9 @@ import java.net.URI;
 
 import com.gargoylesoftware.htmlunit.WebClient;
 import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
-import com.gargoylesoftware.htmlunit.html.HtmlDivision;
 import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
+
 import org.apache.maven.surefire.its.fixture.OutputValidator;
 import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
 import org.apache.maven.surefire.its.fixture.TestFile;
@@ -58,7 +59,7 @@ public class Surefire260TestWithIdenticalNamesIT extends 
SurefireJUnit4Integrati
             HtmlAnchor a = (HtmlAnchor)
                     page.getByXPath("//a[@href = 
\"javascript:toggleDisplay('surefire260.TestB.testDup');\"]")
                             .get(0);
-            HtmlDivision content = (HtmlDivision) 
page.getElementById("surefire260.TestB.testDup-failure");
+            HtmlTableRow content = (HtmlTableRow) 
page.getElementById("surefire260.TestB.testDup-failure");
             assertNotNull(content);
             assertTrue(content.getAttribute("style").contains("none"));
             a.click();
diff --git 
a/surefire-report-parser/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportParser.java
 
b/surefire-report-parser/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportParser.java
index 467d78487..230317647 100644
--- 
a/surefire-report-parser/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportParser.java
+++ 
b/surefire-report-parser/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportParser.java
@@ -22,16 +22,13 @@ import javax.xml.parsers.ParserConfigurationException;
 
 import java.io.File;
 import java.io.IOException;
-import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
-import org.apache.maven.reporting.MavenReportException;
 import org.apache.maven.shared.utils.io.DirectoryScanner;
 import org.xml.sax.SAXException;
 
@@ -46,23 +43,18 @@ public final class SurefireReportParser {
     private static final String EXCLUDES =
             "*.txt, testng-failed.xml, testng-failures.xml, 
testng-results.xml, failsafe-summary*.xml";
 
-    private static final int PCENT = 100;
-
     private final List<ReportTestSuite> testSuites = new ArrayList<>();
 
-    private final NumberFormat numberFormat;
-
     private final ConsoleLogger consoleLogger;
 
     private final List<File> reportsDirectories;
 
-    public SurefireReportParser(List<File> reportsDirectories, Locale locale, 
ConsoleLogger consoleLogger) {
+    public SurefireReportParser(List<File> reportsDirectories, ConsoleLogger 
consoleLogger) {
         this.reportsDirectories = reportsDirectories;
-        numberFormat = NumberFormat.getInstance(locale);
         this.consoleLogger = consoleLogger;
     }
 
-    public List<ReportTestSuite> parseXMLReportFiles() throws 
MavenReportException {
+    public List<ReportTestSuite> parseXMLReportFiles() {
         final Collection<File> xmlReportFiles = new ArrayList<>();
         for (File reportsDirectory : reportsDirectories) {
             if (reportsDirectory.exists()) {
@@ -76,19 +68,19 @@ public final class SurefireReportParser {
             try {
                 
testSuites.addAll(parser.parse(aXmlReportFileList.getAbsolutePath()));
             } catch (ParserConfigurationException e) {
-                throw new MavenReportException("Error setting up parser for 
JUnit XML report", e);
+                throw new RuntimeException("Error setting up parser for JUnit 
XML report", e);
             } catch (SAXException e) {
-                throw new MavenReportException("Error parsing JUnit XML report 
" + aXmlReportFileList, e);
+                throw new RuntimeException("Error parsing JUnit XML report " + 
aXmlReportFileList, e);
             } catch (IOException e) {
-                throw new MavenReportException("Error reading JUnit XML report 
" + aXmlReportFileList, e);
+                throw new RuntimeException("Error reading JUnit XML report " + 
aXmlReportFileList, e);
             }
         }
 
         return testSuites;
     }
 
-    public Map<String, String> getSummary(List<ReportTestSuite> suites) {
-        Map<String, String> totalSummary = new HashMap<>();
+    public Map<String, Object> getSummary(List<ReportTestSuite> suites) {
+        Map<String, Object> totalSummary = new HashMap<>();
 
         int totalNumberOfTests = 0;
 
@@ -112,28 +104,24 @@ public final class SurefireReportParser {
             totalElapsedTime += suite.getTimeElapsed();
         }
 
-        String totalPercentage =
+        float totalPercentage =
                 computePercentage(totalNumberOfTests, totalNumberOfErrors, 
totalNumberOfFailures, totalNumberOfSkipped);
 
-        totalSummary.put("totalTests", Integer.toString(totalNumberOfTests));
+        totalSummary.put("totalTests", totalNumberOfTests);
 
-        totalSummary.put("totalErrors", Integer.toString(totalNumberOfErrors));
+        totalSummary.put("totalErrors", totalNumberOfErrors);
 
-        totalSummary.put("totalFailures", 
Integer.toString(totalNumberOfFailures));
+        totalSummary.put("totalFailures", totalNumberOfFailures);
 
-        totalSummary.put("totalSkipped", 
Integer.toString(totalNumberOfSkipped));
+        totalSummary.put("totalSkipped", totalNumberOfSkipped);
 
-        totalSummary.put("totalElapsedTime", 
numberFormat.format(totalElapsedTime));
+        totalSummary.put("totalElapsedTime", totalElapsedTime);
 
         totalSummary.put("totalPercentage", totalPercentage);
 
         return totalSummary;
     }
 
-    public NumberFormat getNumberFormat() {
-        return numberFormat;
-    }
-
     public Map<String, List<ReportTestSuite>> 
getSuitesGroupByPackage(List<ReportTestSuite> testSuitesList) {
         Map<String, List<ReportTestSuite>> suitePackage = new HashMap<>();
 
@@ -152,9 +140,12 @@ public final class SurefireReportParser {
         return suitePackage;
     }
 
-    public String computePercentage(int tests, int errors, int failures, int 
skipped) {
-        float percentage = tests == 0 ? 0 : ((float) (tests - errors - 
failures - skipped) / (float) tests) * PCENT;
-        return numberFormat.format(percentage);
+    /**
+     * @return a float between 0.0 and 1.0
+     */
+    public float computePercentage(int tests, int errors, int failures, int 
skipped) {
+        float percentage = tests == 0 ? 0 : ((float) (tests - errors - 
failures - skipped) / (float) tests);
+        return percentage;
     }
 
     public List<ReportTestCase> getFailureDetails(List<ReportTestSuite> 
testSuites) {
diff --git 
a/surefire-report-parser/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportParserTest.java
 
b/surefire-report-parser/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportParserTest.java
index 4596f60ab..8a9240e11 100644
--- 
a/surefire-report-parser/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportParserTest.java
+++ 
b/surefire-report-parser/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportParserTest.java
@@ -22,7 +22,6 @@ import java.io.File;
 import java.io.UnsupportedEncodingException;
 import java.net.URL;
 import java.net.URLDecoder;
-import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -31,7 +30,6 @@ import junit.framework.TestCase;
 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
 
 import static java.util.Collections.singletonList;
-import static java.util.Locale.ENGLISH;
 
 /**
  *
@@ -39,10 +37,9 @@ import static java.util.Locale.ENGLISH;
 @SuppressWarnings("checkstyle:magicnumber")
 public class SurefireReportParserTest extends TestCase {
     public void testParseXMLReportFiles() throws Exception {
-        SurefireReportParser report =
-                new SurefireReportParser(singletonList(getTestDir()), ENGLISH, 
new NullConsoleLogger());
+        SurefireReportParser parser = new 
SurefireReportParser(singletonList(getTestDir()), new NullConsoleLogger());
 
-        List<ReportTestSuite> suites = report.parseXMLReportFiles();
+        List<ReportTestSuite> suites = parser.parseXMLReportFiles();
 
         assertEquals(8, suites.size());
 
@@ -80,23 +77,21 @@ public class SurefireReportParserTest extends TestCase {
 
         suites.add(tSuite2);
 
-        SurefireReportParser report = new SurefireReportParser(null, ENGLISH, 
new NullConsoleLogger());
+        SurefireReportParser parser = new SurefireReportParser(null, new 
NullConsoleLogger());
 
-        Map<String, String> testMap = report.getSummary(suites);
+        Map<String, Object> testMap = parser.getSummary(suites);
 
-        assertEquals(20, Integer.parseInt(testMap.get("totalErrors")));
+        assertEquals(20, (int) testMap.get("totalErrors"));
 
-        assertEquals(40, Integer.parseInt(testMap.get("totalFailures")));
+        assertEquals(40, (int) testMap.get("totalFailures"));
 
-        assertEquals(200, Integer.parseInt(testMap.get("totalTests")));
+        assertEquals(200, (int) testMap.get("totalTests"));
 
-        assertEquals(4, Integer.parseInt(testMap.get("totalSkipped")));
+        assertEquals(4, (int) testMap.get("totalSkipped"));
 
-        NumberFormat numberFormat = report.getNumberFormat();
+        assertEquals(2.0f, (float) testMap.get("totalElapsedTime"));
 
-        assertEquals(2.0f, 
numberFormat.parse(testMap.get("totalElapsedTime")).floatValue(), 0.0f);
-
-        assertEquals(68.00f, 
numberFormat.parse(testMap.get("totalPercentage")).floatValue(), 0);
+        assertEquals(0.68f, (float) testMap.get("totalPercentage"));
     }
 
     public void testGetSuitesGroupByPackage() {
@@ -120,9 +115,9 @@ public class SurefireReportParserTest extends TestCase {
 
         suites.add(tSuite3);
 
-        SurefireReportParser report = new SurefireReportParser(null, ENGLISH, 
new NullConsoleLogger());
+        SurefireReportParser parser = new SurefireReportParser(null, new 
NullConsoleLogger());
 
-        Map<String, List<ReportTestSuite>> groupMap = 
report.getSuitesGroupByPackage(suites);
+        Map<String, List<ReportTestSuite>> groupMap = 
parser.getSuitesGroupByPackage(suites);
 
         assertEquals(2, groupMap.size());
 
@@ -134,13 +129,9 @@ public class SurefireReportParserTest extends TestCase {
     }
 
     public void testComputePercentage() throws Exception {
-        SurefireReportParser report = new SurefireReportParser(null, ENGLISH, 
new NullConsoleLogger());
-        NumberFormat numberFormat = report.getNumberFormat();
+        SurefireReportParser parser = new SurefireReportParser(null, new 
NullConsoleLogger());
 
-        assertEquals(
-                70.00f,
-                numberFormat.parse(report.computePercentage(100, 20, 10, 
0)).floatValue(),
-                0);
+        assertEquals(0.7f, (float) parser.computePercentage(100, 20, 10, 0));
     }
 
     public void testGetFailureDetails() {
@@ -178,9 +169,9 @@ public class SurefireReportParserTest extends TestCase {
 
         suites.add(tSuite2);
 
-        SurefireReportParser report = new SurefireReportParser(null, ENGLISH, 
new NullConsoleLogger());
+        SurefireReportParser parser = new SurefireReportParser(null, new 
NullConsoleLogger());
 
-        List<ReportTestCase> failures = report.getFailureDetails(suites);
+        List<ReportTestCase> failures = parser.getFailureDetails(suites);
 
         assertEquals(2, failures.size());
 


Reply via email to