NIHVIVO-222 Create the output summary HTML before and after each test suite is run. Include information on pending suites.

This commit is contained in:
jeb228 2010-08-19 20:09:24 +00:00
parent f09fcee448
commit a05c62e30a
10 changed files with 457 additions and 154 deletions

View file

@ -17,6 +17,8 @@ import java.util.regex.Pattern;
* Extract any summary information from the log file. * Extract any summary information from the log file.
*/ */
public class LogStats { public class LogStats {
public static LogStats EMPTY_LOG_STATS = new LogStats();
private static final Pattern SUITE_NAME_PATTERN = Pattern private static final Pattern SUITE_NAME_PATTERN = Pattern
.compile("Running suite (.*)"); .compile("Running suite (.*)");
private static final Pattern ERROR_PATTERN = Pattern private static final Pattern ERROR_PATTERN = Pattern
@ -35,6 +37,10 @@ public class LogStats {
private final List<String> errors = new ArrayList<String>(); private final List<String> errors = new ArrayList<String>();
private final List<String> warnings = new ArrayList<String>(); private final List<String> warnings = new ArrayList<String>();
private LogStats() {
// Nothing to initialize for empty instance.
}
private LogStats(File logFile) { private LogStats(File logFile) {
BufferedReader reader = null; BufferedReader reader = null;

View file

@ -9,6 +9,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.DataModel;
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener; import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputManager; import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputManager;
@ -24,8 +25,7 @@ public class SeleniumRunner {
private final ModelCleaner modelCleaner; private final ModelCleaner modelCleaner;
private final SuiteRunner suiteRunner; private final SuiteRunner suiteRunner;
private final OutputManager outputManager; private final OutputManager outputManager;
private final DataModel dataModel;
private final List<File> selectedSuites = new ArrayList<File>();
public SeleniumRunner(SeleniumRunnerParameters parms) { public SeleniumRunner(SeleniumRunnerParameters parms) {
this.parms = parms; this.parms = parms;
@ -35,6 +35,7 @@ public class SeleniumRunner {
this.modelCleaner = new ModelCleaner(parms, this.tomcatController); this.modelCleaner = new ModelCleaner(parms, this.tomcatController);
this.suiteRunner = new SuiteRunner(parms); this.suiteRunner = new SuiteRunner(parms);
this.outputManager = new OutputManager(parms); this.outputManager = new OutputManager(parms);
this.dataModel = new DataModel();
} }
@ -45,7 +46,7 @@ public class SeleniumRunner {
Listener listener = parms.getListener(); Listener listener = parms.getListener();
IgnoredTests ignored = parms.getIgnoredTests(); IgnoredTests ignored = parms.getIgnoredTests();
this.selectedSuites.clear(); List<File> suites = new ArrayList<File>();
for (File parentDir : parms.getSuiteParentDirectories()) { for (File parentDir : parms.getSuiteParentDirectories()) {
for (File suite : parms.findSuiteDirs(parentDir)) { for (File suite : parms.findSuiteDirs(parentDir)) {
@ -54,10 +55,12 @@ public class SeleniumRunner {
listener.suiteIgnored(suite); listener.suiteIgnored(suite);
} else { } else {
listener.suiteAdded(suite); listener.suiteAdded(suite);
this.selectedSuites.add(suite); suites.add(suite);
} }
} }
} }
dataModel.setSelectedSuites(suites);
} }
/** /**
@ -76,8 +79,8 @@ public class SeleniumRunner {
tomcatController.cleanup(); tomcatController.cleanup();
listener.runEndTime(); listener.runEndTime();
Status status = outputManager.summarizeOutput(); outputManager.summarizeOutput(dataModel);
success = (status == Status.OK); success = (dataModel.getRunStatus() == Status.OK);
} catch (IOException e) { } catch (IOException e) {
listener.runFailed(e); listener.runFailed(e);
success = false; success = false;
@ -92,7 +95,8 @@ public class SeleniumRunner {
} }
public void runSelectedSuites() { public void runSelectedSuites() {
for (File suiteDir : this.selectedSuites) { for (File suiteDir : dataModel.getSelectedSuites()) {
outputManager.summarizeOutput(dataModel);
listener.suiteStarted(suiteDir); listener.suiteStarted(suiteDir);
try { try {
if (parms.isCleanModel()) { if (parms.isCleanModel()) {

View file

@ -9,6 +9,11 @@ public enum Status {
/** All tests passed, and there were no warnings or errors. */ /** All tests passed, and there were no warnings or errors. */
OK("good"), OK("good"),
/**
* One or more tests have not been run yet.
*/
PENDING(""),
/** /**
* Any test failure was ignored, and any messages were no worse than * Any test failure was ignored, and any messages were no worse than
* warnings. * warnings.
@ -30,4 +35,13 @@ public enum Status {
public String getHtmlClass() { public String getHtmlClass() {
return this.htmlClass; return this.htmlClass;
} }
/** When combined, the more severe status (defined later) takes precedence. */
public static Status combine(Status s1, Status s2) {
if (s1.compareTo(s2) > 0) {
return s1;
} else {
return s2;
}
}
} }

View file

@ -0,0 +1,273 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner.datamodel;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats;
import edu.cornell.mannlib.vitro.utilities.testrunner.Status;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener;
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.
*/
public class DataModel {
/* base data */
private Collection<File> selectedSuites = Collections.emptyList();
private Collection<SuiteResults> suiteResults = Collections.emptyList();
private OutputDataListener.Info dataListenerInfo = OutputDataListener.Info.EMPTY_INFO;
private LogStats logStats = LogStats.EMPTY_LOG_STATS; // TODO
/* derived data */
private Status runStatus = Status.PENDING;
private final List<SuiteData> allSuiteData = new ArrayList<SuiteData>();
private final List<SuiteData> pendingSuites = new ArrayList<SuiteData>();
private final List<SuiteData> passingSuites = new ArrayList<SuiteData>();
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<TestResults> pendingTests = new ArrayList<TestResults>();
private final List<TestResults> passingTests = new ArrayList<TestResults>();
private final List<TestResults> failingTests = new ArrayList<TestResults>();
private final List<TestResults> ignoredTests = new ArrayList<TestResults>();
// ----------------------------------------------------------------------
// Constructor
// ----------------------------------------------------------------------
public DataModel() {
calculate();
}
// ----------------------------------------------------------------------
// Update the base data.
// ----------------------------------------------------------------------
public void setSelectedSuites(Collection<File> selectedSuites) {
this.selectedSuites = new ArrayList<File>(selectedSuites);
calculate();
}
public Collection<File> getSelectedSuites() {
return new ArrayList<File>(selectedSuites);
}
public void setSuiteResults(Collection<SuiteResults> suiteResults) {
this.suiteResults = new ArrayList<SuiteResults>(suiteResults);
calculate();
}
public void captureDataListener(OutputDataListener dataListener) {
this.dataListenerInfo = dataListener.getInfo();
calculate();
}
public void setLogStats(LogStats logStats) { // TODO
this.logStats = logStats;
calculate();
}
// ----------------------------------------------------------------------
// Keep the derived data current.
// ----------------------------------------------------------------------
/**
* Data in the model has been updated. Refresh all derived data.
*/
private void calculate() {
// Clear all derived data.
runStatus = Status.OK;
allSuiteData.clear();
ignoredSuites.clear();
pendingSuites.clear();
failingSuites.clear();
passingSuites.clear();
allTests.clear();
ignoredTests.clear();
pendingTests.clear();
failingTests.clear();
passingTests.clear();
/*
* Suite data.
*/
Map<String, SuiteResults> resultsMap = new HashMap<String, SuiteResults>();
for (SuiteResults result : suiteResults) {
resultsMap.put(result.getName(), result);
}
for (String name : dataListenerInfo.getSuiteNames()) {
if (dataListenerInfo.getIgnoredSuiteNames().contains(name)) {
allSuiteData.add(new SuiteData(name, true, null));
} else if (resultsMap.containsKey(name)) {
allSuiteData.add(new SuiteData(name, false, resultsMap
.get(name)));
} else {
allSuiteData.add(new SuiteData(name, false, null));
}
}
/*
* Tallys of suites and tests.
*/
for (SuiteData sd : allSuiteData) {
SuiteResults result = sd.getResults();
if (result != null) {
tallyTests(result);
}
if (sd.isIgnored()) {
ignoredSuites.add(sd);
} else if (result == null) {
pendingSuites.add(sd);
} else if (result.getStatus() == Status.ERROR) {
failingSuites.add(sd);
} else {
passingSuites.add(sd);
}
}
/*
* Overall status. Warnings in the log are scary, but ignored tests are
* OK.
*/
if (logStats.hasErrors() || !failingSuites.isEmpty()) {
runStatus = Status.ERROR;
} else {
if (logStats.hasWarnings()) {
runStatus = Status.WARN;
} else {
if (!pendingSuites.isEmpty()) {
runStatus = Status.PENDING;
} else {
runStatus = Status.OK;
}
}
}
}
/**
* Categorize all test results according to status.
*/
private void tallyTests(SuiteResults sResult) {
for (TestResults tResult : sResult.getTests()) {
allTests.add(tResult);
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;
}
}
}
// ----------------------------------------------------------------------
// Access the derived data.
// ----------------------------------------------------------------------
public Status getRunStatus() {
return runStatus;
}
public long getStartTime() {
return dataListenerInfo.getStartTime();
}
public long getEndTime() {
return dataListenerInfo.getEndTime();
}
public long getElapsedTime() {
return dataListenerInfo.getElapsedTime();
}
public boolean isAnyPasses() {
return !(passingSuites.isEmpty() && passingTests.isEmpty());
}
public boolean isAnyFailures() {
return !(failingSuites.isEmpty() && failingTests.isEmpty());
}
public boolean isAnyIgnores() {
return !(ignoredSuites.isEmpty() && ignoredTests.isEmpty());
}
public boolean isAnyPending() {
return !pendingSuites.isEmpty();
}
public int getTotalSuiteCount() {
return allSuiteData.size();
}
public int getPassingSuiteCount() {
return passingSuites.size();
}
public int getFailingSuiteCount() {
return failingSuites.size();
}
public int getIgnoredSuiteCount() {
return ignoredSuites.size();
}
public int getPendingSuitesCount() {
return pendingSuites.size();
}
public Collection<SuiteResults> getSuiteResults() {
return Collections.unmodifiableCollection(suiteResults);
}
public int getTotalTestCount() {
return allTests.size();
}
public int getPassingTestCount() {
return passingTests.size();
}
public int getFailingTestCount() {
return failingTests.size();
}
public int getIgnoredTestCount() {
return ignoredTests.size();
}
public Collection<TestResults> getAllTests() {
return Collections.unmodifiableCollection(allTests);
}
public Collection<TestResults> getFailingTests() {
return Collections.unmodifiableCollection(failingTests);
}
public Collection<TestResults> getIgnoredTests() {
return Collections.unmodifiableCollection(ignoredTests);
}
}

View file

@ -0,0 +1,33 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner.datamodel;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults;
/**
* TODO
*/
public class SuiteData {
private final String name;
private final boolean ignored;
private final SuiteResults results;
public SuiteData(String name, boolean ignored, SuiteResults results) {
this.name = name;
this.ignored = ignored;
this.results = results;
}
public String getName() {
return name;
}
public boolean isIgnored() {
return ignored;
}
public SuiteResults getResults() {
return results;
}
}

View file

@ -5,6 +5,7 @@ package edu.cornell.mannlib.vitro.utilities.testrunner.output;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -50,9 +51,40 @@ public class OutputDataListener implements Listener {
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// Accessor methods // A class that holds a snapshot of the data.
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
/**
* A snapshot of the data that the listener has accumulated so far.
*/
public static class Info {
public static Info EMPTY_INFO = new Info();
private final boolean runCompleted;
private final long startTime;
private final long endTime;
private final List<String> suiteNames;
private final List<String> ignoredSuiteNames;
Info() {
this.runCompleted = false;
this.startTime = 0;
this.endTime = 0;
this.suiteNames = Collections.emptyList();
this.ignoredSuiteNames = Collections.emptyList();
}
Info(OutputDataListener parent) {
this.runCompleted = parent.runCompleted;
this.startTime = parent.startTime;
this.endTime = parent.endTime;
this.suiteNames = Collections
.unmodifiableList(new ArrayList<String>(parent.suiteNames));
this.ignoredSuiteNames = Collections
.unmodifiableList(new ArrayList<String>(
parent.ignoredSuiteNames));
}
public boolean isRunCompleted() { public boolean isRunCompleted() {
return runCompleted; return runCompleted;
} }
@ -80,6 +112,14 @@ public class OutputDataListener implements Listener {
public List<String> getIgnoredSuiteNames() { public List<String> getIgnoredSuiteNames() {
return ignoredSuiteNames; return ignoredSuiteNames;
} }
}
/**
* Get a snapshot of the data.
*/
public Info getInfo() {
return new Info(this);
}
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// Listener methods that don't affect the data model // Listener methods that don't affect the data model

View file

@ -5,13 +5,13 @@ package edu.cornell.mannlib.vitro.utilities.testrunner.output;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.ArrayList;
import java.util.Map; import java.util.List;
import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper; import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper;
import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats; 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.datamodel.DataModel;
/** /**
* Manages the contents of the output area. Removes old files prior to a run. * Manages the contents of the output area. Removes old files prior to a run.
@ -63,21 +63,23 @@ public class OutputManager {
* Parse each of the output files from the test suites, and create a unified * Parse each of the output files from the test suites, and create a unified
* output file. * output file.
*/ */
public Status summarizeOutput() { public void summarizeOutput(DataModel dataModel) {
LogStats log = LogStats.parse(parms.getLogFile()); LogStats log = LogStats.parse(parms.getLogFile());
Map<String, SuiteResults> suites = new HashMap<String, SuiteResults>(); List<SuiteResults> suiteResults = new ArrayList<SuiteResults>();
for (File outputFile : parms.getOutputDirectory().listFiles( for (File outputFile : parms.getOutputDirectory().listFiles(
new HtmlFileFilter())) { new HtmlFileFilter())) {
SuiteResults suite = SuiteResults.parse(parms, outputFile); SuiteResults suite = SuiteResults.parse(parms, outputFile);
if (suite != null) { if (suite != null) {
suites.put(suite.getName(), suite); suiteResults.add(suite);
} }
} }
dataModel.setSuiteResults(suiteResults);
dataModel.captureDataListener(dataListener);
OutputSummaryFormatter formatter = new OutputSummaryFormatter(parms); OutputSummaryFormatter formatter = new OutputSummaryFormatter(parms);
formatter.format(log, suites, dataListener); formatter.format(log, dataModel);
return formatter.figureOverallStatus(log, suites);
} }
private static class HtmlFileFilter implements FileFilter { private static class HtmlFileFilter implements FileFilter {

View file

@ -9,15 +9,14 @@ import java.io.InputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.Reader; import java.io.Reader;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List;
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.LogStats; 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.output.SuiteResults.TestResults; import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults.TestResults;
/** /**
@ -31,17 +30,7 @@ public class OutputSummaryFormatter {
private final SeleniumRunnerParameters parms; private final SeleniumRunnerParameters parms;
private LogStats logStats; private LogStats logStats;
private Map<String, SuiteResults> suites; private DataModel dataModel;
private OutputDataListener dataListener;
private Status runStatus;
private List<TestResults> allTests = new ArrayList<TestResults>();
private int passingTestCount;
private List<TestResults> failingTests = new ArrayList<TestResults>();
private List<TestResults> ignoredTests = new ArrayList<TestResults>();
private List<String> passingSuites = new ArrayList<String>();
private List<String> failingSuites = new ArrayList<String>();
private List<String> ignoredSuites = new ArrayList<String>();
private List<String> remainingSuites = new ArrayList<String>();
public OutputSummaryFormatter(SeleniumRunnerParameters parms) { public OutputSummaryFormatter(SeleniumRunnerParameters parms) {
this.parms = parms; this.parms = parms;
@ -51,14 +40,9 @@ public class OutputSummaryFormatter {
* Create a summary HTML file from the info contained in this log file and * Create a summary HTML file from the info contained in this log file and
* these suite outputs. * these suite outputs.
*/ */
public void format(LogStats logStats, Map<String, SuiteResults> suites, public void format(LogStats logStats, DataModel dataModel) {
OutputDataListener dataListener) {
this.logStats = logStats; this.logStats = logStats;
this.suites = suites; this.dataModel = dataModel;
this.dataListener = dataListener;
this.runStatus = figureOverallStatus(logStats, suites);
tallyTests();
tallySuites();
PrintWriter writer = null; PrintWriter writer = null;
try { try {
@ -107,67 +91,9 @@ public class OutputSummaryFormatter {
} }
} }
/**
* The overall status for the run is the worst status of any component.
*/
public Status figureOverallStatus(LogStats log,
Map<String, SuiteResults> suites) {
if (log.hasErrors()) {
return Status.ERROR;
}
boolean hasWarnings = log.hasWarnings();
for (SuiteResults s : suites.values()) {
if (s.getStatus() == Status.ERROR) {
return Status.ERROR;
} else if (s.getStatus() == Status.WARN) {
hasWarnings = true;
}
}
if (hasWarnings) {
return Status.WARN;
} else {
return Status.OK;
}
}
private void tallyTests() {
for (SuiteResults s : suites.values()) {
for (TestResults t : s.getTests()) {
this.allTests.add(t);
if (t.getStatus() == Status.OK) {
this.passingTestCount++;
} else if (t.getStatus() == Status.WARN) {
this.ignoredTests.add(t);
} else {
this.failingTests.add(t);
}
}
}
}
private void tallySuites() {
List<String> ignoredSuiteNames = dataListener.getIgnoredSuiteNames();
for (String name : dataListener.getSuiteNames()) {
if (ignoredSuiteNames.contains(name)) {
this.ignoredSuites.add(name);
} else if (!suites.containsKey(name)) {
this.remainingSuites.add(name);
} else {
SuiteResults s = suites.get(name);
if (s.getStatus() == Status.ERROR) {
this.failingSuites.add(name);
} else {
this.passingSuites.add(name);
}
}
}
}
private void writeHeader(PrintWriter writer) { private void writeHeader(PrintWriter writer) {
String startString = formatDateTime(dataListener.getStartTime()); Status runStatus = dataModel.getRunStatus();
String startString = formatDateTime(dataModel.getStartTime());
writer.println("<html>"); writer.println("<html>");
writer.println("<head>"); writer.println("<head>");
@ -180,21 +106,22 @@ public class OutputSummaryFormatter {
writer.println(); writer.println();
writer.println(" <div class=\"heading\">"); writer.println(" <div class=\"heading\">");
writer.println(" Acceptance test results: " + startString); writer.println(" Acceptance test results: " + startString);
writer.println(" <div class=\"" + this.runStatus.getHtmlClass() writer.println(" <div class=\"" + runStatus.getHtmlClass()
+ " one-word\">" + this.runStatus + "</div>"); + " one-word\">" + runStatus + "</div>");
writer.println(" </div>"); writer.println(" </div>");
} }
private void writeStatsSection(PrintWriter writer) { private void writeStatsSection(PrintWriter writer) {
String passClass = Status.OK.getHtmlClass(); String passClass = dataModel.isAnyPasses() ? Status.OK.getHtmlClass()
String failClass = this.failingTests.isEmpty() ? "" : Status.ERROR : "";
.getHtmlClass(); String failClass = dataModel.isAnyFailures() ? Status.ERROR
String ignoreClass = this.ignoredTests.isEmpty() ? "" : Status.WARN .getHtmlClass() : "";
.getHtmlClass(); String ignoreClass = dataModel.isAnyIgnores() ? Status.WARN
.getHtmlClass() : "";
String start = formatDateTime(dataListener.getStartTime()); String start = formatDateTime(dataModel.getStartTime());
String end = formatDateTime(dataListener.getEndTime()); String end = formatDateTime(dataModel.getEndTime());
String elapsed = formatElapsedTime(dataListener.getElapsedTime()); String elapsed = formatElapsedTime(dataModel.getElapsedTime());
writer.println(" <div class=\"section\">Summary</div>"); writer.println(" <div class=\"section\">Summary</div>");
writer.println(); writer.println();
@ -211,28 +138,24 @@ public class OutputSummaryFormatter {
writer.println(" </table>"); writer.println(" </table>");
writer.println(" </td>"); writer.println(" </td>");
writer.println(" <td>"); writer.println(" <td>");
writer.println(" <table cellspacing=\"0\">"); writer.println(" <table class=\"tallys\" cellspacing=\"0\">");
writer.println(" <tr><th>&nbsp;</th><th>Suites</th><th>Tests</th>"); writer.println(" <tr><th>&nbsp;</th><th>Suites</th><th>Tests</th>");
writer.println(" <tr><th>Total</th><td>"
+ (this.passingSuites.size() + this.failingSuites.size()
+ this.ignoredSuites.size() + this.remainingSuites
.size())
+ "</td><td>"
+ (this.passingTestCount + this.failingTests.size() + this.ignoredTests
.size()) + "</td>");
writer.println(" <tr class=\"" + passClass writer.println(" <tr class=\"" + passClass
+ "\"><th>Passed</th><td>" + this.passingSuites.size() + "\"><td>Passed</td><td>" + dataModel.getPassingSuiteCount()
+ "</td><td>" + this.passingTestCount + "</td>"); + "</td><td>" + dataModel.getPassingTestCount() + "</td>");
writer.println(" <tr class=\"" + failClass writer.println(" <tr class=\"" + failClass
+ "\"><th>Failed</th><td>" + this.failingSuites.size() + "\"><td>Failed</td><td>" + dataModel.getFailingSuiteCount()
+ "</td><td>" + this.failingTests.size() + "</td>"); + "</td><td>" + dataModel.getFailingTestCount() + "</td>");
writer.println(" <tr class=\"" + ignoreClass writer.println(" <tr class=\"" + ignoreClass
+ "\"><th>Ignored</th><td>" + this.ignoredSuites.size() + "\"><td>Ignored</td><td>" + dataModel.getIgnoredSuiteCount()
+ "</td><td>" + this.ignoredTests.size() + "</td>"); + "</td><td>" + dataModel.getIgnoredTestCount() + "</td>");
if (!this.remainingSuites.isEmpty()) { if (dataModel.isAnyPending()) {
writer.println(" <tr><th>Remaining</th><td>" writer.println(" <tr><td>Pending</td><td>"
+ this.remainingSuites.size() + "</td><td>?</td>"); + dataModel.getPendingSuitesCount() + "</td><td>?</td>");
} }
writer.println(" <tr><td class=\"total\">Total</td><td>"
+ dataModel.getTotalSuiteCount() + "</td><td>"
+ dataModel.getTotalTestCount() + "</td>");
writer.println(" </table>"); writer.println(" </table>");
writer.println(" </td>"); writer.println(" </td>");
writer.println(" </tr>"); writer.println(" </tr>");
@ -266,6 +189,7 @@ public class OutputSummaryFormatter {
private void writeFailureSection(PrintWriter writer) { private void writeFailureSection(PrintWriter writer) {
String errorClass = Status.ERROR.getHtmlClass(); String errorClass = Status.ERROR.getHtmlClass();
Collection<TestResults> failingTests = dataModel.getFailingTests();
writer.println(" <div class=section>Failing tests</div>"); writer.println(" <div class=section>Failing tests</div>");
writer.println(); writer.println();
@ -289,6 +213,7 @@ public class OutputSummaryFormatter {
private void writeIgnoreSection(PrintWriter writer) { private void writeIgnoreSection(PrintWriter writer) {
String warnClass = Status.WARN.getHtmlClass(); String warnClass = Status.WARN.getHtmlClass();
Collection<TestResults> ignoredTests = dataModel.getIgnoredTests();
writer.println(" <div class=section>Ignored tests</div>"); writer.println(" <div class=section>Ignored tests</div>");
writer.println(); writer.println();
@ -318,7 +243,7 @@ public class OutputSummaryFormatter {
writer.println(); writer.println();
writer.println(" <table cellspacing=\"0\">"); writer.println(" <table cellspacing=\"0\">");
for (SuiteResults s : suites.values()) { for (SuiteResults s : dataModel.getSuiteResults()) {
writer.println(" <tr class=\"" + s.getStatus().getHtmlClass() writer.println(" <tr class=\"" + s.getStatus().getHtmlClass()
+ "\">"); + "\">");
writer.println(" <td><a href=\"" + s.getOutputLink() + "\">" writer.println(" <td><a href=\"" + s.getOutputLink() + "\">"
@ -331,6 +256,8 @@ public class OutputSummaryFormatter {
} }
private void writeAllTestsSection(PrintWriter writer) { private void writeAllTestsSection(PrintWriter writer) {
Collection<TestResults> allTests = dataModel.getAllTests();
writer.println(" <div class=section>All tests</div>"); writer.println(" <div class=section>All tests</div>");
writer.println(); writer.println();
writer.println(" <table cellspacing=\"0\">"); writer.println(" <table cellspacing=\"0\">");

View file

@ -94,11 +94,7 @@ public class SuiteResults{
status = Status.OK; status = Status.OK;
for (TestResults t : tests) { for (TestResults t : tests) {
if (t.status == Status.ERROR) { status = Status.combine(status, t.status);
status = Status.ERROR;
} else if ((t.status == Status.WARN) && (status == Status.OK)) {
status = Status.WARN;
}
} }
if (isSuiteOutputFile) { if (isSuiteOutputFile) {

View file

@ -40,6 +40,14 @@ table.summary td {
vertical-align: top; vertical-align: top;
} }
table.tallys td {
align: right;
}
table.tallys td.total {
font.weight: bold;
}
.section { .section {
background: rgb(70%, 85%, 85%); background: rgb(70%, 85%, 85%);
font-size: larger; font-size: larger;