NIHVIVO-222 Consolidate pre-run and post-run data into SuiteData, backing away from SuiteContents and SuiteResults, except as data-gathering classes. Distinguish between INGORED and WARN as two valid statuses with distinct meanings.

This commit is contained in:
jeb228 2010-08-23 15:00:34 +00:00
parent ff1e62c0a9
commit 7a2c5691cf
10 changed files with 435 additions and 317 deletions

View file

@ -29,6 +29,13 @@ public class ModelCleaner {
this.tomcatController = tomcatController; this.tomcatController = tomcatController;
sanityCheck(); sanityCheck();
try {
tomcatController.stopTheWebapp();
tomcatController.startTheWebapp();
} catch (CommandRunnerException e) {
throw new FatalException(
"sanityCheck: Failed to stop and start Tomcat.", e);
}
} }
private void sanityCheck() { private void sanityCheck() {

View file

@ -61,7 +61,7 @@ public class SeleniumRunner {
listener.runEndTime(); listener.runEndTime();
outputManager.summarizeOutput(dataModel); outputManager.summarizeOutput(dataModel);
success = (dataModel.getRunStatus() == Status.OK); success = Status.isSuccess(dataModel.getRunStatus());
} catch (IOException e) { } catch (IOException e) {
listener.runFailed(e); listener.runFailed(e);
success = false; success = false;

View file

@ -11,7 +11,6 @@ import java.io.Reader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
@ -411,7 +410,6 @@ public class SeleniumRunnerParameters {
* recognize a suite directory because it contains a file named Suite.html. * recognize a suite directory because it contains a file named Suite.html.
*/ */
public Collection<File> findSuiteDirs(File parentDir) { public Collection<File> findSuiteDirs(File parentDir) {
System.out.println("parentDir: " + parentDir);
return Arrays.asList(parentDir.listFiles(new FileFilter() { return Arrays.asList(parentDir.listFiles(new FileFilter() {
public boolean accept(File pathname) { public boolean accept(File pathname) {
if (!pathname.isDirectory()) { if (!pathname.isDirectory()) {

View file

@ -12,13 +12,13 @@ public enum Status {
/** /**
* One or more tests have not been run yet. * One or more tests have not been run yet.
*/ */
PENDING(""), PENDING("pending"),
/** /**
* Any test failure was ignored, and any messages were no worse than * Will not run because it is ignored, or has run and failed but the failure
* warnings. * is ignored.
*/ */
WARN("fair"), IGNORED("fair"),
/** /**
* A test failed and could not be ignored, or an error message was * A test failed and could not be ignored, or an error message was
@ -44,4 +44,9 @@ public enum Status {
return s2; return s2;
} }
} }
/** Anything except ERROR is considered to be a success. */
public static boolean isSuccess(Status status) {
return status != Status.ERROR;
}
} }

View file

@ -6,6 +6,7 @@ import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -16,10 +17,10 @@ import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests;
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo; import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo;
import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats; import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats;
import edu.cornell.mannlib.vitro.utilities.testrunner.Status; import edu.cornell.mannlib.vitro.utilities.testrunner.Status;
import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData.TestData;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener; import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener.ProcessOutput; import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener.ProcessOutput;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults; import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults.TestResults;
/** /**
* Collect all that we know about suites, tests, and their current status. * Collect all that we know about suites, tests, and their current status.
@ -38,16 +39,12 @@ public class DataModel {
private Status runStatus = Status.PENDING; private Status runStatus = Status.PENDING;
private final SortedMap<String, SuiteData> suiteDataMap = new TreeMap<String, SuiteData>(); private final SortedMap<String, SuiteData> suiteDataMap = new TreeMap<String, SuiteData>();
private final List<SuiteData> pendingSuites = new ArrayList<SuiteData>(); private final EnumMap<Status, List<SuiteData>> suiteMapByStatus = new EnumMap<Status, List<SuiteData>>(
private final List<SuiteData> passingSuites = new ArrayList<SuiteData>(); Status.class);
private final List<SuiteData> failingSuites = new ArrayList<SuiteData>();
private final List<SuiteData> ignoredSuites = new ArrayList<SuiteData>();
private final List<TestResults> allTests = new ArrayList<TestResults>(); private final List<TestData> allTests = new ArrayList<TestData>();
private final List<TestResults> pendingTests = new ArrayList<TestResults>(); private final EnumMap<Status, List<TestData>> testMapByStatus = new EnumMap<Status, List<TestData>>(
private final List<TestResults> passingTests = new ArrayList<TestResults>(); Status.class);
private final List<TestResults> failingTests = new ArrayList<TestResults>();
private final List<TestResults> ignoredTests = new ArrayList<TestResults>();
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// Constructor // Constructor
@ -107,20 +104,19 @@ public class DataModel {
runStatus = Status.OK; runStatus = Status.OK;
suiteDataMap.clear(); suiteDataMap.clear();
suiteMapByStatus.clear();
ignoredSuites.clear(); for (Status s : Status.values()) {
pendingSuites.clear(); suiteMapByStatus.put(s, new ArrayList<SuiteData>());
failingSuites.clear(); }
passingSuites.clear();
allTests.clear(); allTests.clear();
ignoredTests.clear(); testMapByStatus.clear();
pendingTests.clear(); for (Status s : Status.values()) {
failingTests.clear(); testMapByStatus.put(s, new ArrayList<TestData>());
passingTests.clear(); }
/* /*
* Suite data. * Populate the Suite map with all Suites.
*/ */
Map<String, SuiteResults> resultsMap = new HashMap<String, SuiteResults>(); Map<String, SuiteResults> resultsMap = new HashMap<String, SuiteResults>();
for (SuiteResults result : suiteResults) { for (SuiteResults result : suiteResults) {
@ -142,90 +138,30 @@ public class DataModel {
} }
/* /*
* Tallys of suites and tests. * Map the Suites by status.
*/ */
for (SuiteData sd : suiteDataMap.values()) { for (SuiteData s : suiteDataMap.values()) {
switch (sd.getSuiteStatus()) { getSuites(s.getStatus()).add(s);
case ERROR: }
failingSuites.add(sd);
break; /**
case PENDING: * Populate the Test map with all Tests, and map by status.
pendingSuites.add(sd); */
break; for (SuiteData s : suiteDataMap.values()) {
case WARN: for (TestData t : s.getTestMap().values()) {
ignoredSuites.add(sd); allTests.add(t);
break; getTests(t.getStatus()).add(t);
default: // Status.OK
passingSuites.add(sd);
break;
} }
} }
for (SuiteData sd : suiteDataMap.values()) { if (logStats.hasErrors() || !getSuites(Status.ERROR).isEmpty()) {
SuiteResults sResult = sd.getResults();
if (sResult != null) {
tallyTestResults(sResult);
} else if (sd.getContents() != null) {
tallyTestContents(sd);
}
}
for (TestResults tResult : allTests) {
switch (tResult.getStatus()) {
case OK:
passingTests.add(tResult);
break;
case PENDING:
pendingTests.add(tResult);
break;
case WARN:
ignoredTests.add(tResult);
break;
default: // Status.ERROR
failingTests.add(tResult);
break;
}
}
/*
* Overall status. Warnings in the log are scary, but ignored tests are
* OK.
*/
if (logStats.hasErrors() || !failingSuites.isEmpty()) {
runStatus = Status.ERROR; runStatus = Status.ERROR;
} else { } else if (!getSuites(Status.PENDING).isEmpty()) {
if (logStats.hasWarnings()) {
runStatus = Status.WARN;
} else {
if (!pendingSuites.isEmpty()) {
runStatus = Status.PENDING; runStatus = Status.PENDING;
} else { } else {
runStatus = Status.OK; runStatus = Status.OK;
} }
} }
}
}
/**
* Categorize all test results according to status.
*/
private void tallyTestResults(SuiteResults sResult) {
for (TestResults tResult : sResult.getTests()) {
allTests.add(tResult);
}
}
/**
* Populate {@link #allTests} with the tests for which we have no results.
*/
private void tallyTestContents(SuiteData suiteData) {
Status suiteStatus = suiteData.getSuiteStatus();
for (String testName : suiteData.getContents().getTestNames()) {
TestResults t = new TestResults(testName, suiteData.getName(), "",
suiteStatus, "");
allTests.add(t);
}
}
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// Access the derived data. // Access the derived data.
@ -248,19 +184,19 @@ public class DataModel {
} }
public boolean isAnyPasses() { public boolean isAnyPasses() {
return !(passingSuites.isEmpty() && passingTests.isEmpty()); return !getTests(Status.OK).isEmpty();
} }
public boolean isAnyFailures() { public boolean isAnyFailures() {
return !(failingSuites.isEmpty() && failingTests.isEmpty()); return !getTests(Status.ERROR).isEmpty();
} }
public boolean isAnyIgnores() { public boolean isAnyIgnores() {
return !(ignoredSuites.isEmpty() && ignoredTests.isEmpty()); return !getTests(Status.IGNORED).isEmpty();
} }
public boolean isAnyPending() { public boolean isAnyPending() {
return !pendingSuites.isEmpty(); return !getTests(Status.PENDING).isEmpty();
} }
public int getTotalSuiteCount() { public int getTotalSuiteCount() {
@ -268,23 +204,33 @@ public class DataModel {
} }
public int getPassingSuiteCount() { public int getPassingSuiteCount() {
return passingSuites.size(); return getSuites(Status.OK).size();
} }
public int getFailingSuiteCount() { public int getFailingSuiteCount() {
return failingSuites.size(); return getSuites(Status.ERROR).size();
} }
public int getIgnoredSuiteCount() { public int getIgnoredSuiteCount() {
return ignoredSuites.size(); return getSuites(Status.IGNORED).size();
} }
public int getPendingSuitesCount() { public int getPendingSuiteCount() {
return pendingSuites.size(); return getSuites(Status.PENDING).size();
} }
public Collection<SuiteResults> getSuiteResults() { public Collection<SuiteData> getAllSuites() {
return Collections.unmodifiableCollection(suiteResults); return suiteDataMap.values();
}
public Map<String, SuiteData> getSuitesWithFailureMessages() {
Map<String, SuiteData> map = new TreeMap<String, SuiteData>();
for (SuiteData s : suiteDataMap.values()) {
if (s.getFailureMessages() != null) {
map.put(s.getName(), s);
}
}
return map;
} }
public int getTotalTestCount() { public int getTotalTestCount() {
@ -292,57 +238,57 @@ public class DataModel {
} }
public int getPassingTestCount() { public int getPassingTestCount() {
return passingTests.size(); return getTests(Status.OK).size();
} }
public int getFailingTestCount() { public int getFailingTestCount() {
return failingTests.size(); return getTests(Status.ERROR).size();
} }
public int getIgnoredTestCount() { public int getIgnoredTestCount() {
return ignoredTests.size(); return getTests(Status.IGNORED).size();
} }
public int getPendingTestsCount() { public int getPendingTestCount() {
return pendingTests.size(); return getTests(Status.PENDING).size();
} }
public Collection<TestResults> getAllTests() { public Collection<TestData> getAllTests() {
return Collections.unmodifiableCollection(allTests); return Collections.unmodifiableCollection(allTests);
} }
public Collection<TestResults> getFailingTests() { public Collection<TestData> getFailingTests() {
return Collections.unmodifiableCollection(failingTests); return Collections.unmodifiableCollection(getTests(Status.ERROR));
} }
public Collection<TestResults> getIgnoredTests() { public Collection<TestData> getIgnoredTests() {
return Collections.unmodifiableCollection(ignoredTests); return Collections.unmodifiableCollection(getTests(Status.IGNORED));
} }
public Collection<IgnoredTestInfo> getIgnoredTestInfo() { public Collection<IgnoredTestInfo> getIgnoredTestInfo() {
return ignoredTestList.getList(); return ignoredTestList.getList();
} }
public String getOutputLink(String suiteName, String testName) {
SuiteData sd = suiteDataMap.get(suiteName);
if (sd != null) {
SuiteResults s = sd.getResults();
if (s != null) {
if (testName.equals("*")) {
return s.getOutputLink();
} else {
TestResults t = s.getTest(testName);
if (t != null) {
return t.getOutputLink();
}
}
}
}
return "";
}
public String getReasonForIgnoring(String suiteName, String testName) { public String getReasonForIgnoring(String suiteName, String testName) {
return ignoredTestList.getReasonForIgnoring(suiteName, testName); return ignoredTestList.getReasonForIgnoring(suiteName, testName);
} }
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
/**
* Get the list of suites that have this status.
*/
private List<SuiteData> getSuites(Status st) {
return suiteMapByStatus.get(st);
}
/**
* Get the list of tests that have this status.
*/
private List<TestData> getTests(Status st) {
return testMapByStatus.get(st);
}
} }

View file

@ -2,6 +2,9 @@
package edu.cornell.mannlib.vitro.utilities.testrunner.datamodel; package edu.cornell.mannlib.vitro.utilities.testrunner.datamodel;
import java.util.LinkedHashMap;
import java.util.Map;
import edu.cornell.mannlib.vitro.utilities.testrunner.Status; import edu.cornell.mannlib.vitro.utilities.testrunner.Status;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener.ProcessOutput; import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener.ProcessOutput;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults; import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults;
@ -11,56 +14,168 @@ import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults.TestRe
* What do we know about this suite, both before it runs and after it has run? * What do we know about this suite, both before it runs and after it has run?
*/ */
public class SuiteData { public class SuiteData {
/**
* If this suite has failure messages, the output link is to an anchor on
* the same page.
*/
public static String failureMessageAnchor(SuiteData s) {
return "suiteFailure_" + s.getName();
}
private final String name; private final String name;
private final boolean ignored; private final Status status;
private final SuiteContents contents; private final String outputLink;
private final SuiteResults results;
private final ProcessOutput failureMessages; private final ProcessOutput failureMessages;
/**
* This map iterates according to the order that the tests were specified in
* the suite file.
*/
private final Map<String, TestData> testMap;
public SuiteData(String name, boolean ignored, SuiteContents contents, public SuiteData(String name, boolean ignored, SuiteContents contents,
SuiteResults results, ProcessOutput failureMessages) { SuiteResults results, ProcessOutput failureMessages) {
this.name = name; this.name = name;
this.ignored = ignored;
this.contents = contents;
this.results = results;
this.failureMessages = failureMessages; this.failureMessages = failureMessages;
if (ignored) {
this.status = Status.IGNORED;
this.outputLink = null;
this.testMap = buildTestMap(contents, results);
} else if (failureMessages != null) {
this.status = Status.ERROR;
this.outputLink = "#" + failureMessageAnchor(this);
this.testMap = buildTestMap(contents, results);
} else if (results != null) {
this.testMap = buildTestMap(contents, results);
this.status = buildStatusFromTestMap();
this.outputLink = results.getOutputLink();
} else {
this.status = Status.PENDING;
this.outputLink = null;
this.testMap = buildTestMap(contents, results);
}
}
/**
* Build the test map. Do we have test results, or only the advance list of
* tests?
*/
private Map<String, TestData> buildTestMap(SuiteContents contents,
SuiteResults results) {
if (results == null) {
return buildTestMapFromContents(contents);
} else {
return buildTestMapFromResults(contents, results);
}
}
/**
* All we have to build from is the contents of the Suite HTML file.
*/
private Map<String, TestData> buildTestMapFromContents(
SuiteContents contents) {
Map<String, TestData> map = new LinkedHashMap<String, TestData>();
for (String testName : contents.getTestNames()) {
map.put(testName, new TestData(testName, this.name, this.status,
null));
}
return map;
}
/**
* We can build from both the contents of the Suite HTML file and from the
* test results output file.
*/
private Map<String, TestData> buildTestMapFromResults(
SuiteContents contents, SuiteResults results) {
Map<String, TestData> map = new LinkedHashMap<String, TestData>();
for (String testName : contents.getTestNames()) {
TestResults testResult = results.getTest(testName);
if (testResult == null) {
// This shouldn't happen. How do we show it?
map.put(testName, new TestData(testName, this.name,
Status.PENDING, null));
} else {
map.put(testName,
new TestData(testName, this.name, testResult
.getStatus(), testResult.getOutputLink()));
}
}
return map;
}
/**
* The suite ran to completion, so its status is the worst of the individual
* test statuses.
*/
private Status buildStatusFromTestMap() {
Status status = Status.OK;
for (TestData t : this.testMap.values()) {
status = Status.combine(status, t.getStatus());
}
return status;
} }
public String getName() { public String getName() {
return name; return name;
} }
public boolean isIgnored() { public Status getStatus() {
return ignored;
}
public SuiteContents getContents() {
return contents;
}
public SuiteResults getResults() {
return results;
}
public Status getSuiteStatus() {
if (ignored) {
return Status.WARN;
}
if (failureMessages != null) {
return Status.ERROR;
}
if (results == null) {
return Status.PENDING;
}
/*
* If we have results and no failure messages, scan the results for the
* worst status.
*/
Status status = Status.OK;
for (TestResults t : results.getTests()) {
status = Status.combine(status, t.getStatus());
}
return status; return status;
} }
public String getOutputLink() {
return outputLink;
}
public ProcessOutput getFailureMessages() {
return failureMessages;
}
public Map<String, TestData> getTestMap() {
return testMap;
}
/**
* What do we know about this test, both before it runs and after it has
* run?
*/
public static class TestData {
private final String testName;
private final String suiteName;
private final Status status;
private final String outputLink;
public TestData(String testName, String suiteName, Status status,
String outputLink) {
this.testName = testName;
this.suiteName = suiteName;
this.status = status;
this.outputLink = outputLink;
}
public String getTestName() {
return testName;
}
public String getSuiteName() {
return suiteName;
}
public Status getStatus() {
return status;
}
public String getOutputLink() {
return outputLink;
}
@Override
public String toString() {
return "TestData[testName=" + testName + ", suiteName=" + suiteName
+ ", status=" + status + ", outputLink=" + outputLink + "]";
}
}
} }

View file

@ -9,6 +9,8 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper; import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper;
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener; import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
@ -138,6 +140,7 @@ public class OutputDataListener implements Listener {
* interesting if it indicates a suite failure. * interesting if it indicates a suite failure.
*/ */
public static class ProcessOutput { public static class ProcessOutput {
private static final String SUITE_FAILURE_PATTERN = "exception|error(?i)";
private final String suiteName; private final String suiteName;
private final StringBuilder stdout = new StringBuilder(); private final StringBuilder stdout = new StringBuilder();
private final StringBuilder errout = new StringBuilder(); private final StringBuilder errout = new StringBuilder();
@ -167,7 +170,9 @@ public class OutputDataListener implements Listener {
} }
public boolean isSuiteFailure() { public boolean isSuiteFailure() {
return errout.length() > 0; Pattern p = Pattern.compile(SUITE_FAILURE_PATTERN);
Matcher m = p.matcher(errout);
return m.find();
} }
} }

View file

@ -11,6 +11,7 @@ import java.io.Reader;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.Map;
import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper; import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper;
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo; import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo;
@ -18,7 +19,9 @@ import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats;
import edu.cornell.mannlib.vitro.utilities.testrunner.SeleniumRunnerParameters; import edu.cornell.mannlib.vitro.utilities.testrunner.SeleniumRunnerParameters;
import edu.cornell.mannlib.vitro.utilities.testrunner.Status; import edu.cornell.mannlib.vitro.utilities.testrunner.Status;
import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.DataModel; import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.DataModel;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults.TestResults; import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData;
import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData.TestData;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener.ProcessOutput;
/** /**
* Creates the summary HTML file. * Creates the summary HTML file.
@ -60,6 +63,7 @@ public class OutputSummaryFormatter {
writeIgnoreSection(writer); writeIgnoreSection(writer);
writeSuitesSection(writer); writeSuitesSection(writer);
writeAllTestsSection(writer); writeAllTestsSection(writer);
writeSuiteErrorMessagesSection(writer);
writeFooter(writer); writeFooter(writer);
} catch (IOException e) { } catch (IOException e) {
// There is no appeal for any problems here. Just report them. // There is no appeal for any problems here. Just report them.
@ -92,207 +96,234 @@ public class OutputSummaryFormatter {
} }
} }
private void writeHeader(PrintWriter writer) { private void writeHeader(PrintWriter w) {
Status runStatus = dataModel.getRunStatus(); Status runStatus = dataModel.getRunStatus();
String statusString = (runStatus == Status.PENDING) ? "IN PROGRESS" String statusString = (runStatus == Status.PENDING) ? "IN PROGRESS"
: runStatus.toString(); : runStatus.toString();
String startString = formatDateTime(dataModel.getStartTime()); String startString = formatDateTime(dataModel.getStartTime());
writer.println("<html>"); w.println("<html>");
writer.println("<head>"); w.println("<head>");
writer.println(" <title>Summary of Acceptance Tests " + startString w.println(" <title>Summary of Acceptance Tests " + startString
+ "</title>"); + "</title>");
writer.println(" <link rel=\"stylesheet\" type=\"text/css\" " w.println(" <link rel=\"stylesheet\" type=\"text/css\" "
+ "href=\"summary.css\">"); + "href=\"summary.css\">");
writer.println("</head>"); w.println("</head>");
writer.println("<body>"); w.println("<body>");
writer.println(); w.println();
writer.println(" <div class=\"heading\">"); w.println(" <div class=\"heading\">");
writer.println(" Acceptance test results: " + startString); w.println(" Acceptance test results: " + startString);
writer.println(" <div class=\"" + runStatus.getHtmlClass() w.println(" <div class=\"" + runStatus.getHtmlClass()
+ " one-word\">" + statusString + "</div>"); + " one-word\">" + statusString + "</div>");
writer.println(" </div>"); w.println(" </div>");
} }
private void writeStatsSection(PrintWriter writer) { private void writeStatsSection(PrintWriter w) {
String passClass = dataModel.isAnyPasses() ? Status.OK.getHtmlClass() String passClass = dataModel.isAnyPasses() ? Status.OK.getHtmlClass()
: ""; : "";
String failClass = dataModel.isAnyFailures() ? Status.ERROR String failClass = dataModel.isAnyFailures() ? Status.ERROR
.getHtmlClass() : ""; .getHtmlClass() : "";
String ignoreClass = dataModel.isAnyIgnores() ? Status.WARN String ignoreClass = dataModel.isAnyIgnores() ? Status.IGNORED
.getHtmlClass() : ""; .getHtmlClass() : "";
String start = formatDateTime(dataModel.getStartTime()); String start = formatDateTime(dataModel.getStartTime());
String end = formatDateTime(dataModel.getEndTime()); String end = formatDateTime(dataModel.getEndTime());
String elapsed = formatElapsedTime(dataModel.getElapsedTime()); String elapsed = formatElapsedTime(dataModel.getElapsedTime());
writer.println(" <div class=\"section\">Summary</div>"); w.println(" <div class=\"section\">Summary</div>");
writer.println(); w.println();
writer.println(" <table class=\"summary\" cellspacing=\"0\">"); w.println(" <table class=\"summary\" cellspacing=\"0\">");
writer.println(" <tr>"); w.println(" <tr>");
writer.println(" <td>"); w.println(" <td>");
writer.println(" <table cellspacing=\"0\">"); w.println(" <table cellspacing=\"0\">");
writer.println(" <tr><td>Start time:</td><td>" + start w.println(" <tr><td>Start time:</td><td>" + start
+ "</td></tr>"); + "</td></tr>");
writer.println(" <tr><td>End time:</td><td>" + end w.println(" <tr><td>End time:</td><td>" + end + "</td></tr>");
w.println(" <tr><td>Elapsed time</td><td>" + elapsed
+ "</td></tr>"); + "</td></tr>");
writer.println(" <tr><td>Elapsed time</td><td>" + elapsed w.println(" </table>");
+ "</td></tr>"); w.println(" </td>");
writer.println(" </table>"); w.println(" <td>");
writer.println(" </td>"); w.println(" <table class=\"tallys\" cellspacing=\"0\">");
writer.println(" <td>"); w.println(" <tr><th>&nbsp;</th><th>Suites</th><th>Tests</th>");
writer.println(" <table class=\"tallys\" cellspacing=\"0\">"); w.println(" <tr class=\"" + passClass
writer.println(" <tr><th>&nbsp;</th><th>Suites</th><th>Tests</th>");
writer.println(" <tr class=\"" + passClass
+ "\"><td>Passed</td><td>" + dataModel.getPassingSuiteCount() + "\"><td>Passed</td><td>" + dataModel.getPassingSuiteCount()
+ "</td><td>" + dataModel.getPassingTestCount() + "</td>"); + "</td><td>" + dataModel.getPassingTestCount() + "</td>");
writer.println(" <tr class=\"" + failClass w.println(" <tr class=\"" + failClass
+ "\"><td>Failed</td><td>" + dataModel.getFailingSuiteCount() + "\"><td>Failed</td><td>" + dataModel.getFailingSuiteCount()
+ "</td><td>" + dataModel.getFailingTestCount() + "</td>"); + "</td><td>" + dataModel.getFailingTestCount() + "</td>");
writer.println(" <tr class=\"" + ignoreClass w.println(" <tr class=\"" + ignoreClass
+ "\"><td>Ignored</td><td>" + dataModel.getIgnoredSuiteCount() + "\"><td>Ignored</td><td>" + dataModel.getIgnoredSuiteCount()
+ "</td><td>" + dataModel.getIgnoredTestCount() + "</td>"); + "</td><td>" + dataModel.getIgnoredTestCount() + "</td>");
if (dataModel.isAnyPending()) { if (dataModel.isAnyPending()) {
writer.println(" <tr><td>Pending</td><td>" w.println(" <tr class=\"" + Status.PENDING.getHtmlClass()
+ dataModel.getPendingSuitesCount() + "</td><td>" + "\"><td>Pending</td><td>"
+ dataModel.getPendingTestsCount() + "</td>"); + dataModel.getPendingSuiteCount() + "</td><td>"
+ dataModel.getPendingTestCount() + "</td>");
} }
writer.println(" <tr><td class=\"total\">Total</td><td>" w.println(" <tr><td class=\"total\">Total</td><td>"
+ dataModel.getTotalSuiteCount() + "</td><td>" + dataModel.getTotalSuiteCount() + "</td><td>"
+ dataModel.getTotalTestCount() + "</td>"); + dataModel.getTotalTestCount() + "</td>");
writer.println(" </table>"); w.println(" </table>");
writer.println(" </td>"); w.println(" </td>");
writer.println(" </tr>"); w.println(" </tr>");
writer.println(" </table>"); w.println(" </table>");
writer.println(); w.println();
} }
private void writeErrorMessagesSection(PrintWriter writer) { private void writeErrorMessagesSection(PrintWriter w) {
String errorClass = Status.ERROR.getHtmlClass(); String errorClass = Status.ERROR.getHtmlClass();
String warnClass = Status.WARN.getHtmlClass();
writer.println(" <div class=section>Errors and warnings</div>"); w.println(" <div class=section>Errors and warnings</div>");
writer.println(); w.println();
writer.println(" <table cellspacing=\"0\">"); w.println(" <table cellspacing=\"0\">");
if ((!logStats.hasErrors()) && (!logStats.hasWarnings())) { if ((!logStats.hasErrors()) && (!logStats.hasWarnings())) {
writer.println(" <tr><td colspan=\"2\">No errors or warnings</td></tr>"); w.println(" <tr><td colspan=\"2\">No errors or warnings</td></tr>");
} else { } else {
for (String e : logStats.getErrors()) { for (String e : logStats.getErrors()) {
writer.println(" <tr class=\"" + errorClass w.println(" <tr class=\"" + errorClass
+ "\"><td>ERROR</td><td>" + e + "</td></tr>"); + "\"><td>ERROR</td><td>" + e + "</td></tr>");
} }
for (String w : logStats.getWarnings()) {
writer.println(" <tr class=\"" + warnClass
+ "\"><td>ERROR</td><td>" + w + "</td></tr>");
} }
} w.println(" </table>");
writer.println(" </table>"); w.println();
writer.println();
} }
private void writeFailureSection(PrintWriter writer) { private void writeFailureSection(PrintWriter w) {
String errorClass = Status.ERROR.getHtmlClass(); String errorClass = Status.ERROR.getHtmlClass();
Collection<TestResults> failingTests = dataModel.getFailingTests(); Collection<TestData> failingTests = dataModel.getFailingTests();
writer.println(" <div class=section>Failing tests</div>"); w.println(" <div class=section>Failures</div>");
writer.println(); w.println();
writer.println(" <table cellspacing=\"0\">"); w.println(" <table cellspacing=\"0\">");
writer.println(" <tr><th>Suite name</th><th>Test name</th></tr>\n"); w.println(" <tr><th>Suite name</th><th>Test name</th></tr>\n");
if (failingTests.isEmpty()) { if (failingTests.isEmpty()) {
writer.println(" <tr><td colspan=\"2\">No tests failed.</td>" w.println(" <tr><td colspan=\"2\">No tests failed.</td>"
+ "</tr>"); + "</tr>");
} else { } else {
for (TestResults t : failingTests) { Map<String, SuiteData> failedSuiteMap = dataModel
writer.println(" <tr class=\"" + errorClass + "\">"); .getSuitesWithFailureMessages();
writer.println(" <td>" + t.getSuiteName() + "</td>"); for (SuiteData s : failedSuiteMap.values()) {
writer.println(" <td><a href=\"" + t.getOutputLink() w.println(" <tr class=\"" + errorClass + "\">");
+ "\">" + t.getTestName() + "</a></td>"); w.println(" <td>" + s.getName() + "</td>");
writer.println(" </tr>"); w.println(" <td>" + outputLink(s) + "</td>");
w.println(" </tr>");
}
for (TestData t : failingTests) {
if (!failedSuiteMap.containsKey(t.getSuiteName())) {
w.println(" <tr class=\"" + errorClass + "\">");
w.println(" <td>" + t.getSuiteName() + "</td>");
w.println(" <td>" + outputLink(t) + "</td>");
w.println(" </tr>");
} }
} }
writer.println(" </table>"); }
writer.println(); w.println(" </table>");
w.println();
} }
private void writeIgnoreSection(PrintWriter writer) { private void writeIgnoreSection(PrintWriter w) {
String warnClass = Status.WARN.getHtmlClass(); String warnClass = Status.IGNORED.getHtmlClass();
Collection<IgnoredTestInfo> ignoredTests = dataModel Collection<IgnoredTestInfo> ignoredTests = dataModel
.getIgnoredTestInfo(); .getIgnoredTestInfo();
writer.println(" <div class=section>Ignored tests</div>"); w.println(" <div class=section>Ignored</div>");
writer.println(); w.println();
writer.println(" <table cellspacing=\"0\">"); w.println(" <table cellspacing=\"0\">");
writer.println(" <tr><th>Suite name</th><th>Test name</th>" w.println(" <tr><th>Suite name</th><th>Test name</th>"
+ "<th>Reason for ignoring</th></tr>\n"); + "<th>Reason for ignoring</th></tr>\n");
if (ignoredTests.isEmpty()) { if (ignoredTests.isEmpty()) {
writer.println(" <tr><td colspan=\"3\">No tests ignored.</td>" w.println(" <tr><td colspan=\"3\">No tests ignored.</td>"
+ "</tr>"); + "</tr>");
} else { } else {
for (IgnoredTestInfo info : ignoredTests) { for (IgnoredTestInfo info : ignoredTests) {
String suiteName = info.suiteName; String suiteName = info.suiteName;
String testName = info.testName; String testName = info.testName;
String link = dataModel.getOutputLink(suiteName, testName);
String reason = dataModel.getReasonForIgnoring(suiteName, String reason = dataModel.getReasonForIgnoring(suiteName,
testName); testName);
writer.println(" <tr class=\"" + warnClass + "\">"); w.println(" <tr class=\"" + warnClass + "\">");
writer.println(" <td>" + suiteName + "</td>"); w.println(" <td>" + suiteName + "</td>");
if (link.isEmpty()) { w.println(" <td>" + testName + "</td>");
writer.println(" <td>" + testName + "</td>"); w.println(" <td>" + reason + "</td>");
} else { w.println(" </tr>");
writer.println(" <td><a href=\"" + link + "\">"
+ testName + "</a></td>");
}
writer.println(" <td>" + reason + "</td>");
writer.println(" </tr>");
} }
} }
writer.println(" </table>"); w.println(" </table>");
writer.println(); w.println();
} }
private void writeSuitesSection(PrintWriter writer) { private void writeSuitesSection(PrintWriter w) {
writer.println(" <div class=section>Suites</div>"); w.println(" <div class=section>Suites Summary</div>");
writer.println(); w.println();
writer.println(" <table cellspacing=\"0\">"); w.println(" <table cellspacing=\"0\">");
for (SuiteResults s : dataModel.getSuiteResults()) { for (SuiteData s : dataModel.getAllSuites()) {
writer.println(" <tr class=\"" + s.getStatus().getHtmlClass() w.println(" <tr class=\"" + s.getStatus().getHtmlClass() + "\">");
w.println(" <td>" + outputLink(s) + "</td>");
w.println(" <td>" + s.getStatus() + "</td>");
w.println(" </tr>");
}
w.println(" </table>");
w.println();
}
private void writeAllTestsSection(PrintWriter w) {
Collection<TestData> allTests = dataModel.getAllTests();
w.println(" <div class=section>All tests</div>");
w.println();
w.println(" <table cellspacing=\"0\">");
w.println(" <tr><th>Suite name</th><th>Test name</th><th>&nbsp;</th></tr>\n");
for (TestData t : allTests) {
w.println(" <tr class=\"" + t.getStatus().getHtmlClass() + "\">");
w.println(" <td>" + t.getSuiteName() + "</td>");
w.println(" <td>" + outputLink(t) + "</td>");
w.println(" <td>" + t.getStatus() + "</td>");
w.println(" </tr>");
}
w.println(" </table>");
w.println();
}
private void writeSuiteErrorMessagesSection(PrintWriter w) {
Map<String, SuiteData> failedSuiteMap = dataModel
.getSuitesWithFailureMessages();
if (failedSuiteMap.isEmpty()) {
return;
}
w.println(" <div class=section>All tests</div>");
w.println();
for (SuiteData s : failedSuiteMap.values()) {
ProcessOutput output = s.getFailureMessages();
w.println(" <a name=\"" + SuiteData.failureMessageAnchor(s)
+ "\">"); + "\">");
writer.println(" <td><a href=\"" + s.getOutputLink() + "\">" w.println(" <table cellspacing=\"0\">");
+ s.getName() + "</a></td>"); w.println(" <tr><th>Standard Output</th></tr>\n");
writer.println(" </tr>"); w.println(" <tr><td><pre>" + output.getStdout()
+ "</pre></td></tr>\n");
w.println(" </table>");
w.println("<br/>&nbsp;<br/>");
w.println(" <table cellspacing=\"0\">");
w.println(" <tr><th>Error Output</th></tr>\n");
w.println(" <tr><td><pre>" + output.getErrout()
+ "</pre></td></tr>\n");
w.println(" </table>");
w.println("<br/>&nbsp;<br/>");
w.println();
}
} }
writer.println(" </table>"); private void writeFooter(PrintWriter w) {
writer.println(); w.println(" <div class=section>Log</div>");
} w.println(" <pre>");
private void writeAllTestsSection(PrintWriter writer) {
Collection<TestResults> allTests = dataModel.getAllTests();
writer.println(" <div class=section>All tests</div>");
writer.println();
writer.println(" <table cellspacing=\"0\">");
writer.println(" <tr><th>Suite name</th><th>Test name</th></tr>\n");
for (TestResults t : allTests) {
writer.println(" <tr class=\"" + t.getStatus().getHtmlClass()
+ "\">");
writer.println(" <td>" + t.getSuiteName() + "</td>");
writer.println(" <td><a href=\"" + t.getOutputLink() + "\">"
+ t.getTestName() + "</a></td>");
writer.println(" </tr>");
}
writer.println(" </table>");
writer.println();
}
private void writeFooter(PrintWriter writer) {
writer.println(" <div class=section>Log</div>");
writer.println(" <pre>");
Reader reader = null; Reader reader = null;
try { try {
@ -300,7 +331,7 @@ public class OutputSummaryFormatter {
char[] buffer = new char[4096]; char[] buffer = new char[4096];
int howMany; int howMany;
while (-1 != (howMany = reader.read(buffer))) { while (-1 != (howMany = reader.read(buffer))) {
writer.write(buffer, 0, howMany); w.write(buffer, 0, howMany);
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@ -314,9 +345,9 @@ public class OutputSummaryFormatter {
} }
} }
writer.println(" </pre>"); w.println(" </pre>");
writer.println("</body>"); w.println("</body>");
writer.println("</html>"); w.println("</html>");
} }
private String formatElapsedTime(long elapsed) { private String formatElapsedTime(long elapsed) {
@ -350,4 +381,21 @@ public class OutputSummaryFormatter {
return dateFormat.format(new Date(dateTime)); return dateFormat.format(new Date(dateTime));
} }
private String outputLink(SuiteData s) {
if (s.getOutputLink() == null) {
return s.getName();
} else {
return "<a href=\"" + s.getOutputLink() + "\">" + s.getName()
+ "</a>";
}
}
private String outputLink(TestData t) {
if (t.getOutputLink() == null) {
return t.getTestName();
} else {
return "<a href=\"" + t.getOutputLink() + "\">" + t.getTestName()
+ "</a>";
}
}
} }

View file

@ -76,21 +76,16 @@ public class SuiteResults {
String testLink = outputLink + m.group(2); String testLink = outputLink + m.group(2);
Status testStatus; Status testStatus;
String reasonForIgnoring;
if ("status_passed".equals(m.group(1))) { if ("status_passed".equals(m.group(1))) {
testStatus = Status.OK; testStatus = Status.OK;
reasonForIgnoring = "";
} else if (ignoredTests.isIgnored(suiteName, testName)) { } else if (ignoredTests.isIgnored(suiteName, testName)) {
testStatus = Status.WARN; testStatus = Status.IGNORED;
reasonForIgnoring = ignoredTests.getReasonForIgnoring(
suiteName, testName);
} else { } else {
testStatus = Status.ERROR; testStatus = Status.ERROR;
reasonForIgnoring = "";
} }
tests.add(new TestResults(testName, suiteName, testLink, tests.add(new TestResults(testName, suiteName, testLink,
testStatus, reasonForIgnoring)); testStatus));
} }
} }
@ -163,15 +158,13 @@ public class SuiteResults {
private final String suite; private final String suite;
private final String outputLink; private final String outputLink;
private final Status status; private final Status status;
private final String reasonForIgnoring;
public TestResults(String name, String suite, String outputLink, public TestResults(String name, String suite, String outputLink,
Status status, String reasonForIgnoring) { Status status) {
this.name = name; this.name = name;
this.suite = suite; this.suite = suite;
this.outputLink = outputLink; this.outputLink = outputLink;
this.status = status; this.status = status;
this.reasonForIgnoring = reasonForIgnoring;
} }
public Status getStatus() { public Status getStatus() {
@ -190,9 +183,6 @@ public class SuiteResults {
return outputLink; return outputLink;
} }
public String getReasonForIgnoring() {
return reasonForIgnoring;
}
} }
} }

View file

@ -67,6 +67,10 @@ table.tallys td.total {
background: rgb(100%, 100%, 60%); background: rgb(100%, 100%, 60%);
} }
.pending {
background: rgb(90%, 90%, 100%);
}
.one-word { .one-word {
width: 20%; width: 20%;
text-align: center; text-align: center;