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:
parent
f09fcee448
commit
a05c62e30a
10 changed files with 457 additions and 154 deletions
|
@ -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;
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -20,14 +25,23 @@ public enum Status {
|
||||||
* generated.
|
* generated.
|
||||||
*/
|
*/
|
||||||
ERROR("bad");
|
ERROR("bad");
|
||||||
|
|
||||||
private final String htmlClass;
|
private final String htmlClass;
|
||||||
|
|
||||||
private Status(String htmlClass) {
|
private Status(String htmlClass) {
|
||||||
this.htmlClass = htmlClass;
|
this.htmlClass = htmlClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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,35 +51,74 @@ public class OutputDataListener implements Listener {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Accessor methods
|
// A class that holds a snapshot of the data.
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
public boolean isRunCompleted() {
|
/**
|
||||||
return runCompleted;
|
* A snapshot of the data that the listener has accumulated so far.
|
||||||
}
|
*/
|
||||||
|
public static class Info {
|
||||||
|
public static Info EMPTY_INFO = new Info();
|
||||||
|
|
||||||
public long getStartTime() {
|
private final boolean runCompleted;
|
||||||
return startTime;
|
private final long startTime;
|
||||||
}
|
private final long endTime;
|
||||||
|
private final List<String> suiteNames;
|
||||||
|
private final List<String> ignoredSuiteNames;
|
||||||
|
|
||||||
public long getEndTime() {
|
Info() {
|
||||||
return endTime;
|
this.runCompleted = false;
|
||||||
}
|
this.startTime = 0;
|
||||||
|
this.endTime = 0;
|
||||||
|
this.suiteNames = Collections.emptyList();
|
||||||
|
this.ignoredSuiteNames = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
public long getElapsedTime() {
|
Info(OutputDataListener parent) {
|
||||||
if ((startTime == 0) || (endTime == 0)) {
|
this.runCompleted = parent.runCompleted;
|
||||||
return 0;
|
this.startTime = parent.startTime;
|
||||||
} else {
|
this.endTime = parent.endTime;
|
||||||
return endTime - startTime;
|
this.suiteNames = Collections
|
||||||
|
.unmodifiableList(new ArrayList<String>(parent.suiteNames));
|
||||||
|
this.ignoredSuiteNames = Collections
|
||||||
|
.unmodifiableList(new ArrayList<String>(
|
||||||
|
parent.ignoredSuiteNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRunCompleted() {
|
||||||
|
return runCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getStartTime() {
|
||||||
|
return startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getEndTime() {
|
||||||
|
return endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getElapsedTime() {
|
||||||
|
if ((startTime == 0) || (endTime == 0)) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return endTime - startTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getSuiteNames() {
|
||||||
|
return suiteNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getIgnoredSuiteNames() {
|
||||||
|
return ignoredSuiteNames;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getSuiteNames() {
|
/**
|
||||||
return suiteNames;
|
* Get a snapshot of the data.
|
||||||
}
|
*/
|
||||||
|
public Info getInfo() {
|
||||||
public List<String> getIgnoredSuiteNames() {
|
return new Info(this);
|
||||||
return ignoredSuiteNames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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> </th><th>Suites</th><th>Tests</th>");
|
writer.println(" <tr><th> </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\">");
|
||||||
|
|
|
@ -22,7 +22,7 @@ import edu.cornell.mannlib.vitro.utilities.testrunner.Status;
|
||||||
* Extract any summary information from an HTML output file, produced by a test
|
* Extract any summary information from an HTML output file, produced by a test
|
||||||
* suite.
|
* suite.
|
||||||
*/
|
*/
|
||||||
public class SuiteResults{
|
public class SuiteResults {
|
||||||
/**
|
/**
|
||||||
* If the file doesn't contain a line that includes this pattern, it is not
|
* If the file doesn't contain a line that includes this pattern, it is not
|
||||||
* a suite output file.
|
* a suite output 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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue