1dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linpackage org.testng.reporters;
2dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
3dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport java.io.BufferedWriter;
4dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport java.io.File;
5dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport java.io.FileWriter;
6dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport java.io.IOException;
7dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport java.io.PrintWriter;
8dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport java.text.NumberFormat;
9dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport java.util.Collections;
10dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport java.util.Comparator;
11dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport java.util.Iterator;
12dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport java.util.List;
13dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport java.util.Set;
14dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
15dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport org.testng.IReporter;
16dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport org.testng.ISuite;
17dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport org.testng.ISuiteResult;
18dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport org.testng.ITestContext;
19dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport org.testng.ITestResult;
20dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport org.testng.Reporter;
21dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport org.testng.collections.Lists;
22dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport org.testng.internal.Utils;
23dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport org.testng.log4testng.Logger;
24dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linimport org.testng.xml.XmlSuite;
25dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
26dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin/**
27dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin * Reporter that generates a single-page HTML report of the test results.
28dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin * <p>
29dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin * Based on an earlier implementation by Paul Mendelson.
30dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin * </p>
31dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin *
32dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin * @author Abraham Lin
33dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin */
34dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Linpublic class EmailableReporter2 implements IReporter {
35dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    private static final Logger LOG = Logger.getLogger(EmailableReporter.class);
36dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
37dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected PrintWriter writer;
38dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
39dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected List<SuiteResult> suiteResults = Lists.newArrayList();
40dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
41dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    // Reusable buffer
42dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    private StringBuilder buffer = new StringBuilder();
43dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
44dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    @Override
45dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites,
46dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            String outputDirectory) {
47dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        try {
48dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer = createWriter(outputDirectory);
49dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        } catch (IOException e) {
50dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            LOG.error("Unable to create output file", e);
51dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return;
52dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
53dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        for (ISuite suite : suites) {
54dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            suiteResults.add(new SuiteResult(suite));
55dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
56dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
57dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writeDocumentStart();
58dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writeHead();
59dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writeBody();
60dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writeDocumentEnd();
61dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
62dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.close();
63dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
64dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
65dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected PrintWriter createWriter(String outdir) throws IOException {
66dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        new File(outdir).mkdirs();
67dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        return new PrintWriter(new BufferedWriter(new FileWriter(new File(
68dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                outdir, "emailable-report.html"))));
69dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
70dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
71dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeDocumentStart() {
72dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");
73dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
74dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
75dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
76dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeHead() {
77dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<head>");
78dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<title>TestNG Report</title>");
79dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writeStylesheet();
80dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</head>");
81dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
82dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
83dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeStylesheet() {
84dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<style type=\"text/css\">");
85dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("table {margin-bottom:10px;border-collapse:collapse;empty-cells:show}");
86dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("th,td {border:1px solid #009;padding:.25em .5em}");
87dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("th {vertical-align:bottom}");
88dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("td {vertical-align:top}");
89dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("table a {font-weight:bold}");
90dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(".stripe td {background-color: #E6EBF9}");
91dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(".num {text-align:right}");
92dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(".passedodd td {background-color: #3F3}");
93dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(".passedeven td {background-color: #0A0}");
94dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(".skippedodd td {background-color: #DDD}");
95dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(".skippedeven td {background-color: #CCC}");
96dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(".failedodd td,.attn {background-color: #F33}");
97dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(".failedeven td,.stripe .attn {background-color: #D00}");
98dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(".stacktrace {white-space:pre;font-family:monospace}");
99dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(".totop {font-size:85%;text-align:center;border-bottom:2px solid #000}");
100dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</style>");
101dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
102dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
103dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeBody() {
104dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<body>");
105dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writeSuiteSummary();
106dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writeScenarioSummary();
107dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writeScenarioDetails();
108dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</body>");
109dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
110dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
111dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeDocumentEnd() {
112dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</html>");
113dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
114dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
115dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeSuiteSummary() {
116dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        NumberFormat integerFormat = NumberFormat.getIntegerInstance();
117dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        NumberFormat decimalFormat = NumberFormat.getNumberInstance();
118dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
119dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        int totalPassedTests = 0;
120dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        int totalSkippedTests = 0;
121dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        int totalFailedTests = 0;
122dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        long totalDuration = 0;
123dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
124dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<table>");
125dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<tr>");
126dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<th>Test</th>");
127dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<th># Passed</th>");
128dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<th># Skipped</th>");
129dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<th># Failed</th>");
130dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<th>Time (ms)</th>");
131dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<th>Included Groups</th>");
132dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<th>Excluded Groups</th>");
133dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</tr>");
134dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
135dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        int testIndex = 0;
136dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        for (SuiteResult suiteResult : suiteResults) {
137dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<tr><th colspan=\"7\">");
138dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print(Utils.escapeHtml(suiteResult.getSuiteName()));
139dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("</th></tr>");
140dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
141dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            for (TestResult testResult : suiteResult.getTestResults()) {
142dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                int passedTests = testResult.getPassedTestCount();
143dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                int skippedTests = testResult.getSkippedTestCount();
144dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                int failedTests = testResult.getFailedTestCount();
145dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                long duration = testResult.getDuration();
146dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
147dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("<tr");
148dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                if ((testIndex % 2) == 1) {
149dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    writer.print(" class=\"stripe\"");
150dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                }
151dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(">");
152dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
153dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                buffer.setLength(0);
154dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writeTableData(buffer.append("<a href=\"#t").append(testIndex)
155dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        .append("\">")
156dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        .append(Utils.escapeHtml(testResult.getTestName()))
157dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        .append("</a>").toString());
158dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writeTableData(integerFormat.format(passedTests), "num");
159dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writeTableData(integerFormat.format(skippedTests),
160dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        (skippedTests > 0 ? "num attn" : "num"));
161dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writeTableData(integerFormat.format(failedTests),
162dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        (failedTests > 0 ? "num attn" : "num"));
163dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writeTableData(decimalFormat.format(duration), "num");
164dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writeTableData(testResult.getIncludedGroups());
165dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writeTableData(testResult.getExcludedGroups());
166dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
167dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("</tr>");
168dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
169dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                totalPassedTests += passedTests;
170dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                totalSkippedTests += skippedTests;
171dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                totalFailedTests += failedTests;
172dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                totalDuration += duration;
173dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
174dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                testIndex++;
175dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
176dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
177dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
178dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        // Print totals if there was more than one test
179dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        if (testIndex > 1) {
180dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<tr>");
181dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<th>Total</th>");
182dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writeTableHeader(integerFormat.format(totalPassedTests), "num");
183dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writeTableHeader(integerFormat.format(totalSkippedTests),
184dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    (totalSkippedTests > 0 ? "num attn" : "num"));
185dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writeTableHeader(integerFormat.format(totalFailedTests),
186dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    (totalFailedTests > 0 ? "num attn" : "num"));
187dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writeTableHeader(decimalFormat.format(totalDuration), "num");
188dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<th colspan=\"2\"></th>");
189dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("</tr>");
190dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
191dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
192dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</table>");
193dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
194dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
195dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
196dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Writes a summary of all the test scenarios.
197dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
198dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeScenarioSummary() {
199dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<table>");
200dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<thead>");
201dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<tr>");
202dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<th>Class</th>");
203dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<th>Method</th>");
204dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<th>Start</th>");
205dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<th>Time (ms)</th>");
206dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</tr>");
207dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</thead>");
208dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
209dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        int testIndex = 0;
210dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        int scenarioIndex = 0;
211dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        for (SuiteResult suiteResult : suiteResults) {
212dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<tbody><tr><th colspan=\"4\">");
213dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print(Utils.escapeHtml(suiteResult.getSuiteName()));
214dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("</th></tr></tbody>");
215dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
216dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            for (TestResult testResult : suiteResult.getTestResults()) {
217dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("<tbody id=\"t");
218dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(testIndex);
219dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("\">");
220dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
221dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                String testName = Utils.escapeHtml(testResult.getTestName());
222dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
223dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                scenarioIndex += writeScenarioSummary(testName
224dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        + " &#8212; failed (configuration methods)",
225dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        testResult.getFailedConfigurationResults(), "failed",
226dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        scenarioIndex);
227dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                scenarioIndex += writeScenarioSummary(testName
228dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        + " &#8212; failed", testResult.getFailedTestResults(),
229dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        "failed", scenarioIndex);
230dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                scenarioIndex += writeScenarioSummary(testName
231dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        + " &#8212; skipped (configuration methods)",
232dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        testResult.getSkippedConfigurationResults(), "skipped",
233dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        scenarioIndex);
234dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                scenarioIndex += writeScenarioSummary(testName
235dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        + " &#8212; skipped",
236dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        testResult.getSkippedTestResults(), "skipped",
237dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        scenarioIndex);
238dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                scenarioIndex += writeScenarioSummary(testName
239dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        + " &#8212; passed", testResult.getPassedTestResults(),
240dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        "passed", scenarioIndex);
241dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
242dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("</tbody>");
243dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
244dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                testIndex++;
245dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
246dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
247dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
248dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</table>");
249dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
250dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
251dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
252dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Writes the scenario summary for the results of a given state for a single
253dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * test.
254dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
255dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    private int writeScenarioSummary(String description,
256dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            List<ClassResult> classResults, String cssClassPrefix,
257dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            int startingScenarioIndex) {
258dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        int scenarioCount = 0;
259dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        if (!classResults.isEmpty()) {
260dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<tr><th colspan=\"4\">");
261dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print(description);
262dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("</th></tr>");
263dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
264dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            int scenarioIndex = startingScenarioIndex;
265dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            int classIndex = 0;
266dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            for (ClassResult classResult : classResults) {
267dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                String cssClass = cssClassPrefix
268dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        + ((classIndex % 2) == 0 ? "even" : "odd");
269dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
270dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                buffer.setLength(0);
271dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
272dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                int scenariosPerClass = 0;
273dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                int methodIndex = 0;
274dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                for (MethodResult methodResult : classResult.getMethodResults()) {
275dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    List<ITestResult> results = methodResult.getResults();
276dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    int resultsCount = results.size();
277dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    assert resultsCount > 0;
278dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
279dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    ITestResult firstResult = results.iterator().next();
280dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    String methodName = Utils.escapeHtml(firstResult
281dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                            .getMethod().getMethodName());
282dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    long start = firstResult.getStartMillis();
283dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    long duration = firstResult.getEndMillis() - start;
284dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
285dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    // The first method per class shares a row with the class
286dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    // header
287dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    if (methodIndex > 0) {
288dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        buffer.append("<tr class=\"").append(cssClass)
289dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                                .append("\">");
290dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
291dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    }
292dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
293dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    // Write the timing information with the first scenario per
294dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    // method
295dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    buffer.append("<td><a href=\"#m").append(scenarioIndex)
296dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                            .append("\">").append(methodName)
297dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                            .append("</a></td>").append("<td rowspan=\"")
298dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                            .append(resultsCount).append("\">").append(start)
299dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                            .append("</td>").append("<td rowspan=\"")
300dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                            .append(resultsCount).append("\">")
301dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                            .append(duration).append("</td></tr>");
302dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    scenarioIndex++;
303dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
304dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    // Write the remaining scenarios for the method
305dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    for (int i = 1; i < resultsCount; i++) {
306dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        buffer.append("<tr class=\"").append(cssClass)
307dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                                .append("\">").append("<td><a href=\"#m")
308dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                                .append(scenarioIndex).append("\">")
309dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                                .append(methodName).append("</a></td></tr>");
310dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        scenarioIndex++;
311dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    }
312dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
313dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    scenariosPerClass += resultsCount;
314dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    methodIndex++;
315dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                }
316dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
317dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                // Write the test results for the class
318dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("<tr class=\"");
319dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(cssClass);
320dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("\">");
321dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("<td rowspan=\"");
322dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(scenariosPerClass);
323dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("\">");
324dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(Utils.escapeHtml(classResult.getClassName()));
325dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("</td>");
326dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(buffer);
327dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
328dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                classIndex++;
329dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
330dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            scenarioCount = scenarioIndex - startingScenarioIndex;
331dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
332dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        return scenarioCount;
333dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
334dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
335dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
336dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Writes the details for all test scenarios.
337dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
338dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeScenarioDetails() {
339dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        int scenarioIndex = 0;
340dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        for (SuiteResult suiteResult : suiteResults) {
341dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            for (TestResult testResult : suiteResult.getTestResults()) {
342dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("<h2>");
343dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(Utils.escapeHtml(testResult.getTestName()));
344dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("</h2>");
345dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
346dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                scenarioIndex += writeScenarioDetails(
347dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        testResult.getFailedConfigurationResults(),
348dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        scenarioIndex);
349dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                scenarioIndex += writeScenarioDetails(
350dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        testResult.getFailedTestResults(), scenarioIndex);
351dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                scenarioIndex += writeScenarioDetails(
352dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        testResult.getSkippedConfigurationResults(),
353dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        scenarioIndex);
354dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                scenarioIndex += writeScenarioDetails(
355dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        testResult.getSkippedTestResults(), scenarioIndex);
356dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                scenarioIndex += writeScenarioDetails(
357dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        testResult.getPassedTestResults(), scenarioIndex);
358dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
359dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
360dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
361dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
362dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
363dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Writes the scenario details for the results of a given state for a single
364dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * test.
365dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
366dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    private int writeScenarioDetails(List<ClassResult> classResults,
367dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            int startingScenarioIndex) {
368dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        int scenarioIndex = startingScenarioIndex;
369dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        for (ClassResult classResult : classResults) {
370dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            String className = classResult.getClassName();
371dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            for (MethodResult methodResult : classResult.getMethodResults()) {
372dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                List<ITestResult> results = methodResult.getResults();
373dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                assert !results.isEmpty();
374dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
375dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                String label = Utils
376dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        .escapeHtml(className
377dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                                + "#"
378dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                                + results.iterator().next().getMethod()
379dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                                        .getMethodName());
380dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                for (ITestResult result : results) {
381dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    writeScenario(scenarioIndex, label, result);
382dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    scenarioIndex++;
383dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                }
384dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
385dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
386dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
387dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        return scenarioIndex - startingScenarioIndex;
388dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
389dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
390dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
391dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Writes the details for an individual test scenario.
392dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
393dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    private void writeScenario(int scenarioIndex, String label,
394dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            ITestResult result) {
395dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<h3 id=\"m");
396dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(scenarioIndex);
397dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("\">");
398dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(label);
399dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</h3>");
400dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
401dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<table class=\"result\">");
402dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
403dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        // Write test parameters (if any)
404dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        Object[] parameters = result.getParameters();
405dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        int parameterCount = (parameters == null ? 0 : parameters.length);
406dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        if (parameterCount > 0) {
407dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<tr class=\"param\">");
408dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            for (int i = 1; i <= parameterCount; i++) {
409dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("<th>Parameter #");
410dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(i);
411dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("</th>");
412dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
413dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("</tr><tr class=\"param stripe\">");
414dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            for (Object parameter : parameters) {
415dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("<td>");
416dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(Utils.escapeHtml(Utils.toString(parameter)));
417dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("</td>");
418dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
419dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("</tr>");
420dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
421dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
422dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        // Write reporter messages (if any)
423dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        List<String> reporterMessages = Reporter.getOutput(result);
424dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        if (!reporterMessages.isEmpty()) {
425dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<tr><th");
426dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            if (parameterCount > 1) {
427dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(" colspan=\"");
428dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(parameterCount);
429dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("\"");
430dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
431dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print(">Messages</th></tr>");
432dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
433dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<tr><td");
434dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            if (parameterCount > 1) {
435dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(" colspan=\"");
436dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(parameterCount);
437dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("\"");
438dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
439dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print(">");
440dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writeReporterMessages(reporterMessages);
441dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("</td></tr>");
442dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
443dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
444dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        // Write exception (if any)
445dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        Throwable throwable = result.getThrowable();
446dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        if (throwable != null) {
447dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<tr><th");
448dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            if (parameterCount > 1) {
449dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(" colspan=\"");
450dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(parameterCount);
451dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("\"");
452dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
453dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print(">");
454dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print((result.getStatus() == ITestResult.SUCCESS ? "Expected Exception"
455dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    : "Exception"));
456dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("</th></tr>");
457dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
458dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<tr><td");
459dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            if (parameterCount > 1) {
460dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(" colspan=\"");
461dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print(parameterCount);
462dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                writer.print("\"");
463dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
464dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print(">");
465dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writeStackTrace(throwable);
466dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("</td></tr>");
467dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
468dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
469dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</table>");
470dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<p class=\"totop\"><a href=\"#summary\">back to summary</a></p>");
471dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
472dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
473dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeReporterMessages(List<String> reporterMessages) {
474dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<div class=\"messages\">");
475dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        Iterator<String> iterator = reporterMessages.iterator();
476dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        assert iterator.hasNext();
477dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(Utils.escapeHtml(iterator.next()));
478dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        while (iterator.hasNext()) {
479dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("<br/>");
480dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print(Utils.escapeHtml(iterator.next()));
481dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
482dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</div>");
483dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
484dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
485dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeStackTrace(Throwable throwable) {
486dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<div class=\"stacktrace\">");
487dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(Utils.stackTrace(throwable, true)[0]);
488dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</div>");
489dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
490dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
491dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
492dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Writes a TH element with the specified contents and CSS class names.
493dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *
494dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * @param html
495dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *            the HTML contents
496dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * @param cssClasses
497dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *            the space-delimited CSS classes or null if there are no
498dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *            classes to apply
499dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
500dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeTableHeader(String html, String cssClasses) {
501dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writeTag("th", html, cssClasses);
502dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
503dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
504dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
505dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Writes a TD element with the specified contents.
506dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *
507dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * @param html
508dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *            the HTML contents
509dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
510dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeTableData(String html) {
511dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writeTableData(html, null);
512dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
513dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
514dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
515dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Writes a TD element with the specified contents and CSS class names.
516dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *
517dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * @param html
518dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *            the HTML contents
519dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * @param cssClasses
520dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *            the space-delimited CSS classes or null if there are no
521dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *            classes to apply
522dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
523dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeTableData(String html, String cssClasses) {
524dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writeTag("td", html, cssClasses);
525dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
526dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
527dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
528dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Writes an arbitrary HTML element with the specified contents and CSS
529dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * class names.
530dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *
531dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * @param tag
532dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *            the tag name
533dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * @param html
534dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *            the HTML contents
535dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * @param cssClasses
536dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *            the space-delimited CSS classes or null if there are no
537dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     *            classes to apply
538dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
539dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected void writeTag(String tag, String html, String cssClasses) {
540dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("<");
541dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(tag);
542dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        if (cssClasses != null) {
543dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print(" class=\"");
544dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print(cssClasses);
545dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            writer.print("\"");
546dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
547dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(">");
548dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(html);
549dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print("</");
550dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(tag);
551dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        writer.print(">");
552dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
553dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
554dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
555dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Groups {@link TestResult}s by suite.
556dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
557dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected static class SuiteResult {
558dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final String suiteName;
559dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final List<TestResult> testResults = Lists.newArrayList();
560dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
561dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public SuiteResult(ISuite suite) {
562dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            suiteName = suite.getName();
563dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            for (ISuiteResult suiteResult : suite.getResults().values()) {
564dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                testResults.add(new TestResult(suiteResult.getTestContext()));
565dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
566dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
567dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
568dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public String getSuiteName() {
569dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return suiteName;
570dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
571dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
572dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
573dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * @return the test results (possibly empty)
574dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
575dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public List<TestResult> getTestResults() {
576dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return testResults;
577dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
578dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
579dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
580dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
581dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Groups {@link ClassResult}s by test, type (configuration or test), and
582dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * status.
583dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
584dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected static class TestResult {
585dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
586dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * Orders test results by class name and then by method name (in
587dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * lexicographic order).
588dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
589dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        protected static final Comparator<ITestResult> RESULT_COMPARATOR = new Comparator<ITestResult>() {
590dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            @Override
591dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            public int compare(ITestResult o1, ITestResult o2) {
592dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                int result = o1.getTestClass().getName()
593dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        .compareTo(o2.getTestClass().getName());
594dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                if (result == 0) {
595dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    result = o1.getMethod().getMethodName()
596dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                            .compareTo(o2.getMethod().getMethodName());
597dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                }
598dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                return result;
599dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
600dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        };
601dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
602dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final String testName;
603dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final List<ClassResult> failedConfigurationResults;
604dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final List<ClassResult> failedTestResults;
605dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final List<ClassResult> skippedConfigurationResults;
606dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final List<ClassResult> skippedTestResults;
607dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final List<ClassResult> passedTestResults;
608dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final int failedTestCount;
609dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final int skippedTestCount;
610dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final int passedTestCount;
611dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final long duration;
612dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final String includedGroups;
613dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final String excludedGroups;
614dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
615dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public TestResult(ITestContext context) {
616dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            testName = context.getName();
617dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
618dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            Set<ITestResult> failedConfigurations = context
619dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    .getFailedConfigurations().getAllResults();
620dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            Set<ITestResult> failedTests = context.getFailedTests()
621dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    .getAllResults();
622dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            Set<ITestResult> skippedConfigurations = context
623dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    .getSkippedConfigurations().getAllResults();
624dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            Set<ITestResult> skippedTests = context.getSkippedTests()
625dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    .getAllResults();
626dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            Set<ITestResult> passedTests = context.getPassedTests()
627dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    .getAllResults();
628dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
629dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            failedConfigurationResults = groupResults(failedConfigurations);
630dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            failedTestResults = groupResults(failedTests);
631dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            skippedConfigurationResults = groupResults(skippedConfigurations);
632dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            skippedTestResults = groupResults(skippedTests);
633dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            passedTestResults = groupResults(passedTests);
634dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
635dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            failedTestCount = failedTests.size();
636dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            skippedTestCount = skippedTests.size();
637dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            passedTestCount = passedTests.size();
638dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
639dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            duration = context.getEndDate().getTime()
640dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    - context.getStartDate().getTime();
641dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
642dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            includedGroups = formatGroups(context.getIncludedGroups());
643dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            excludedGroups = formatGroups(context.getExcludedGroups());
644dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
645dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
646dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
647dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * Groups test results by method and then by class.
648dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
649dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        protected List<ClassResult> groupResults(Set<ITestResult> results) {
650dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            List<ClassResult> classResults = Lists.newArrayList();
651dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            if (!results.isEmpty()) {
652dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                List<MethodResult> resultsPerClass = Lists.newArrayList();
653dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                List<ITestResult> resultsPerMethod = Lists.newArrayList();
654dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
655dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                List<ITestResult> resultsList = Lists.newArrayList(results);
656dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                Collections.sort(resultsList, RESULT_COMPARATOR);
657dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                Iterator<ITestResult> resultsIterator = resultsList.iterator();
658dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                assert resultsIterator.hasNext();
659dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
660dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                ITestResult result = resultsIterator.next();
661dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                resultsPerMethod.add(result);
662dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
663dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                String previousClassName = result.getTestClass().getName();
664dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                String previousMethodName = result.getMethod().getMethodName();
665dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                while (resultsIterator.hasNext()) {
666dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    result = resultsIterator.next();
667dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
668bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                    String className = result.getTestClass().getName();
669bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                    if (!previousClassName.equals(className)) {
670bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                        // Different class implies different method
671dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        assert !resultsPerMethod.isEmpty();
672dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        resultsPerClass.add(new MethodResult(resultsPerMethod));
673dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        resultsPerMethod = Lists.newArrayList();
674dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
675dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        assert !resultsPerClass.isEmpty();
676dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        classResults.add(new ClassResult(previousClassName,
677dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                                resultsPerClass));
678dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        resultsPerClass = Lists.newArrayList();
679dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
680dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        previousClassName = className;
681bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                        previousMethodName = result.getMethod().getMethodName();
682bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                    } else {
683bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                        String methodName = result.getMethod().getMethodName();
684bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                        if (!previousMethodName.equals(methodName)) {
685bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                            assert !resultsPerMethod.isEmpty();
686bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                            resultsPerClass.add(new MethodResult(resultsPerMethod));
687bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                            resultsPerMethod = Lists.newArrayList();
688bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin
689bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                            previousMethodName = methodName;
690bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                        }
691dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                    }
692bbc3d5ac4c477fcc63abb1604206348c4fe13470Abraham Lin                    resultsPerMethod.add(result);
693dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                }
694dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                assert !resultsPerMethod.isEmpty();
695dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                resultsPerClass.add(new MethodResult(resultsPerMethod));
696dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                assert !resultsPerClass.isEmpty();
697dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                classResults.add(new ClassResult(previousClassName,
698dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                        resultsPerClass));
699dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
700dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return classResults;
701dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
702dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
703dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public String getTestName() {
704dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return testName;
705dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
706dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
707dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
708dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * @return the results for failed configurations (possibly empty)
709dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
710dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public List<ClassResult> getFailedConfigurationResults() {
711dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return failedConfigurationResults;
712dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
713dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
714dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
715dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * @return the results for failed tests (possibly empty)
716dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
717dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public List<ClassResult> getFailedTestResults() {
718dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return failedTestResults;
719dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
720dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
721dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
722dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * @return the results for skipped configurations (possibly empty)
723dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
724dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public List<ClassResult> getSkippedConfigurationResults() {
725dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return skippedConfigurationResults;
726dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
727dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
728dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
729dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * @return the results for skipped tests (possibly empty)
730dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
731dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public List<ClassResult> getSkippedTestResults() {
732dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return skippedTestResults;
733dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
734dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
735dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
736dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * @return the results for passed tests (possibly empty)
737dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
738dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public List<ClassResult> getPassedTestResults() {
739dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return passedTestResults;
740dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
741dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
742dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public int getFailedTestCount() {
743dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return failedTestCount;
744dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
745dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
746dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public int getSkippedTestCount() {
747dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return skippedTestCount;
748dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
749dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
750dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public int getPassedTestCount() {
751dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return passedTestCount;
752dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
753dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
754dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public long getDuration() {
755dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return duration;
756dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
757dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
758dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public String getIncludedGroups() {
759dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return includedGroups;
760dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
761dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
762dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public String getExcludedGroups() {
763dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return excludedGroups;
764dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
765dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
766dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
767dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * Formats an array of groups for display.
768dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
769dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        protected String formatGroups(String[] groups) {
770dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            if (groups.length == 0) {
771dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                return "";
772dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
773dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
774dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            StringBuilder builder = new StringBuilder();
775dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            builder.append(groups[0]);
776dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            for (int i = 1; i < groups.length; i++) {
777dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin                builder.append(", ").append(groups[i]);
778dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            }
779dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return builder.toString();
780dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
781dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
782dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
783dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
784dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Groups {@link MethodResult}s by class.
785dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
786dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected static class ClassResult {
787dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final String className;
788dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final List<MethodResult> methodResults;
789dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
790dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
791dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * @param className
792dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         *            the class name
793dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * @param methodResults
794dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         *            the non-null, non-empty {@link MethodResult} list
795dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
796dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public ClassResult(String className, List<MethodResult> methodResults) {
797dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            this.className = className;
798dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            this.methodResults = methodResults;
799dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
800dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
801dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public String getClassName() {
802dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return className;
803dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
804dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
805dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
806dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * @return the non-null, non-empty {@link MethodResult} list
807dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
808dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public List<MethodResult> getMethodResults() {
809dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return methodResults;
810dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
811dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
812dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
813dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    /**
814dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     * Groups test results by method.
815dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin     */
816dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    protected static class MethodResult {
817dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        private final List<ITestResult> results;
818dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
819dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
820dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * @param results
821dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         *            the non-null, non-empty result list
822dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
823dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public MethodResult(List<ITestResult> results) {
824dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            this.results = results;
825dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
826dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin
827dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        /**
828dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         * @return the non-null, non-empty result list
829dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin         */
830dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        public List<ITestResult> getResults() {
831dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin            return results;
832dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin        }
833dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin    }
834dc41cff2ca2b554c5084fe0edc3b40b7eb722b9cAbraham Lin}
835