package fitnesse.reporting.history;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import fitnesse.testsystems.ExecutionResult;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import fitnesse.FitNesseVersion;
import fitnesse.testsystems.TestSummary;
import fitnesse.util.DateTimeUtil;
import fitnesse.util.TimeMeasurement;
import fitnesse.util.XmlUtil;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import static java.lang.String.format;
public abstract class ExecutionReport {
private static final int NO_RUN_TIME = -1;
private String version;
private String rootPath;
private TestSummary finalCounts = new TestSummary(0, 0, 0, 0);
public Date date;
private long totalRunTimeInMillis = NO_RUN_TIME;
private List<ExecutionLogReport> executionLogs = new ArrayList<>();
protected ExecutionReport() {
version = new FitNesseVersion().toString();
}
public ExecutionReport(FitNesseVersion version, String rootPath) {
this.version = version == null ? "null" : version.toString();
this.rootPath = rootPath;
}
public void tallyPageCounts(ExecutionResult result) {
finalCounts.add(result);
}
@Override
public String toString() {
return rootPath;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ExecutionReport))
return false;
ExecutionReport e = (ExecutionReport) o;
if (!StringUtils.equals(rootPath, e.rootPath))
return false;
else if (!StringUtils.equals(version, e.version))
return false;
else if (!DateTimeUtil.datesNullOrEqual(date, e.date))
return false;
else if(!finalCounts.equals(e.finalCounts))
return false;
else if(totalRunTimeInMillis != e.totalRunTimeInMillis)
return false;
return true;
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(rootPath).append(version).append(date).hashCode();
}
public static ExecutionReport makeReport(String xmlString) throws InvalidReportException {
Document xmlDocument = null;
try {
xmlDocument = XmlUtil.newDocument(xmlString);
} catch (IOException | SAXException e) {
throw new InvalidReportException(format("%s is not a valid execution report", xmlString), e);
}
Element documentElement = xmlDocument.getDocumentElement();
String documentNodeName = documentElement.getNodeName();
switch (documentNodeName) {
case "testResults":
return new TestExecutionReport(xmlDocument);
case "suiteResults":
return new SuiteExecutionReport(xmlDocument);
default:
throw new InvalidReportException(format("%s is not a valid document element tag for an Execution Report.", documentNodeName));
}
}
protected void unpackCommonFields(Element documentElement) throws InvalidReportException {
version = XmlUtil.getTextValue(documentElement, "FitNesseVersion");
rootPath = XmlUtil.getTextValue(documentElement, "rootPath");
String dateString = XmlUtil.getTextValue(documentElement, "date");
if (dateString != null)
try {
date = DateTimeUtil.getDateFromString(dateString);
} catch (ParseException e) {
throw new InvalidReportException(format("'%s' is not a valid date.", dateString), e);
}
unpackFinalCounts(documentElement);
totalRunTimeInMillis = getTotalRunTimeInMillisOrMinusOneIfNotPresent(documentElement);
}
protected long getTotalRunTimeInMillisOrMinusOneIfNotPresent(Element documentElement) {
String textValue = XmlUtil.getTextValue(documentElement, "totalRunTimeInMillis");
return textValue == null ? NO_RUN_TIME : Long.parseLong(textValue);
}
private void unpackFinalCounts(Element testResults) {
Element counts = XmlUtil.getElementByTagName(testResults, "finalCounts");
if (counts != null) {
finalCounts = new TestSummary(
Integer.parseInt(XmlUtil.getTextValue(counts, "right")),
Integer.parseInt(XmlUtil.getTextValue(counts, "wrong")),
Integer.parseInt(XmlUtil.getTextValue(counts, "ignores")),
Integer.parseInt(XmlUtil.getTextValue(counts, "exceptions"))
);
}
}
protected void unpackXml(Document xmlDoc) throws InvalidReportException {
Element historyDocument = xmlDoc.getDocumentElement();
unpackCommonFields(historyDocument);
unpackResults(historyDocument);
unpackExecutionLogs(historyDocument);
}
private void unpackExecutionLogs(Element historyDocument) {
NodeList logs = historyDocument.getElementsByTagName("executionLog");
if (logs == null) {
return;
}
for (int i = 0; i < logs.getLength(); i++) {
Element log = (Element) logs.item(i);
String commandLine = XmlUtil.getTextValue(log, "command");
String testSystemName = XmlUtil.getTextValue(log, "testSystem");
String exitCode = XmlUtil.getTextValue(log, "exitCode");
String stdOut = XmlUtil.getTextValue(log, "stdOut");
String stdErr = XmlUtil.getTextValue(log, "stdErr");
ExecutionLogReport report = new ExecutionLogReport(commandLine, testSystemName);
if (StringUtils.isNotBlank(exitCode)) {
report.exitCode(Integer.parseInt(exitCode));
}
if (stdOut != null) {
report.setStdOut(stdOut);
}
if (stdErr != null) {
report.setStdErr(stdErr);
}
NodeList exceptionNodes = log.getElementsByTagName("exception");
if (exceptionNodes != null) {
for (int k = 0; k < exceptionNodes.getLength(); k++) {
report.exceptionOccurred(new Exception(exceptionNodes.item(k).getTextContent()));
}
}
executionLogs.add(report);
}
}
protected abstract void unpackResults(Element testResults) throws InvalidReportException;
public TestSummary getFinalCounts() {
return finalCounts;
}
public String getVersion() {
return version;
}
public long getTotalRunTimeInMillis() {
return totalRunTimeInMillis;
}
public void setTotalRunTimeInMillis(TimeMeasurement totalTimeMeasurement) {
totalRunTimeInMillis = totalTimeMeasurement.elapsed();
}
public String getRootPath() {
return rootPath;
}
public Date getDate() {
return new Date(date.getTime());
}
public void setDate(Date date) {
this.date = new Date(date.getTime());
}
public boolean hasRunTimes() {
return totalRunTimeInMillis != NO_RUN_TIME;
}
public List<ExecutionLogReport> getExecutionLogs() {
return new ArrayList<>(executionLogs);
}
public void addExecutionContext(String command, String testSystemName) {
executionLogs.add(new ExecutionLogReport(command, testSystemName));
}
private ExecutionLogReport executionLogReport() {
ExecutionLogReport log;
if (!executionLogs.isEmpty()) {
log = executionLogs.get(executionLogs.size() - 1);
} else {
log = new ExecutionLogReport("", "");
executionLogs.add(log);
}
return log;
}
public void addStdOut(String output) {
executionLogReport().addStdOut(output);
}
public void addStdErr(String output) {
executionLogReport().addStdErr(output);
}
public void exitCode(int exitCode) {
executionLogReport().exitCode(exitCode);
}
public void exceptionOccurred(Throwable e) {
executionLogReport().exceptionOccurred(e);
}
public static class ExecutionLogReport {
private final String command;
private final String testSystemName;
private StringBuffer stdOut = new StringBuffer();
private StringBuffer stdErr = new StringBuffer();
private int exitCode;
private List<Throwable> exceptions = new ArrayList<>();
public ExecutionLogReport(String command, String testSystemName) {
this.command = command;
this.testSystemName = testSystemName;
}
public String getCommand() {
return command;
}
public String getTestSystemName() {
return testSystemName;
}
public void addStdOut(String output) {
stdOut.append(output).append("\n");
}
public void setStdOut(String output) {
this.stdOut.append(output);
}
public String getStdOut() {
return stdOut.toString();
}
public void addStdErr(String output) {
stdErr.append(output).append("\n");
}
public void setStdErr(String output) {
this.stdErr.append(output);
}
public String getStdErr() {
return stdErr.toString();
}
public void exitCode(int exitCode) {
this.exitCode = exitCode;
}
public int getExitCode() {
return exitCode;
}
public void exceptionOccurred(Throwable e) {
exceptions.add(e);
}
public List<Throwable> getExceptions() {
return new ArrayList<>(exceptions);
}
}
}