
io.cucumber.testngxmlformatter.XmlReportWriter Maven / Gradle / Ivy
package io.cucumber.testngxmlformatter;
import io.cucumber.messages.types.Exception;
import io.cucumber.messages.types.Feature;
import io.cucumber.messages.types.TestCaseStarted;
import io.cucumber.messages.types.TestStepResult;
import io.cucumber.messages.types.TestStepResultStatus;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import java.io.Writer;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.function.Supplier;
import static io.cucumber.messages.types.TestStepResultStatus.PASSED;
import static io.cucumber.messages.types.TestStepResultStatus.SKIPPED;
class XmlReportWriter {
private final XmlReportData data;
XmlReportWriter(XmlReportData data) {
this.data = data;
}
void writeXmlReport(Writer out) throws XMLStreamException {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
EscapingXmlStreamWriter writer = new EscapingXmlStreamWriter(factory.createXMLStreamWriter(out));
writer.writeStartDocument("UTF-8", "1.0");
writer.writeNewLine();
writeTestngResults(writer);
writer.writeEndDocument();
writer.flush();
}
private void writeTestngResults(EscapingXmlStreamWriter writer) throws XMLStreamException {
writer.writeStartElement("testng-results");
writeTestngResultsAttributes(writer);
writer.writeNewLine();
writeSuite(writer);
writer.writeEndElement();
writer.writeNewLine();
}
private void writeTestngResultsAttributes(EscapingXmlStreamWriter writer) throws XMLStreamException {
Map counts = data.getTestCaseStatusCounts();
writer.writeAttribute("failed", String.valueOf(countFailures(counts)));
writer.writeAttribute("passed", counts.getOrDefault(PASSED, 0L).toString());
writer.writeAttribute("skipped", counts.getOrDefault(SKIPPED, 0L).toString());
writer.writeAttribute("total", String.valueOf(data.getTestCaseCount()));
}
private static long countFailures(Map counts) {
return createNotPassedNotSkippedSet().stream().mapToLong(s -> counts.getOrDefault(s, 0L)).sum();
}
private static EnumSet createNotPassedNotSkippedSet() {
EnumSet notPassedNotSkipped = EnumSet.allOf(TestStepResultStatus.class);
notPassedNotSkipped.remove(PASSED);
notPassedNotSkipped.remove(SKIPPED);
return notPassedNotSkipped;
}
private void writeSuite(EscapingXmlStreamWriter writer) throws XMLStreamException {
writer.writeStartElement("suite");
writeSuiteAttributes(writer);
writer.writeNewLine();
writeTest(writer);
writer.writeEndElement();
writer.writeNewLine();
}
private void writeSuiteAttributes(EscapingXmlStreamWriter writer) throws XMLStreamException {
writer.writeAttribute("name", "Cucumber");
writer.writeAttribute("duration-ms", String.valueOf(data.getSuiteDurationInMilliSeconds()));
}
private void writeTest(EscapingXmlStreamWriter writer) throws XMLStreamException {
writer.writeStartElement("test");
writeTestAttributes(writer);
writer.writeNewLine();
for (Entry, List> entry : data.getAllTestCaseStartedGroupedByFeature()) {
writer.writeStartElement("class");
String featureName = entry.getKey()
.map(Feature::getName)
.orElse("Unknown");
writer.writeAttribute("name", featureName);
writer.writeNewLine();
for (TestCaseStarted testCaseStarted : entry.getValue()) {
writeTestMethod(writer, testCaseStarted);
}
writer.writeEndElement();
writer.writeNewLine();
}
writer.writeEndElement();
writer.writeNewLine();
}
private void writeTestAttributes(EscapingXmlStreamWriter writer) throws XMLStreamException {
writer.writeAttribute("name", "Cucumber");
writer.writeAttribute("duration-ms", String.valueOf(data.getSuiteDurationInMilliSeconds()));
}
private void writeTestMethod(EscapingXmlStreamWriter writer, TestCaseStarted testCaseStarted) throws XMLStreamException {
TestStepResult result = data.getTestCaseStatus(testCaseStarted);
boolean passing = isPassed(result);
if (passing) {
writer.writeEmptyElement("test-method");
} else {
writer.writeStartElement("test-method");
}
writeTestMethodAttributes(writer, testCaseStarted, result);
if (!passing) {
writer.writeNewLine();
writeException(writer, testCaseStarted, result);
writer.writeEndElement();
}
writer.writeNewLine();
}
private static boolean isPassed(TestStepResult result) {
TestStepResultStatus status = result.getStatus();
return status == PASSED;
}
private void writeTestMethodAttributes(EscapingXmlStreamWriter writer, TestCaseStarted testCaseStarted, TestStepResult result) throws XMLStreamException {
writer.writeAttribute("name", data.getPickleName(testCaseStarted));
writer.writeAttribute("status", writeStatus(result));
writer.writeAttribute("duration-ms", String.valueOf(data.getDurationInMilliSeconds(testCaseStarted)));
writer.writeAttribute("started-at", data.getStartedAt(testCaseStarted));
writer.writeAttribute("finished-at", data.getFinishedAt(testCaseStarted));
}
private String writeStatus(TestStepResult status) {
switch (status.getStatus()) {
case PASSED:
return "PASS";
case SKIPPED:
return "SKIP";
default:
return "FAIL";
}
}
private void writeException(EscapingXmlStreamWriter writer, TestCaseStarted testCaseStarted, TestStepResult result) throws XMLStreamException {
Exception exceptionOrSkippedOrUndefined = result.getException()
.orElseGet(undefinedOrSkippedException(result.getStatus()));
Optional stackTrace = exceptionOrSkippedOrUndefined.getStackTrace();
writer.writeStartElement("exception");
writeExceptionAttributes(writer, exceptionOrSkippedOrUndefined);
writer.writeNewLine();
writeMessage(writer, testCaseStarted);
if (stackTrace.isPresent()) {
writer.writeStartElement("full-stacktrace");
writer.writeNewLine();
writer.writeCData(stackTrace.get());
writer.writeNewLine();
writer.writeEndElement();
writer.writeNewLine();
}
writer.writeEndElement();
writer.writeNewLine();
}
private static Supplier undefinedOrSkippedException(TestStepResultStatus status) {
if (status == SKIPPED) {
// Skipped may not be caused by an exception, i.e. `return "skipped` in JS or Ruby.
return () -> new Exception(
"The scenario has skipped step(s)",
null,
"The scenario has skipped step(s)"
);
}
// Undefined is not caused by an exception
return () -> new Exception(
"The scenario has undefined step(s)",
null,
"The scenario has undefined step(s)"
);
}
private void writeExceptionAttributes(EscapingXmlStreamWriter writer, Exception exception) throws XMLStreamException {
writer.writeAttribute("class", exception.getType());
}
private void writeMessage(EscapingXmlStreamWriter writer, TestCaseStarted testCaseStarted) throws XMLStreamException {
List> results = data.getStepsAndResult(testCaseStarted);
if (results.isEmpty()) {
return;
}
writer.writeStartElement("message");
writer.writeCData(createStepResultList(results));
writer.writeEndElement();
writer.writeNewLine();
}
private String createStepResultList(List> results) {
StringBuilder sb = new StringBuilder();
sb.append("\n");
results.forEach(r -> {
String stepText = r.getKey();
String status = r.getValue();
sb.append(stepText);
sb.append(".");
for (int i = 75 - stepText.length(); i > 0; i--) {
sb.append(".");
}
sb.append(status);
sb.append("\n");
});
return sb.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy