org.testng.reporters.EmailableReporter2 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of testng Show documentation
Show all versions of testng Show documentation
A testing framework for the JVM
package org.testng.reporters;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.testng.IReporter;
import org.testng.ISuite;
import org.testng.ISuiteResult;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.collections.Lists;
import org.testng.internal.Utils;
import org.testng.log4testng.Logger;
import org.testng.xml.XmlSuite;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.newBufferedWriter;
/**
* Reporter that generates a single-page HTML report of the test results.
*/
public class EmailableReporter2 implements IReporter {
private static final Logger LOG = Logger.getLogger(EmailableReporter2.class);
protected PrintWriter writer;
protected final List suiteResults = Lists.newArrayList();
// Reusable buffer
private final StringBuilder buffer = new StringBuilder();
private String fileName = "emailable-report.html";
private static final String JVM_ARG = "emailable.report2.name";
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileName() {
return fileName;
}
@Override
public void generateReport(List xmlSuites, List suites, String outputDirectory) {
try {
writer = createWriter(outputDirectory);
} catch (IOException e) {
LOG.error("Unable to create output file", e);
return;
}
for (ISuite suite : suites) {
suiteResults.add(new SuiteResult(suite));
}
writeDocumentStart();
writeHead();
writeBody();
writeDocumentEnd();
writer.close();
}
protected PrintWriter createWriter(String outdir) throws IOException {
new File(outdir).mkdirs();
String jvmArg = System.getProperty(JVM_ARG);
if (jvmArg != null && !jvmArg.trim().isEmpty()) {
fileName = jvmArg;
}
return new PrintWriter(newBufferedWriter(new File(outdir, fileName).toPath(), UTF_8));
}
protected void writeDocumentStart() {
writer.println("");
writer.println("");
}
protected void writeHead() {
writer.println("");
writer.println("");
writer.println("TestNG Report ");
writeStylesheet();
writer.println("");
}
protected void writeStylesheet() {
writer.print("");
}
protected void writeBody() {
writer.println("");
writeSuiteSummary();
writeScenarioSummary();
writeScenarioDetails();
writer.println("");
}
protected void writeDocumentEnd() {
writer.println("");
}
protected void writeSuiteSummary() {
NumberFormat integerFormat = NumberFormat.getIntegerInstance();
NumberFormat decimalFormat = NumberFormat.getNumberInstance();
int totalPassedTests = 0;
int totalSkippedTests = 0;
int totalFailedTests = 0;
long totalDuration = 0;
writer.println("");
writer.print("");
writer.print("Test ");
writer.print("# Passed ");
writer.print("# Skipped ");
writer.print("# Failed ");
writer.print("Time (ms) ");
writer.print("Included Groups ");
writer.print("Excluded Groups ");
writer.println(" ");
int testIndex = 0;
for (SuiteResult suiteResult : suiteResults) {
writer.print("");
writer.print(Utils.escapeHtml(suiteResult.getSuiteName()));
writer.println(" ");
for (TestResult testResult : suiteResult.getTestResults()) {
int passedTests = testResult.getPassedTestCount();
int skippedTests = testResult.getSkippedTestCount();
int failedTests = testResult.getFailedTestCount();
long duration = testResult.getDuration();
writer.print("");
buffer.setLength(0);
writeTableData(buffer.append("")
.append(Utils.escapeHtml(testResult.getTestName()))
.append("").toString());
writeTableData(integerFormat.format(passedTests), "num");
writeTableData(integerFormat.format(skippedTests),
(skippedTests > 0 ? "num attn" : "num"));
writeTableData(integerFormat.format(failedTests),
(failedTests > 0 ? "num attn" : "num"));
writeTableData(decimalFormat.format(duration), "num");
writeTableData(testResult.getIncludedGroups());
writeTableData(testResult.getExcludedGroups());
writer.println(" ");
totalPassedTests += passedTests;
totalSkippedTests += skippedTests;
totalFailedTests += failedTests;
totalDuration += duration;
testIndex++;
}
}
// Print totals if there was more than one test
if (testIndex > 1) {
writer.print("");
writer.print("Total ");
writeTableHeader(integerFormat.format(totalPassedTests), "num");
writeTableHeader(integerFormat.format(totalSkippedTests),
(totalSkippedTests > 0 ? "num attn" : "num"));
writeTableHeader(integerFormat.format(totalFailedTests),
(totalFailedTests > 0 ? "num attn" : "num"));
writeTableHeader(decimalFormat.format(totalDuration), "num");
writer.print(" ");
writer.println(" ");
}
writer.println("
");
}
/**
* Writes a summary of all the test scenarios.
*/
protected void writeScenarioSummary() {
writer.print("");
writer.print("");
writer.print("");
writer.print("Class ");
writer.print("Method ");
writer.print("Start ");
writer.print("Time (ms) ");
writer.print(" ");
writer.print("");
int testIndex = 0;
int scenarioIndex = 0;
for (SuiteResult suiteResult : suiteResults) {
writer.print("");
writer.print(Utils.escapeHtml(suiteResult.getSuiteName()));
writer.print(" ");
for (TestResult testResult : suiteResult.getTestResults()) {
writer.printf("", testIndex);
String testName = Utils.escapeHtml(testResult.getTestName());
int startIndex = scenarioIndex;
scenarioIndex += writeScenarioSummary(testName
+ " — failed (configuration methods)",
testResult.getFailedConfigurationResults(), "failed",
scenarioIndex);
scenarioIndex += writeScenarioSummary(testName
+ " — failed", testResult.getFailedTestResults(),
"failed", scenarioIndex);
scenarioIndex += writeScenarioSummary(testName
+ " — skipped (configuration methods)",
testResult.getSkippedConfigurationResults(), "skipped",
scenarioIndex);
scenarioIndex += writeScenarioSummary(testName
+ " — skipped",
testResult.getSkippedTestResults(), "skipped",
scenarioIndex);
scenarioIndex += writeScenarioSummary(testName
+ " — passed", testResult.getPassedTestResults(),
"passed", scenarioIndex);
if (scenarioIndex == startIndex) {
writer.print(" ");
}
writer.println("");
testIndex++;
}
}
writer.println("
");
}
/**
* Writes the scenario summary for the results of a given state for a single
* test.
*/
private int writeScenarioSummary(String description,
List classResults, String cssClassPrefix,
int startingScenarioIndex) {
int scenarioCount = 0;
if (!classResults.isEmpty()) {
writer.print("");
writer.print(description);
writer.print(" ");
int scenarioIndex = startingScenarioIndex;
int classIndex = 0;
for (ClassResult classResult : classResults) {
String cssClass = cssClassPrefix
+ ((classIndex % 2) == 0 ? "even" : "odd");
buffer.setLength(0);
int scenariosPerClass = 0;
int methodIndex = 0;
for (MethodResult methodResult : classResult.getMethodResults()) {
List results = methodResult.getResults();
int resultsCount = results.size();
assert resultsCount > 0;
ITestResult firstResult = results.iterator().next();
String methodName = Utils.escapeHtml(firstResult
.getMethod().getMethodName());
long start = firstResult.getStartMillis();
long duration = firstResult.getEndMillis() - start;
// The first method per class shares a row with the class
// header
if (methodIndex > 0) {
buffer.append("");
}
// Write the timing information with the first scenario per
// method
buffer.append("").append(methodName)
.append(" ").append("").append(start)
.append(" ").append("")
.append(duration).append(" ");
scenarioIndex++;
// Write the remaining scenarios for the method
for (int i = 1; i < resultsCount; i++) {
buffer.append("").append("")
.append(methodName).append(" ");
scenarioIndex++;
}
scenariosPerClass += resultsCount;
methodIndex++;
}
// Write the test results for the class
writer.print("");
writer.print("");
writer.print(Utils.escapeHtml(classResult.getClassName()));
writer.print(" ");
writer.print(buffer);
classIndex++;
}
scenarioCount = scenarioIndex - startingScenarioIndex;
}
return scenarioCount;
}
/**
* Writes the details for all test scenarios.
*/
protected void writeScenarioDetails() {
int scenarioIndex = 0;
for (SuiteResult suiteResult : suiteResults) {
for (TestResult testResult : suiteResult.getTestResults()) {
writer.print("");
writer.print(Utils.escapeHtml(testResult.getTestName()));
writer.print("
");
scenarioIndex += writeScenarioDetails(
testResult.getFailedConfigurationResults(),
scenarioIndex);
scenarioIndex += writeScenarioDetails(
testResult.getFailedTestResults(), scenarioIndex);
scenarioIndex += writeScenarioDetails(
testResult.getSkippedConfigurationResults(),
scenarioIndex);
scenarioIndex += writeScenarioDetails(
testResult.getSkippedTestResults(), scenarioIndex);
scenarioIndex += writeScenarioDetails(
testResult.getPassedTestResults(), scenarioIndex);
}
}
}
/**
* Writes the scenario details for the results of a given state for a single
* test.
*/
private int writeScenarioDetails(List classResults,
int startingScenarioIndex) {
int scenarioIndex = startingScenarioIndex;
for (ClassResult classResult : classResults) {
String className = classResult.getClassName();
for (MethodResult methodResult : classResult.getMethodResults()) {
List results = methodResult.getResults();
assert !results.isEmpty();
String label = Utils
.escapeHtml(className
+ "#"
+ results.iterator().next().getMethod()
.getMethodName());
for (ITestResult result : results) {
writeScenario(scenarioIndex, label, result);
scenarioIndex++;
}
}
}
return scenarioIndex - startingScenarioIndex;
}
/**
* Writes the details for an individual test scenario.
*/
private void writeScenario(int scenarioIndex, String label,
ITestResult result) {
writer.print("");
writer.print(label);
writer.print("
");
writer.print("");
boolean hasRows = false;
// Write test parameters (if any)
Object[] parameters = result.getParameters();
int parameterCount = (parameters == null ? 0 : parameters.length);
if (parameterCount > 0) {
writer.print("");
for (int i = 1; i <= parameterCount; i++) {
writer.print("Parameter #");
writer.print(i);
writer.print(" ");
}
writer.print(" ");
for (Object parameter : parameters) {
writer.print("");
writer.print(Utils.escapeHtml(Utils.toString(parameter)));
writer.print(" ");
}
writer.print(" ");
hasRows = true;
}
// Write reporter messages (if any)
List reporterMessages = Reporter.getOutput(result);
if (!reporterMessages.isEmpty()) {
writer.print(" 1) {
writer.printf(" colspan=\"%d\"", parameterCount);
}
writer.print(">Messages ");
writer.print(" 1) {
writer.printf(" colspan=\"%d\"", parameterCount);
}
writer.print(">");
writeReporterMessages(reporterMessages);
writer.print(" ");
hasRows = true;
}
// Write exception (if any)
Throwable throwable = result.getThrowable();
if (throwable != null) {
writer.print(" 1) {
writer.printf(" colspan=\"%d\"", parameterCount);
}
writer.print(">");
writer.print((result.getStatus() == ITestResult.SUCCESS ? "Expected Exception"
: "Exception"));
writer.print(" ");
writer.print(" 1) {
writer.printf(" colspan=\"%d\"", parameterCount);
}
writer.print(">");
writeStackTrace(throwable);
writer.print(" ");
hasRows = true;
}
if (!hasRows) {
writer.print(" 1) {
writer.printf(" colspan=\"%d\"", parameterCount);
}
writer.print(" class=\"invisible\"/> ");
}
writer.print("
");
writer.println("");
}
protected void writeReporterMessages(List reporterMessages) {
writer.print("");
writer.print(Utils.shortStackTrace(throwable, true));
writer.print("");
}
/**
* Writes a TH element with the specified contents and CSS class names.
*
* @param html
* the HTML contents
* @param cssClasses
* the space-delimited CSS classes or null if there are no
* classes to apply
*/
protected void writeTableHeader(String html, String cssClasses) {
writeTag("th", html, cssClasses);
}
/**
* Writes a TD element with the specified contents.
*
* @param html
* the HTML contents
*/
protected void writeTableData(String html) {
writeTableData(html, null);
}
/**
* Writes a TD element with the specified contents and CSS class names.
*
* @param html
* the HTML contents
* @param cssClasses
* the space-delimited CSS classes or null if there are no
* classes to apply
*/
protected void writeTableData(String html, String cssClasses) {
writeTag("td", html, cssClasses);
}
/**
* Writes an arbitrary HTML element with the specified contents and CSS
* class names.
*
* @param tag
* the tag name
* @param html
* the HTML contents
* @param cssClasses
* the space-delimited CSS classes or null if there are no
* classes to apply
*/
protected void writeTag(String tag, String html, String cssClasses) {
writer.print("<");
writer.print(tag);
if (cssClasses != null) {
writer.print(" class=\"");
writer.print(cssClasses);
writer.print("\"");
}
writer.print(">");
writer.print(html);
writer.print("");
}
/**
* Groups {@link TestResult}s by suite.
*/
protected static class SuiteResult {
private final String suiteName;
private final List testResults = Lists.newArrayList();
public SuiteResult(ISuite suite) {
suiteName = suite.getName();
for (ISuiteResult suiteResult : suite.getResults().values()) {
testResults.add(new TestResult(suiteResult.getTestContext()));
}
}
public String getSuiteName() {
return suiteName;
}
/**
* @return the test results (possibly empty)
*/
public List getTestResults() {
return testResults;
}
}
/**
* Groups {@link ClassResult}s by test, type (configuration or test), and
* status.
*/
protected static class TestResult {
/**
* Orders test results by class name and then by method name (in
* lexicographic order).
*/
protected static final Comparator RESULT_COMPARATOR = new Comparator() {
@Override
public int compare(ITestResult o1, ITestResult o2) {
int result = o1.getTestClass().getName()
.compareTo(o2.getTestClass().getName());
if (result == 0) {
result = o1.getMethod().getMethodName()
.compareTo(o2.getMethod().getMethodName());
}
return result;
}
};
private final String testName;
private final List failedConfigurationResults;
private final List failedTestResults;
private final List skippedConfigurationResults;
private final List skippedTestResults;
private final List passedTestResults;
private final int failedTestCount;
private final int skippedTestCount;
private final int passedTestCount;
private final long duration;
private final String includedGroups;
private final String excludedGroups;
public TestResult(ITestContext context) {
testName = context.getName();
Set failedConfigurations = context
.getFailedConfigurations().getAllResults();
Set failedTests = context.getFailedTests()
.getAllResults();
Set skippedConfigurations = context
.getSkippedConfigurations().getAllResults();
Set skippedTests = context.getSkippedTests()
.getAllResults();
Set passedTests = context.getPassedTests()
.getAllResults();
failedConfigurationResults = groupResults(failedConfigurations);
failedTestResults = groupResults(failedTests);
skippedConfigurationResults = groupResults(skippedConfigurations);
skippedTestResults = groupResults(skippedTests);
passedTestResults = groupResults(passedTests);
failedTestCount = failedTests.size();
skippedTestCount = skippedTests.size();
passedTestCount = passedTests.size();
duration = context.getEndDate().getTime()
- context.getStartDate().getTime();
includedGroups = formatGroups(context.getIncludedGroups());
excludedGroups = formatGroups(context.getExcludedGroups());
}
/**
* Groups test results by method and then by class.
*/
protected List groupResults(Set results) {
List classResults = Lists.newArrayList();
if (!results.isEmpty()) {
List resultsPerClass = Lists.newArrayList();
List resultsPerMethod = Lists.newArrayList();
List resultsList = Lists.newArrayList(results);
Collections.sort(resultsList, RESULT_COMPARATOR);
Iterator resultsIterator = resultsList.iterator();
assert resultsIterator.hasNext();
ITestResult result = resultsIterator.next();
resultsPerMethod.add(result);
String previousClassName = result.getTestClass().getName();
String previousMethodName = result.getMethod().getMethodName();
while (resultsIterator.hasNext()) {
result = resultsIterator.next();
String className = result.getTestClass().getName();
if (!previousClassName.equals(className)) {
// Different class implies different method
assert !resultsPerMethod.isEmpty();
resultsPerClass.add(new MethodResult(resultsPerMethod));
resultsPerMethod = Lists.newArrayList();
assert !resultsPerClass.isEmpty();
classResults.add(new ClassResult(previousClassName,
resultsPerClass));
resultsPerClass = Lists.newArrayList();
previousClassName = className;
previousMethodName = result.getMethod().getMethodName();
} else {
String methodName = result.getMethod().getMethodName();
if (!previousMethodName.equals(methodName)) {
assert !resultsPerMethod.isEmpty();
resultsPerClass.add(new MethodResult(resultsPerMethod));
resultsPerMethod = Lists.newArrayList();
previousMethodName = methodName;
}
}
resultsPerMethod.add(result);
}
assert !resultsPerMethod.isEmpty();
resultsPerClass.add(new MethodResult(resultsPerMethod));
assert !resultsPerClass.isEmpty();
classResults.add(new ClassResult(previousClassName,
resultsPerClass));
}
return classResults;
}
public String getTestName() {
return testName;
}
/**
* @return the results for failed configurations (possibly empty)
*/
public List getFailedConfigurationResults() {
return failedConfigurationResults;
}
/**
* @return the results for failed tests (possibly empty)
*/
public List getFailedTestResults() {
return failedTestResults;
}
/**
* @return the results for skipped configurations (possibly empty)
*/
public List getSkippedConfigurationResults() {
return skippedConfigurationResults;
}
/**
* @return the results for skipped tests (possibly empty)
*/
public List getSkippedTestResults() {
return skippedTestResults;
}
/**
* @return the results for passed tests (possibly empty)
*/
public List getPassedTestResults() {
return passedTestResults;
}
public int getFailedTestCount() {
return failedTestCount;
}
public int getSkippedTestCount() {
return skippedTestCount;
}
public int getPassedTestCount() {
return passedTestCount;
}
public long getDuration() {
return duration;
}
public String getIncludedGroups() {
return includedGroups;
}
public String getExcludedGroups() {
return excludedGroups;
}
/**
* Formats an array of groups for display.
*/
protected String formatGroups(String[] groups) {
if (groups.length == 0) {
return "";
}
StringBuilder builder = new StringBuilder();
builder.append(groups[0]);
for (int i = 1; i < groups.length; i++) {
builder.append(", ").append(groups[i]);
}
return builder.toString();
}
}
/**
* Groups {@link MethodResult}s by class.
*/
protected static class ClassResult {
private final String className;
private final List methodResults;
/**
* @param className
* the class name
* @param methodResults
* the non-null, non-empty {@link MethodResult} list
*/
public ClassResult(String className, List methodResults) {
this.className = className;
this.methodResults = methodResults;
}
public String getClassName() {
return className;
}
/**
* @return the non-null, non-empty {@link MethodResult} list
*/
public List getMethodResults() {
return methodResults;
}
}
/**
* Groups test results by method.
*/
protected static class MethodResult {
private final List results;
/**
* @param results
* the non-null, non-empty result list
*/
public MethodResult(List results) {
this.results = results;
}
/**
* @return the non-null, non-empty result list
*/
public List getResults() {
return results;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy
");
}
protected void writeStackTrace(Throwable throwable) {
writer.print("