Line-ending characters
This commit is contained in:
parent
0192edbd67
commit
2e8ef62bb1
5 changed files with 1155 additions and 1155 deletions
|
@ -1,26 +1,26 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||
|
||||
/**
|
||||
* Indicates a problem with the attempt to run a command in a sub-process.
|
||||
*/
|
||||
public class CommandRunnerException extends Exception {
|
||||
|
||||
public CommandRunnerException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CommandRunnerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CommandRunnerException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public CommandRunnerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||
|
||||
/**
|
||||
* Indicates a problem with the attempt to run a command in a sub-process.
|
||||
*/
|
||||
public class CommandRunnerException extends Exception {
|
||||
|
||||
public CommandRunnerException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CommandRunnerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CommandRunnerException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public CommandRunnerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||
|
||||
/**
|
||||
* Indicates a problem so severe that we might as well stop now.
|
||||
*/
|
||||
public class FatalException extends RuntimeException {
|
||||
|
||||
public FatalException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FatalException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public FatalException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public FatalException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||
|
||||
/**
|
||||
* Indicates a problem so severe that we might as well stop now.
|
||||
*/
|
||||
public class FatalException extends RuntimeException {
|
||||
|
||||
public FatalException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FatalException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public FatalException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public FatalException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,434 +1,434 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.LoggingListener;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.MulticastListener;
|
||||
|
||||
/**
|
||||
* Holds the runtime parameters that are read from the properties file, perhaps
|
||||
* with modifications from the GUI if we are running interactively.
|
||||
*/
|
||||
public class SeleniumRunnerParameters {
|
||||
public static final String PROP_OUTPUT_DIRECTORY = "output_directory";
|
||||
public static final String PROP_UPLOAD_DIRECTORY = "upload_directory";
|
||||
public static final String PROP_SUITE_DIRECTORIES = "suite_parent_directories";
|
||||
public static final String PROP_WEBSITE_URL = "website_url";
|
||||
public static final String PROP_USER_EXTENSIONS_PATH = "user_extensions_path";
|
||||
public static final String PROP_FIREFOX_PROFILE_PATH = "firefox_profile_template_path";
|
||||
public static final String PROP_SUITE_TIMEOUT_LIMIT = "suite_timeout_limit";
|
||||
public static final String PROP_SELENIUM_JAR_PATH = "selenium_jar_path";
|
||||
public static final String PROP_IGNORED_TESTS = "ignored_tests_file";
|
||||
public static final String PROP_SUMMARY_CSS = "summary_css_file";
|
||||
|
||||
public static final String LOGFILE_NAME = "log_file.txt";
|
||||
|
||||
private final String websiteUrl;
|
||||
private final File userExtensionsFile;
|
||||
private final File firefoxProfileDir;
|
||||
private final int suiteTimeoutLimit;
|
||||
private final File seleniumJarPath;
|
||||
private final File uploadDirectory;
|
||||
private final File outputDirectory;
|
||||
private final File logFile;
|
||||
private final Collection<File> suiteParentDirectories;
|
||||
private final ModelCleanerProperties modelCleanerProperties;
|
||||
private final IgnoredTests ignoredTests;
|
||||
|
||||
private boolean cleanModel = true;
|
||||
private boolean cleanUploads = true;
|
||||
|
||||
// If we fail during the parameter parsing, we'll still write the log
|
||||
// somewhere.
|
||||
private Listener listener = new LoggingListener(System.out);
|
||||
|
||||
/**
|
||||
* Read the required properties from the property file, and do some checks
|
||||
* on them.
|
||||
*/
|
||||
public SeleniumRunnerParameters(String propertiesFilepath)
|
||||
throws IOException {
|
||||
Properties props = loadPropertiesFile(propertiesFilepath);
|
||||
|
||||
this.websiteUrl = getRequiredProperty(props, PROP_WEBSITE_URL);
|
||||
this.userExtensionsFile = checkReadableFile(props,
|
||||
PROP_USER_EXTENSIONS_PATH);
|
||||
this.firefoxProfileDir = checkOptionalReadableDirectory(props,
|
||||
PROP_FIREFOX_PROFILE_PATH);
|
||||
this.suiteTimeoutLimit = getRequiredIntegerProperty(props,
|
||||
PROP_SUITE_TIMEOUT_LIMIT);
|
||||
this.seleniumJarPath = checkReadableFile(props, PROP_SELENIUM_JAR_PATH);
|
||||
this.uploadDirectory = checkReadWriteDirectory(props,
|
||||
PROP_UPLOAD_DIRECTORY);
|
||||
|
||||
this.outputDirectory = checkOutputDirectory(props);
|
||||
this.logFile = new File(this.outputDirectory, LOGFILE_NAME);
|
||||
this.listener = new MulticastListener();
|
||||
addListener(new LoggingListener(this.logFile));
|
||||
|
||||
this.suiteParentDirectories = checkSuiteParentDirectories(props);
|
||||
|
||||
this.modelCleanerProperties = new ModelCleanerProperties(props);
|
||||
|
||||
// Get the list of ignored tests.
|
||||
String ignoredFilesPath = getRequiredProperty(props, PROP_IGNORED_TESTS);
|
||||
File ignoredFilesFile = new File(ignoredFilesPath);
|
||||
FileHelper.checkReadableFile(ignoredFilesFile, "File '"
|
||||
+ ignoredFilesPath + "'");
|
||||
this.ignoredTests = new IgnoredTests(ignoredFilesFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the properties from the properties file.
|
||||
*/
|
||||
private Properties loadPropertiesFile(String propertiesFilepath)
|
||||
throws FileNotFoundException, IOException {
|
||||
File propsFile = new File(propertiesFilepath);
|
||||
if (!propsFile.exists()) {
|
||||
throw new FileNotFoundException("Property file does not exist: '"
|
||||
+ propsFile + "'");
|
||||
}
|
||||
|
||||
Reader propsReader = null;
|
||||
try {
|
||||
propsReader = new FileReader(propsFile);
|
||||
Properties props = new Properties();
|
||||
props.load(propsReader);
|
||||
return props;
|
||||
} finally {
|
||||
if (propsReader != null) {
|
||||
try {
|
||||
propsReader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a parameter for this key, it should point to a readable
|
||||
* directory.
|
||||
*/
|
||||
private File checkOptionalReadableDirectory(Properties props, String key) {
|
||||
String value = props.getProperty(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
value = value.trim();
|
||||
if (value.trim().length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File dir = new File(value);
|
||||
|
||||
if (!dir.exists()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' does not exist.");
|
||||
}
|
||||
|
||||
if (!dir.isDirectory()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not a directory.");
|
||||
}
|
||||
|
||||
if (!dir.canRead()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not readable.");
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that there is a property for the required directory path, and that
|
||||
* it points to a valid directory.
|
||||
*/
|
||||
private File checkReadWriteDirectory(Properties props, String key) {
|
||||
String value = getRequiredProperty(props, key);
|
||||
|
||||
File dir = new File(value);
|
||||
|
||||
if (!dir.exists()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' does not exist. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
|
||||
if (!dir.isDirectory()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not a directory. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
|
||||
if (!dir.canRead()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not readable. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
|
||||
if (!dir.canWrite()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not writeable. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
private File checkReadableFile(Properties props, String key) {
|
||||
String value = getRequiredProperty(props, key);
|
||||
|
||||
File file = new File(value);
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new IllegalArgumentException("File " + key + ": '" + value
|
||||
+ "' does not exist. (" + file.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!file.isFile()) {
|
||||
throw new IllegalArgumentException("File " + key + ": '" + value
|
||||
+ "' is not a file. (" + file.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!file.canRead()) {
|
||||
throw new IllegalArgumentException("File " + key + ": '" + value
|
||||
+ "' is not readable. (" + file.getAbsolutePath() + ")");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property for the output directory. If it does not exist, create
|
||||
* it (the parent must exist). Ensure that it is writeable.
|
||||
*/
|
||||
private File checkOutputDirectory(Properties props) throws IOException {
|
||||
String value = getRequiredProperty(props, PROP_OUTPUT_DIRECTORY);
|
||||
File outputDirectory = new File(value);
|
||||
File outputParent = outputDirectory.getParentFile();
|
||||
|
||||
if (!outputDirectory.exists()) {
|
||||
if (!outputParent.exists()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Output directory does not exist, nor does its parent. '"
|
||||
+ outputDirectory + "' ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
outputDirectory.mkdir();
|
||||
if (!outputDirectory.exists()) {
|
||||
throw new IOException("Failed to create output directory: '"
|
||||
+ outputDirectory + "' ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
if (!outputDirectory.isDirectory()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ outputDirectory.getPath() + "' is not a directory. ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!outputDirectory.canRead()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ outputDirectory.getPath() + "' is not readable. ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!outputDirectory.canWrite()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ outputDirectory.getPath() + "' is not writeable. ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
|
||||
return outputDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property for the suite directories and ensure that each one is
|
||||
* indeed a readable directory.
|
||||
*/
|
||||
private Collection<File> checkSuiteParentDirectories(Properties props) {
|
||||
String value = getRequiredProperty(props, PROP_SUITE_DIRECTORIES);
|
||||
|
||||
List<File> dirs = new ArrayList<File>();
|
||||
String[] paths = value.split("[:;]");
|
||||
for (String path : paths) {
|
||||
File dir = new File(path.trim());
|
||||
|
||||
if (!dir.exists()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ dir.getPath() + "' does not exist. ("
|
||||
+ dir.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!dir.isDirectory()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ dir.getPath() + "' is not a directory. ("
|
||||
+ dir.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!dir.canRead()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ dir.getPath() + "' is not readable. ("
|
||||
+ dir.getAbsolutePath() + ")");
|
||||
}
|
||||
dirs.add(dir);
|
||||
}
|
||||
return dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value for this property. If there isn't one, or if it's empty,
|
||||
* complain.
|
||||
*/
|
||||
private String getRequiredProperty(Properties props, String key) {
|
||||
String value = props.getProperty(key);
|
||||
if ((value == null) || (value.trim().length() == 0)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Property file must provide a value for '" + key + "'");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This required property must be a valid integer.
|
||||
*/
|
||||
private int getRequiredIntegerProperty(Properties props, String key) {
|
||||
String value = getRequiredProperty(props, key);
|
||||
try {
|
||||
return Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Property value for '" + key
|
||||
+ "' is not a valid integer: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
public String getWebsiteUrl() {
|
||||
return websiteUrl;
|
||||
}
|
||||
|
||||
public File getUserExtensionsFile() {
|
||||
return userExtensionsFile;
|
||||
}
|
||||
|
||||
public boolean hasFirefoxProfileDir() {
|
||||
return firefoxProfileDir != null;
|
||||
}
|
||||
|
||||
public File getFirefoxProfileDir() {
|
||||
return firefoxProfileDir;
|
||||
}
|
||||
|
||||
public int getSuiteTimeoutLimit() {
|
||||
return suiteTimeoutLimit;
|
||||
}
|
||||
|
||||
public File getSeleniumJarPath() {
|
||||
return seleniumJarPath;
|
||||
}
|
||||
|
||||
public void addListener(Listener l) {
|
||||
if (listener instanceof MulticastListener) {
|
||||
((MulticastListener) listener).addListener(l);
|
||||
} else {
|
||||
throw new IllegalStateException("Listener is not a multi-cast -- "
|
||||
+ "can't add new listeners.");
|
||||
}
|
||||
}
|
||||
|
||||
public Listener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public void setListener(Listener logger) {
|
||||
this.listener = logger;
|
||||
}
|
||||
|
||||
public File getUploadDirectory() {
|
||||
return uploadDirectory;
|
||||
}
|
||||
|
||||
public File getOutputDirectory() {
|
||||
return outputDirectory;
|
||||
}
|
||||
|
||||
public File getLogFile() {
|
||||
return logFile;
|
||||
}
|
||||
|
||||
public Collection<File> getSuiteParentDirectories() {
|
||||
return suiteParentDirectories;
|
||||
}
|
||||
|
||||
public ModelCleanerProperties getModelCleanerProperties() {
|
||||
return modelCleanerProperties;
|
||||
}
|
||||
|
||||
public IgnoredTests getIgnoredTests() {
|
||||
return ignoredTests;
|
||||
}
|
||||
|
||||
public boolean isCleanModel() {
|
||||
return cleanModel;
|
||||
}
|
||||
|
||||
public void setCleanModel(boolean cleanModel) {
|
||||
this.cleanModel = cleanModel;
|
||||
}
|
||||
|
||||
public boolean isCleanUploads() {
|
||||
return cleanUploads;
|
||||
}
|
||||
|
||||
public void setCleanUploads(boolean cleanUploads) {
|
||||
this.cleanUploads = cleanUploads;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Parameters:" + "\n websiteUrl: " + websiteUrl
|
||||
+ "\n userExtensionsFile: " + userExtensionsFile
|
||||
+ "\n firefoxProfileDir: " + firefoxProfileDir
|
||||
+ "\n suiteTimeoutLimit: " + suiteTimeoutLimit
|
||||
+ "\n seleniumJarPath: " + seleniumJarPath
|
||||
+ "\n uploadDirectory: " + uploadDirectory
|
||||
+ "\n outputDirectory: " + outputDirectory
|
||||
+ "\n suiteParentDirectories: " + suiteParentDirectories
|
||||
+ "\n modelCleanerProperties: " + modelCleanerProperties
|
||||
+ "\n" + ignoredTests + "\n cleanModel: " + cleanModel
|
||||
+ "\n cleanUploads: " + cleanUploads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look inside this parent directory and find any suite directories. You can
|
||||
* recognize a suite directory because it contains a file named Suite.html.
|
||||
*/
|
||||
public Collection<File> findSuiteDirs(File parentDir) {
|
||||
return Arrays.asList(parentDir.listFiles(new FileFilter() {
|
||||
public boolean accept(File pathname) {
|
||||
if (!pathname.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
if (pathname.getName().charAt(0) == '.') {
|
||||
return false;
|
||||
}
|
||||
|
||||
File suiteFile = new File(pathname, "Suite.html");
|
||||
if (suiteFile.exists()) {
|
||||
return true;
|
||||
} else {
|
||||
listener.subProcessErrout("Warning: suite file '"
|
||||
+ suiteFile.getPath() + "' does not exist.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.LoggingListener;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.MulticastListener;
|
||||
|
||||
/**
|
||||
* Holds the runtime parameters that are read from the properties file, perhaps
|
||||
* with modifications from the GUI if we are running interactively.
|
||||
*/
|
||||
public class SeleniumRunnerParameters {
|
||||
public static final String PROP_OUTPUT_DIRECTORY = "output_directory";
|
||||
public static final String PROP_UPLOAD_DIRECTORY = "upload_directory";
|
||||
public static final String PROP_SUITE_DIRECTORIES = "suite_parent_directories";
|
||||
public static final String PROP_WEBSITE_URL = "website_url";
|
||||
public static final String PROP_USER_EXTENSIONS_PATH = "user_extensions_path";
|
||||
public static final String PROP_FIREFOX_PROFILE_PATH = "firefox_profile_template_path";
|
||||
public static final String PROP_SUITE_TIMEOUT_LIMIT = "suite_timeout_limit";
|
||||
public static final String PROP_SELENIUM_JAR_PATH = "selenium_jar_path";
|
||||
public static final String PROP_IGNORED_TESTS = "ignored_tests_file";
|
||||
public static final String PROP_SUMMARY_CSS = "summary_css_file";
|
||||
|
||||
public static final String LOGFILE_NAME = "log_file.txt";
|
||||
|
||||
private final String websiteUrl;
|
||||
private final File userExtensionsFile;
|
||||
private final File firefoxProfileDir;
|
||||
private final int suiteTimeoutLimit;
|
||||
private final File seleniumJarPath;
|
||||
private final File uploadDirectory;
|
||||
private final File outputDirectory;
|
||||
private final File logFile;
|
||||
private final Collection<File> suiteParentDirectories;
|
||||
private final ModelCleanerProperties modelCleanerProperties;
|
||||
private final IgnoredTests ignoredTests;
|
||||
|
||||
private boolean cleanModel = true;
|
||||
private boolean cleanUploads = true;
|
||||
|
||||
// If we fail during the parameter parsing, we'll still write the log
|
||||
// somewhere.
|
||||
private Listener listener = new LoggingListener(System.out);
|
||||
|
||||
/**
|
||||
* Read the required properties from the property file, and do some checks
|
||||
* on them.
|
||||
*/
|
||||
public SeleniumRunnerParameters(String propertiesFilepath)
|
||||
throws IOException {
|
||||
Properties props = loadPropertiesFile(propertiesFilepath);
|
||||
|
||||
this.websiteUrl = getRequiredProperty(props, PROP_WEBSITE_URL);
|
||||
this.userExtensionsFile = checkReadableFile(props,
|
||||
PROP_USER_EXTENSIONS_PATH);
|
||||
this.firefoxProfileDir = checkOptionalReadableDirectory(props,
|
||||
PROP_FIREFOX_PROFILE_PATH);
|
||||
this.suiteTimeoutLimit = getRequiredIntegerProperty(props,
|
||||
PROP_SUITE_TIMEOUT_LIMIT);
|
||||
this.seleniumJarPath = checkReadableFile(props, PROP_SELENIUM_JAR_PATH);
|
||||
this.uploadDirectory = checkReadWriteDirectory(props,
|
||||
PROP_UPLOAD_DIRECTORY);
|
||||
|
||||
this.outputDirectory = checkOutputDirectory(props);
|
||||
this.logFile = new File(this.outputDirectory, LOGFILE_NAME);
|
||||
this.listener = new MulticastListener();
|
||||
addListener(new LoggingListener(this.logFile));
|
||||
|
||||
this.suiteParentDirectories = checkSuiteParentDirectories(props);
|
||||
|
||||
this.modelCleanerProperties = new ModelCleanerProperties(props);
|
||||
|
||||
// Get the list of ignored tests.
|
||||
String ignoredFilesPath = getRequiredProperty(props, PROP_IGNORED_TESTS);
|
||||
File ignoredFilesFile = new File(ignoredFilesPath);
|
||||
FileHelper.checkReadableFile(ignoredFilesFile, "File '"
|
||||
+ ignoredFilesPath + "'");
|
||||
this.ignoredTests = new IgnoredTests(ignoredFilesFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the properties from the properties file.
|
||||
*/
|
||||
private Properties loadPropertiesFile(String propertiesFilepath)
|
||||
throws FileNotFoundException, IOException {
|
||||
File propsFile = new File(propertiesFilepath);
|
||||
if (!propsFile.exists()) {
|
||||
throw new FileNotFoundException("Property file does not exist: '"
|
||||
+ propsFile + "'");
|
||||
}
|
||||
|
||||
Reader propsReader = null;
|
||||
try {
|
||||
propsReader = new FileReader(propsFile);
|
||||
Properties props = new Properties();
|
||||
props.load(propsReader);
|
||||
return props;
|
||||
} finally {
|
||||
if (propsReader != null) {
|
||||
try {
|
||||
propsReader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a parameter for this key, it should point to a readable
|
||||
* directory.
|
||||
*/
|
||||
private File checkOptionalReadableDirectory(Properties props, String key) {
|
||||
String value = props.getProperty(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
value = value.trim();
|
||||
if (value.trim().length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File dir = new File(value);
|
||||
|
||||
if (!dir.exists()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' does not exist.");
|
||||
}
|
||||
|
||||
if (!dir.isDirectory()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not a directory.");
|
||||
}
|
||||
|
||||
if (!dir.canRead()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not readable.");
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that there is a property for the required directory path, and that
|
||||
* it points to a valid directory.
|
||||
*/
|
||||
private File checkReadWriteDirectory(Properties props, String key) {
|
||||
String value = getRequiredProperty(props, key);
|
||||
|
||||
File dir = new File(value);
|
||||
|
||||
if (!dir.exists()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' does not exist. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
|
||||
if (!dir.isDirectory()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not a directory. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
|
||||
if (!dir.canRead()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not readable. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
|
||||
if (!dir.canWrite()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not writeable. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
private File checkReadableFile(Properties props, String key) {
|
||||
String value = getRequiredProperty(props, key);
|
||||
|
||||
File file = new File(value);
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new IllegalArgumentException("File " + key + ": '" + value
|
||||
+ "' does not exist. (" + file.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!file.isFile()) {
|
||||
throw new IllegalArgumentException("File " + key + ": '" + value
|
||||
+ "' is not a file. (" + file.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!file.canRead()) {
|
||||
throw new IllegalArgumentException("File " + key + ": '" + value
|
||||
+ "' is not readable. (" + file.getAbsolutePath() + ")");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property for the output directory. If it does not exist, create
|
||||
* it (the parent must exist). Ensure that it is writeable.
|
||||
*/
|
||||
private File checkOutputDirectory(Properties props) throws IOException {
|
||||
String value = getRequiredProperty(props, PROP_OUTPUT_DIRECTORY);
|
||||
File outputDirectory = new File(value);
|
||||
File outputParent = outputDirectory.getParentFile();
|
||||
|
||||
if (!outputDirectory.exists()) {
|
||||
if (!outputParent.exists()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Output directory does not exist, nor does its parent. '"
|
||||
+ outputDirectory + "' ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
outputDirectory.mkdir();
|
||||
if (!outputDirectory.exists()) {
|
||||
throw new IOException("Failed to create output directory: '"
|
||||
+ outputDirectory + "' ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
if (!outputDirectory.isDirectory()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ outputDirectory.getPath() + "' is not a directory. ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!outputDirectory.canRead()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ outputDirectory.getPath() + "' is not readable. ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!outputDirectory.canWrite()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ outputDirectory.getPath() + "' is not writeable. ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
|
||||
return outputDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property for the suite directories and ensure that each one is
|
||||
* indeed a readable directory.
|
||||
*/
|
||||
private Collection<File> checkSuiteParentDirectories(Properties props) {
|
||||
String value = getRequiredProperty(props, PROP_SUITE_DIRECTORIES);
|
||||
|
||||
List<File> dirs = new ArrayList<File>();
|
||||
String[] paths = value.split("[:;]");
|
||||
for (String path : paths) {
|
||||
File dir = new File(path.trim());
|
||||
|
||||
if (!dir.exists()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ dir.getPath() + "' does not exist. ("
|
||||
+ dir.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!dir.isDirectory()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ dir.getPath() + "' is not a directory. ("
|
||||
+ dir.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!dir.canRead()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ dir.getPath() + "' is not readable. ("
|
||||
+ dir.getAbsolutePath() + ")");
|
||||
}
|
||||
dirs.add(dir);
|
||||
}
|
||||
return dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value for this property. If there isn't one, or if it's empty,
|
||||
* complain.
|
||||
*/
|
||||
private String getRequiredProperty(Properties props, String key) {
|
||||
String value = props.getProperty(key);
|
||||
if ((value == null) || (value.trim().length() == 0)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Property file must provide a value for '" + key + "'");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This required property must be a valid integer.
|
||||
*/
|
||||
private int getRequiredIntegerProperty(Properties props, String key) {
|
||||
String value = getRequiredProperty(props, key);
|
||||
try {
|
||||
return Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Property value for '" + key
|
||||
+ "' is not a valid integer: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
public String getWebsiteUrl() {
|
||||
return websiteUrl;
|
||||
}
|
||||
|
||||
public File getUserExtensionsFile() {
|
||||
return userExtensionsFile;
|
||||
}
|
||||
|
||||
public boolean hasFirefoxProfileDir() {
|
||||
return firefoxProfileDir != null;
|
||||
}
|
||||
|
||||
public File getFirefoxProfileDir() {
|
||||
return firefoxProfileDir;
|
||||
}
|
||||
|
||||
public int getSuiteTimeoutLimit() {
|
||||
return suiteTimeoutLimit;
|
||||
}
|
||||
|
||||
public File getSeleniumJarPath() {
|
||||
return seleniumJarPath;
|
||||
}
|
||||
|
||||
public void addListener(Listener l) {
|
||||
if (listener instanceof MulticastListener) {
|
||||
((MulticastListener) listener).addListener(l);
|
||||
} else {
|
||||
throw new IllegalStateException("Listener is not a multi-cast -- "
|
||||
+ "can't add new listeners.");
|
||||
}
|
||||
}
|
||||
|
||||
public Listener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public void setListener(Listener logger) {
|
||||
this.listener = logger;
|
||||
}
|
||||
|
||||
public File getUploadDirectory() {
|
||||
return uploadDirectory;
|
||||
}
|
||||
|
||||
public File getOutputDirectory() {
|
||||
return outputDirectory;
|
||||
}
|
||||
|
||||
public File getLogFile() {
|
||||
return logFile;
|
||||
}
|
||||
|
||||
public Collection<File> getSuiteParentDirectories() {
|
||||
return suiteParentDirectories;
|
||||
}
|
||||
|
||||
public ModelCleanerProperties getModelCleanerProperties() {
|
||||
return modelCleanerProperties;
|
||||
}
|
||||
|
||||
public IgnoredTests getIgnoredTests() {
|
||||
return ignoredTests;
|
||||
}
|
||||
|
||||
public boolean isCleanModel() {
|
||||
return cleanModel;
|
||||
}
|
||||
|
||||
public void setCleanModel(boolean cleanModel) {
|
||||
this.cleanModel = cleanModel;
|
||||
}
|
||||
|
||||
public boolean isCleanUploads() {
|
||||
return cleanUploads;
|
||||
}
|
||||
|
||||
public void setCleanUploads(boolean cleanUploads) {
|
||||
this.cleanUploads = cleanUploads;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Parameters:" + "\n websiteUrl: " + websiteUrl
|
||||
+ "\n userExtensionsFile: " + userExtensionsFile
|
||||
+ "\n firefoxProfileDir: " + firefoxProfileDir
|
||||
+ "\n suiteTimeoutLimit: " + suiteTimeoutLimit
|
||||
+ "\n seleniumJarPath: " + seleniumJarPath
|
||||
+ "\n uploadDirectory: " + uploadDirectory
|
||||
+ "\n outputDirectory: " + outputDirectory
|
||||
+ "\n suiteParentDirectories: " + suiteParentDirectories
|
||||
+ "\n modelCleanerProperties: " + modelCleanerProperties
|
||||
+ "\n" + ignoredTests + "\n cleanModel: " + cleanModel
|
||||
+ "\n cleanUploads: " + cleanUploads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look inside this parent directory and find any suite directories. You can
|
||||
* recognize a suite directory because it contains a file named Suite.html.
|
||||
*/
|
||||
public Collection<File> findSuiteDirs(File parentDir) {
|
||||
return Arrays.asList(parentDir.listFiles(new FileFilter() {
|
||||
public boolean accept(File pathname) {
|
||||
if (!pathname.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
if (pathname.getName().charAt(0) == '.') {
|
||||
return false;
|
||||
}
|
||||
|
||||
File suiteFile = new File(pathname, "Suite.html");
|
||||
if (suiteFile.exists()) {
|
||||
return true;
|
||||
} else {
|
||||
listener.subProcessErrout("Warning: suite file '"
|
||||
+ suiteFile.getPath() + "' does not exist.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,294 +1,294 @@
|
|||
/* $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.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats;
|
||||
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.ProcessOutput;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults;
|
||||
|
||||
/**
|
||||
* Collect all that we know about suites, tests, and their current status.
|
||||
*/
|
||||
public class DataModel {
|
||||
|
||||
/* base data */
|
||||
private Collection<SuiteContents> suiteContents = Collections.emptyList();
|
||||
private Collection<File> selectedSuites = Collections.emptyList();
|
||||
private Collection<SuiteResults> suiteResults = Collections.emptyList();
|
||||
private OutputDataListener.Info dataListenerInfo = OutputDataListener.Info.EMPTY_INFO;
|
||||
private IgnoredTests ignoredTestList = IgnoredTests.EMPTY_LIST;
|
||||
private LogStats logStats = LogStats.EMPTY_LOG_STATS; // TODO
|
||||
|
||||
/* derived data */
|
||||
private Status runStatus = Status.PENDING;
|
||||
|
||||
private final SortedMap<String, SuiteData> suiteDataMap = new TreeMap<String, SuiteData>();
|
||||
private final EnumMap<Status, List<SuiteData>> suiteMapByStatus = new EnumMap<Status, List<SuiteData>>(
|
||||
Status.class);
|
||||
|
||||
private final List<TestData> allTests = new ArrayList<TestData>();
|
||||
private final EnumMap<Status, List<TestData>> testMapByStatus = new EnumMap<Status, List<TestData>>(
|
||||
Status.class);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public DataModel() {
|
||||
calculate();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Update the base data.
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public void setSuiteContents(Collection<SuiteContents> suiteContents) {
|
||||
this.suiteContents = new ArrayList<SuiteContents>(suiteContents);
|
||||
calculate();
|
||||
}
|
||||
|
||||
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 setIgnoredTestList(IgnoredTests ignoredTestList) {
|
||||
this.ignoredTestList = ignoredTestList;
|
||||
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;
|
||||
|
||||
suiteDataMap.clear();
|
||||
suiteMapByStatus.clear();
|
||||
for (Status s : Status.values()) {
|
||||
suiteMapByStatus.put(s, new ArrayList<SuiteData>());
|
||||
}
|
||||
|
||||
allTests.clear();
|
||||
testMapByStatus.clear();
|
||||
for (Status s : Status.values()) {
|
||||
testMapByStatus.put(s, new ArrayList<TestData>());
|
||||
}
|
||||
|
||||
/*
|
||||
* Populate the Suite map with all Suites.
|
||||
*/
|
||||
Map<String, SuiteResults> resultsMap = new HashMap<String, SuiteResults>();
|
||||
for (SuiteResults result : suiteResults) {
|
||||
resultsMap.put(result.getName(), result);
|
||||
}
|
||||
Map<String, SuiteContents> contentsMap = new HashMap<String, SuiteContents>();
|
||||
for (SuiteContents contents : suiteContents) {
|
||||
contentsMap.put(contents.getName(), contents);
|
||||
}
|
||||
|
||||
for (SuiteContents contents : suiteContents) {
|
||||
String name = contents.getName();
|
||||
SuiteResults result = resultsMap.get(name);
|
||||
boolean ignored = ignoredTestList.isIgnored(name);
|
||||
ProcessOutput failureMessages = dataListenerInfo
|
||||
.getFailureMessages().get(name);
|
||||
suiteDataMap.put(name, new SuiteData(name, ignored, contents,
|
||||
result, failureMessages));
|
||||
}
|
||||
|
||||
/*
|
||||
* Map the Suites by status.
|
||||
*/
|
||||
for (SuiteData s : suiteDataMap.values()) {
|
||||
getSuites(s.getStatus()).add(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the Test map with all Tests, and map by status.
|
||||
*/
|
||||
for (SuiteData s : suiteDataMap.values()) {
|
||||
for (TestData t : s.getTestMap().values()) {
|
||||
allTests.add(t);
|
||||
getTests(t.getStatus()).add(t);
|
||||
}
|
||||
}
|
||||
|
||||
if (logStats.hasErrors() || !getSuites(Status.ERROR).isEmpty()) {
|
||||
runStatus = Status.ERROR;
|
||||
} else if (!getSuites(Status.PENDING).isEmpty()) {
|
||||
runStatus = Status.PENDING;
|
||||
} else {
|
||||
runStatus = Status.OK;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// 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 !getTests(Status.OK).isEmpty();
|
||||
}
|
||||
|
||||
public boolean isAnyFailures() {
|
||||
return !getTests(Status.ERROR).isEmpty();
|
||||
}
|
||||
|
||||
public boolean isAnyIgnores() {
|
||||
return !getTests(Status.IGNORED).isEmpty();
|
||||
}
|
||||
|
||||
public boolean isAnyPending() {
|
||||
return !getTests(Status.PENDING).isEmpty();
|
||||
}
|
||||
|
||||
public int getTotalSuiteCount() {
|
||||
return suiteDataMap.size();
|
||||
}
|
||||
|
||||
public int getPassingSuiteCount() {
|
||||
return getSuites(Status.OK).size();
|
||||
}
|
||||
|
||||
public int getFailingSuiteCount() {
|
||||
return getSuites(Status.ERROR).size();
|
||||
}
|
||||
|
||||
public int getIgnoredSuiteCount() {
|
||||
return getSuites(Status.IGNORED).size();
|
||||
}
|
||||
|
||||
public int getPendingSuiteCount() {
|
||||
return getSuites(Status.PENDING).size();
|
||||
}
|
||||
|
||||
public Collection<SuiteData> getAllSuites() {
|
||||
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() {
|
||||
return allTests.size();
|
||||
}
|
||||
|
||||
public int getPassingTestCount() {
|
||||
return getTests(Status.OK).size();
|
||||
}
|
||||
|
||||
public int getFailingTestCount() {
|
||||
return getTests(Status.ERROR).size();
|
||||
}
|
||||
|
||||
public int getIgnoredTestCount() {
|
||||
return getTests(Status.IGNORED).size();
|
||||
}
|
||||
|
||||
public int getPendingTestCount() {
|
||||
return getTests(Status.PENDING).size();
|
||||
}
|
||||
|
||||
public Collection<TestData> getAllTests() {
|
||||
return Collections.unmodifiableCollection(allTests);
|
||||
}
|
||||
|
||||
public Collection<TestData> getFailingTests() {
|
||||
return Collections.unmodifiableCollection(getTests(Status.ERROR));
|
||||
}
|
||||
|
||||
public Collection<TestData> getIgnoredTests() {
|
||||
return Collections.unmodifiableCollection(getTests(Status.IGNORED));
|
||||
}
|
||||
|
||||
public Collection<IgnoredTestInfo> getIgnoredTestInfo() {
|
||||
return ignoredTestList.getList();
|
||||
}
|
||||
|
||||
public String getReasonForIgnoring(String suiteName, String 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);
|
||||
}
|
||||
|
||||
}
|
||||
/* $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.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats;
|
||||
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.ProcessOutput;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults;
|
||||
|
||||
/**
|
||||
* Collect all that we know about suites, tests, and their current status.
|
||||
*/
|
||||
public class DataModel {
|
||||
|
||||
/* base data */
|
||||
private Collection<SuiteContents> suiteContents = Collections.emptyList();
|
||||
private Collection<File> selectedSuites = Collections.emptyList();
|
||||
private Collection<SuiteResults> suiteResults = Collections.emptyList();
|
||||
private OutputDataListener.Info dataListenerInfo = OutputDataListener.Info.EMPTY_INFO;
|
||||
private IgnoredTests ignoredTestList = IgnoredTests.EMPTY_LIST;
|
||||
private LogStats logStats = LogStats.EMPTY_LOG_STATS; // TODO
|
||||
|
||||
/* derived data */
|
||||
private Status runStatus = Status.PENDING;
|
||||
|
||||
private final SortedMap<String, SuiteData> suiteDataMap = new TreeMap<String, SuiteData>();
|
||||
private final EnumMap<Status, List<SuiteData>> suiteMapByStatus = new EnumMap<Status, List<SuiteData>>(
|
||||
Status.class);
|
||||
|
||||
private final List<TestData> allTests = new ArrayList<TestData>();
|
||||
private final EnumMap<Status, List<TestData>> testMapByStatus = new EnumMap<Status, List<TestData>>(
|
||||
Status.class);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public DataModel() {
|
||||
calculate();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Update the base data.
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public void setSuiteContents(Collection<SuiteContents> suiteContents) {
|
||||
this.suiteContents = new ArrayList<SuiteContents>(suiteContents);
|
||||
calculate();
|
||||
}
|
||||
|
||||
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 setIgnoredTestList(IgnoredTests ignoredTestList) {
|
||||
this.ignoredTestList = ignoredTestList;
|
||||
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;
|
||||
|
||||
suiteDataMap.clear();
|
||||
suiteMapByStatus.clear();
|
||||
for (Status s : Status.values()) {
|
||||
suiteMapByStatus.put(s, new ArrayList<SuiteData>());
|
||||
}
|
||||
|
||||
allTests.clear();
|
||||
testMapByStatus.clear();
|
||||
for (Status s : Status.values()) {
|
||||
testMapByStatus.put(s, new ArrayList<TestData>());
|
||||
}
|
||||
|
||||
/*
|
||||
* Populate the Suite map with all Suites.
|
||||
*/
|
||||
Map<String, SuiteResults> resultsMap = new HashMap<String, SuiteResults>();
|
||||
for (SuiteResults result : suiteResults) {
|
||||
resultsMap.put(result.getName(), result);
|
||||
}
|
||||
Map<String, SuiteContents> contentsMap = new HashMap<String, SuiteContents>();
|
||||
for (SuiteContents contents : suiteContents) {
|
||||
contentsMap.put(contents.getName(), contents);
|
||||
}
|
||||
|
||||
for (SuiteContents contents : suiteContents) {
|
||||
String name = contents.getName();
|
||||
SuiteResults result = resultsMap.get(name);
|
||||
boolean ignored = ignoredTestList.isIgnored(name);
|
||||
ProcessOutput failureMessages = dataListenerInfo
|
||||
.getFailureMessages().get(name);
|
||||
suiteDataMap.put(name, new SuiteData(name, ignored, contents,
|
||||
result, failureMessages));
|
||||
}
|
||||
|
||||
/*
|
||||
* Map the Suites by status.
|
||||
*/
|
||||
for (SuiteData s : suiteDataMap.values()) {
|
||||
getSuites(s.getStatus()).add(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the Test map with all Tests, and map by status.
|
||||
*/
|
||||
for (SuiteData s : suiteDataMap.values()) {
|
||||
for (TestData t : s.getTestMap().values()) {
|
||||
allTests.add(t);
|
||||
getTests(t.getStatus()).add(t);
|
||||
}
|
||||
}
|
||||
|
||||
if (logStats.hasErrors() || !getSuites(Status.ERROR).isEmpty()) {
|
||||
runStatus = Status.ERROR;
|
||||
} else if (!getSuites(Status.PENDING).isEmpty()) {
|
||||
runStatus = Status.PENDING;
|
||||
} else {
|
||||
runStatus = Status.OK;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// 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 !getTests(Status.OK).isEmpty();
|
||||
}
|
||||
|
||||
public boolean isAnyFailures() {
|
||||
return !getTests(Status.ERROR).isEmpty();
|
||||
}
|
||||
|
||||
public boolean isAnyIgnores() {
|
||||
return !getTests(Status.IGNORED).isEmpty();
|
||||
}
|
||||
|
||||
public boolean isAnyPending() {
|
||||
return !getTests(Status.PENDING).isEmpty();
|
||||
}
|
||||
|
||||
public int getTotalSuiteCount() {
|
||||
return suiteDataMap.size();
|
||||
}
|
||||
|
||||
public int getPassingSuiteCount() {
|
||||
return getSuites(Status.OK).size();
|
||||
}
|
||||
|
||||
public int getFailingSuiteCount() {
|
||||
return getSuites(Status.ERROR).size();
|
||||
}
|
||||
|
||||
public int getIgnoredSuiteCount() {
|
||||
return getSuites(Status.IGNORED).size();
|
||||
}
|
||||
|
||||
public int getPendingSuiteCount() {
|
||||
return getSuites(Status.PENDING).size();
|
||||
}
|
||||
|
||||
public Collection<SuiteData> getAllSuites() {
|
||||
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() {
|
||||
return allTests.size();
|
||||
}
|
||||
|
||||
public int getPassingTestCount() {
|
||||
return getTests(Status.OK).size();
|
||||
}
|
||||
|
||||
public int getFailingTestCount() {
|
||||
return getTests(Status.ERROR).size();
|
||||
}
|
||||
|
||||
public int getIgnoredTestCount() {
|
||||
return getTests(Status.IGNORED).size();
|
||||
}
|
||||
|
||||
public int getPendingTestCount() {
|
||||
return getTests(Status.PENDING).size();
|
||||
}
|
||||
|
||||
public Collection<TestData> getAllTests() {
|
||||
return Collections.unmodifiableCollection(allTests);
|
||||
}
|
||||
|
||||
public Collection<TestData> getFailingTests() {
|
||||
return Collections.unmodifiableCollection(getTests(Status.ERROR));
|
||||
}
|
||||
|
||||
public Collection<TestData> getIgnoredTests() {
|
||||
return Collections.unmodifiableCollection(getTests(Status.IGNORED));
|
||||
}
|
||||
|
||||
public Collection<IgnoredTestInfo> getIgnoredTestInfo() {
|
||||
return ignoredTestList.getList();
|
||||
}
|
||||
|
||||
public String getReasonForIgnoring(String suiteName, String 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,375 +1,375 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner.output;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats;
|
||||
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;
|
||||
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.
|
||||
*/
|
||||
public class OutputSummaryFormatter {
|
||||
public static final String SUMMARY_HTML_FILENAME = "summary.html";
|
||||
public static final String SUMMARY_CSS_FILENAME = "summary.css";
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat(
|
||||
"yyyy-MM-dd HH:mm:ss");
|
||||
private final SeleniumRunnerParameters parms;
|
||||
|
||||
private LogStats logStats;
|
||||
private DataModel dataModel;
|
||||
|
||||
public OutputSummaryFormatter(SeleniumRunnerParameters parms) {
|
||||
this.parms = parms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a summary HTML file from the info contained in this log file and
|
||||
* these suite outputs.
|
||||
*/
|
||||
public void format(LogStats logStats, DataModel dataModel) {
|
||||
this.logStats = logStats;
|
||||
this.dataModel = dataModel;
|
||||
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
copyCssFile();
|
||||
|
||||
File outputFile = new File(parms.getOutputDirectory(),
|
||||
SUMMARY_HTML_FILENAME);
|
||||
writer = new PrintWriter(outputFile);
|
||||
|
||||
writeHeader(writer);
|
||||
writeStatsSection(writer);
|
||||
writeErrorMessagesSection(writer);
|
||||
writeCondensedTable(writer);
|
||||
writeIgnoreSection(writer);
|
||||
writeSuiteErrorMessagesSection(writer);
|
||||
writeFooter(writer);
|
||||
} catch (IOException e) {
|
||||
// There is no appeal for any problems here. Just report them.
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the CSS file into the output directory.
|
||||
*/
|
||||
private void copyCssFile() {
|
||||
InputStream cssStream = this.getClass().getResourceAsStream(
|
||||
SUMMARY_CSS_FILENAME);
|
||||
if (cssStream == null) {
|
||||
System.out.println("Couldn't find the CSS file: '"
|
||||
+ SUMMARY_CSS_FILENAME + "'");
|
||||
} else {
|
||||
File cssTarget = new File(parms.getOutputDirectory(),
|
||||
SUMMARY_CSS_FILENAME);
|
||||
try {
|
||||
FileHelper.copy(cssStream, cssTarget);
|
||||
cssStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeHeader(PrintWriter w) {
|
||||
Status runStatus = dataModel.getRunStatus();
|
||||
String statusString = (runStatus == Status.PENDING) ? "IN PROGRESS"
|
||||
: runStatus.toString();
|
||||
String startString = formatDateTime(dataModel.getStartTime());
|
||||
|
||||
w.println("<html>");
|
||||
w.println("<head>");
|
||||
w.println(" <title>Summary of Acceptance Tests " + startString
|
||||
+ "</title>");
|
||||
w.println(" <link rel=\"stylesheet\" type=\"text/css\" "
|
||||
+ "href=\"summary.css\">");
|
||||
w.println("</head>");
|
||||
w.println("<body>");
|
||||
w.println();
|
||||
w.println(" <div class=\"heading\">");
|
||||
w.println(" Acceptance test results: " + startString);
|
||||
w.println(" <div class=\"" + runStatus.getHtmlClass()
|
||||
+ " one-word\">" + statusString + "</div>");
|
||||
w.println(" </div>");
|
||||
}
|
||||
|
||||
private void writeStatsSection(PrintWriter w) {
|
||||
String passClass = dataModel.isAnyPasses() ? Status.OK.getHtmlClass()
|
||||
: "";
|
||||
String failClass = dataModel.isAnyFailures() ? Status.ERROR
|
||||
.getHtmlClass() : "";
|
||||
String ignoreClass = dataModel.isAnyIgnores() ? Status.IGNORED
|
||||
.getHtmlClass() : "";
|
||||
|
||||
String start = formatDateTime(dataModel.getStartTime());
|
||||
String end = formatDateTime(dataModel.getEndTime());
|
||||
String elapsed = formatElapsedTime(dataModel.getElapsedTime());
|
||||
|
||||
w.println(" <div class=\"section\">Summary</div>");
|
||||
w.println();
|
||||
w.println(" <table class=\"summary\" cellspacing=\"0\">");
|
||||
w.println(" <tr>");
|
||||
w.println(" <td>");
|
||||
w.println(" <table cellspacing=\"0\">");
|
||||
w.println(" <tr><td>Start time:</td><td>" + start
|
||||
+ "</td></tr>");
|
||||
w.println(" <tr><td>End time:</td><td>" + end + "</td></tr>");
|
||||
w.println(" <tr><td>Elapsed time</td><td>" + elapsed
|
||||
+ "</td></tr>");
|
||||
w.println(" </table>");
|
||||
w.println(" </td>");
|
||||
w.println(" <td>");
|
||||
w.println(" <table class=\"tallys\" cellspacing=\"0\">");
|
||||
w.println(" <tr><th> </th><th>Suites</th><th>Tests</th>");
|
||||
w.println(" <tr class=\"" + passClass
|
||||
+ "\"><td>Passed</td><td>" + dataModel.getPassingSuiteCount()
|
||||
+ "</td><td>" + dataModel.getPassingTestCount() + "</td>");
|
||||
w.println(" <tr class=\"" + failClass
|
||||
+ "\"><td>Failed</td><td>" + dataModel.getFailingSuiteCount()
|
||||
+ "</td><td>" + dataModel.getFailingTestCount() + "</td>");
|
||||
w.println(" <tr class=\"" + ignoreClass
|
||||
+ "\"><td>Ignored</td><td>" + dataModel.getIgnoredSuiteCount()
|
||||
+ "</td><td>" + dataModel.getIgnoredTestCount() + "</td>");
|
||||
if (dataModel.isAnyPending()) {
|
||||
w.println(" <tr class=\"" + Status.PENDING.getHtmlClass()
|
||||
+ "\"><td>Pending</td><td>"
|
||||
+ dataModel.getPendingSuiteCount() + "</td><td>"
|
||||
+ dataModel.getPendingTestCount() + "</td>");
|
||||
}
|
||||
w.println(" <tr><td class=\"total\">Total</td><td>"
|
||||
+ dataModel.getTotalSuiteCount() + "</td><td>"
|
||||
+ dataModel.getTotalTestCount() + "</td>");
|
||||
w.println(" </table>");
|
||||
w.println(" </td>");
|
||||
w.println(" </tr>");
|
||||
w.println(" </table>");
|
||||
w.println();
|
||||
}
|
||||
|
||||
private void writeErrorMessagesSection(PrintWriter w) {
|
||||
String errorClass = Status.ERROR.getHtmlClass();
|
||||
|
||||
w.println(" <div class=section>Errors and warnings</div>");
|
||||
w.println();
|
||||
w.println(" <table cellspacing=\"0\">");
|
||||
|
||||
if ((!logStats.hasErrors()) && (!logStats.hasWarnings())) {
|
||||
w.println(" <tr><td colspan=\"2\">No errors or warnings</td></tr>");
|
||||
} else {
|
||||
for (String e : logStats.getErrors()) {
|
||||
w.println(" <tr class=\"" + errorClass
|
||||
+ "\"><td>ERROR</td><td>" + e + "</td></tr>");
|
||||
}
|
||||
}
|
||||
w.println(" </table>");
|
||||
w.println();
|
||||
}
|
||||
|
||||
private void writeCondensedTable(PrintWriter w) {
|
||||
w.println(" <div class=section>Condensed List</div>");
|
||||
w.println();
|
||||
w.println(" <table class=\"condensed\" cellspacing=\"0\">");
|
||||
for (SuiteData s : dataModel.getAllSuites()) {
|
||||
String sReason = "";
|
||||
if (s.getStatus() == Status.IGNORED) {
|
||||
sReason = dataModel.getReasonForIgnoring(s.getName(), "*");
|
||||
} else if (s.getFailureMessages() != null) {
|
||||
sReason = s.getFailureMessages().getErrout();
|
||||
} else if (s.getStatus() == Status.PENDING) {
|
||||
sReason = Status.PENDING.toString();
|
||||
}
|
||||
|
||||
w.println(" <tr>");
|
||||
w.println(" <td class=\"" + s.getStatus().getHtmlClass()
|
||||
+ "\">");
|
||||
w.println(" <div class=\"suite\">" + outputLink(s)
|
||||
+ "</div>");
|
||||
if (!sReason.isEmpty()) {
|
||||
// The entire class is either failed or pending or ignored.
|
||||
w.println(" <div class=\"reason\">" + sReason + "</div>");
|
||||
} else {
|
||||
// Show the individual tests.
|
||||
for (TestData t : s.getTestMap().values()) {
|
||||
String tClass = t.getStatus().getHtmlClass();
|
||||
String tReason = dataModel.getReasonForIgnoring(
|
||||
s.getName(), t.getTestName());
|
||||
|
||||
w.println(" <div class=\"test " + tClass + "\">");
|
||||
w.println(" " + outputLink(t));
|
||||
if (!tReason.isEmpty()) {
|
||||
w.println(" <div class=\"tReason\">" + tReason
|
||||
+ "</div>");
|
||||
}
|
||||
w.println(" </div>");
|
||||
}
|
||||
}
|
||||
w.println(" </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)
|
||||
+ "\">");
|
||||
w.println(" <table cellspacing=\"0\">");
|
||||
w.println(" <tr><th>Standard Output</th></tr>\n");
|
||||
w.println(" <tr><td><pre>" + output.getStdout()
|
||||
+ "</pre></td></tr>\n");
|
||||
w.println(" </table>");
|
||||
w.println("<br/> <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/> <br/>");
|
||||
w.println();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeIgnoreSection(PrintWriter w) {
|
||||
String warnClass = Status.IGNORED.getHtmlClass();
|
||||
Collection<IgnoredTestInfo> ignoredTests = dataModel
|
||||
.getIgnoredTestInfo();
|
||||
|
||||
w.println(" <div class=section>Ignored</div>");
|
||||
w.println();
|
||||
w.println(" <table class=\"ignored\" cellspacing=\"0\">");
|
||||
w.println(" <tr><th>Suite name</th><th>Test name</th>"
|
||||
+ "<th>Reason for ignoring</th></tr>\n");
|
||||
if (ignoredTests.isEmpty()) {
|
||||
w.println(" <tr><td colspan=\"3\">No tests ignored.</td>"
|
||||
+ "</tr>");
|
||||
} else {
|
||||
for (IgnoredTestInfo info : ignoredTests) {
|
||||
String suiteName = info.suiteName;
|
||||
String testName = info.testName;
|
||||
String reason = dataModel.getReasonForIgnoring(suiteName,
|
||||
testName);
|
||||
|
||||
w.println(" <tr class=\"" + warnClass + "\">");
|
||||
w.println(" <td>" + suiteName + "</td>");
|
||||
w.println(" <td>" + testName + "</td>");
|
||||
w.println(" <td>" + reason + "</td>");
|
||||
w.println(" </tr>");
|
||||
}
|
||||
}
|
||||
w.println(" </table>");
|
||||
w.println();
|
||||
}
|
||||
|
||||
private void writeFooter(PrintWriter w) {
|
||||
w.println(" <div class=section>Log</div>");
|
||||
w.println(" <pre class=\"log\">");
|
||||
|
||||
Reader reader = null;
|
||||
try {
|
||||
reader = new FileReader(parms.getLogFile());
|
||||
char[] buffer = new char[4096];
|
||||
int howMany;
|
||||
while (-1 != (howMany = reader.read(buffer))) {
|
||||
w.write(buffer, 0, howMany);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.println(" </pre>");
|
||||
w.println("</body>");
|
||||
w.println("</html>");
|
||||
}
|
||||
|
||||
private String formatElapsedTime(long elapsed) {
|
||||
if (elapsed == 0) {
|
||||
return "---";
|
||||
}
|
||||
|
||||
long elapsedSeconds = elapsed / 1000L;
|
||||
long seconds = elapsedSeconds % 60L;
|
||||
long elapsedMinutes = elapsedSeconds / 60L;
|
||||
long minutes = elapsedMinutes % 60L;
|
||||
long hours = elapsedMinutes / 60L;
|
||||
|
||||
String elapsedTime = "";
|
||||
if (hours > 0) {
|
||||
elapsedTime += hours + "h ";
|
||||
}
|
||||
if (minutes > 0 || hours > 0) {
|
||||
elapsedTime += minutes + "m ";
|
||||
}
|
||||
elapsedTime += seconds + "s";
|
||||
|
||||
return elapsedTime;
|
||||
}
|
||||
|
||||
private String formatDateTime(long dateTime) {
|
||||
if (dateTime == 0) {
|
||||
return "---";
|
||||
}
|
||||
|
||||
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>";
|
||||
}
|
||||
}
|
||||
}
|
||||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner.output;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats;
|
||||
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;
|
||||
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.
|
||||
*/
|
||||
public class OutputSummaryFormatter {
|
||||
public static final String SUMMARY_HTML_FILENAME = "summary.html";
|
||||
public static final String SUMMARY_CSS_FILENAME = "summary.css";
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat(
|
||||
"yyyy-MM-dd HH:mm:ss");
|
||||
private final SeleniumRunnerParameters parms;
|
||||
|
||||
private LogStats logStats;
|
||||
private DataModel dataModel;
|
||||
|
||||
public OutputSummaryFormatter(SeleniumRunnerParameters parms) {
|
||||
this.parms = parms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a summary HTML file from the info contained in this log file and
|
||||
* these suite outputs.
|
||||
*/
|
||||
public void format(LogStats logStats, DataModel dataModel) {
|
||||
this.logStats = logStats;
|
||||
this.dataModel = dataModel;
|
||||
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
copyCssFile();
|
||||
|
||||
File outputFile = new File(parms.getOutputDirectory(),
|
||||
SUMMARY_HTML_FILENAME);
|
||||
writer = new PrintWriter(outputFile);
|
||||
|
||||
writeHeader(writer);
|
||||
writeStatsSection(writer);
|
||||
writeErrorMessagesSection(writer);
|
||||
writeCondensedTable(writer);
|
||||
writeIgnoreSection(writer);
|
||||
writeSuiteErrorMessagesSection(writer);
|
||||
writeFooter(writer);
|
||||
} catch (IOException e) {
|
||||
// There is no appeal for any problems here. Just report them.
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the CSS file into the output directory.
|
||||
*/
|
||||
private void copyCssFile() {
|
||||
InputStream cssStream = this.getClass().getResourceAsStream(
|
||||
SUMMARY_CSS_FILENAME);
|
||||
if (cssStream == null) {
|
||||
System.out.println("Couldn't find the CSS file: '"
|
||||
+ SUMMARY_CSS_FILENAME + "'");
|
||||
} else {
|
||||
File cssTarget = new File(parms.getOutputDirectory(),
|
||||
SUMMARY_CSS_FILENAME);
|
||||
try {
|
||||
FileHelper.copy(cssStream, cssTarget);
|
||||
cssStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeHeader(PrintWriter w) {
|
||||
Status runStatus = dataModel.getRunStatus();
|
||||
String statusString = (runStatus == Status.PENDING) ? "IN PROGRESS"
|
||||
: runStatus.toString();
|
||||
String startString = formatDateTime(dataModel.getStartTime());
|
||||
|
||||
w.println("<html>");
|
||||
w.println("<head>");
|
||||
w.println(" <title>Summary of Acceptance Tests " + startString
|
||||
+ "</title>");
|
||||
w.println(" <link rel=\"stylesheet\" type=\"text/css\" "
|
||||
+ "href=\"summary.css\">");
|
||||
w.println("</head>");
|
||||
w.println("<body>");
|
||||
w.println();
|
||||
w.println(" <div class=\"heading\">");
|
||||
w.println(" Acceptance test results: " + startString);
|
||||
w.println(" <div class=\"" + runStatus.getHtmlClass()
|
||||
+ " one-word\">" + statusString + "</div>");
|
||||
w.println(" </div>");
|
||||
}
|
||||
|
||||
private void writeStatsSection(PrintWriter w) {
|
||||
String passClass = dataModel.isAnyPasses() ? Status.OK.getHtmlClass()
|
||||
: "";
|
||||
String failClass = dataModel.isAnyFailures() ? Status.ERROR
|
||||
.getHtmlClass() : "";
|
||||
String ignoreClass = dataModel.isAnyIgnores() ? Status.IGNORED
|
||||
.getHtmlClass() : "";
|
||||
|
||||
String start = formatDateTime(dataModel.getStartTime());
|
||||
String end = formatDateTime(dataModel.getEndTime());
|
||||
String elapsed = formatElapsedTime(dataModel.getElapsedTime());
|
||||
|
||||
w.println(" <div class=\"section\">Summary</div>");
|
||||
w.println();
|
||||
w.println(" <table class=\"summary\" cellspacing=\"0\">");
|
||||
w.println(" <tr>");
|
||||
w.println(" <td>");
|
||||
w.println(" <table cellspacing=\"0\">");
|
||||
w.println(" <tr><td>Start time:</td><td>" + start
|
||||
+ "</td></tr>");
|
||||
w.println(" <tr><td>End time:</td><td>" + end + "</td></tr>");
|
||||
w.println(" <tr><td>Elapsed time</td><td>" + elapsed
|
||||
+ "</td></tr>");
|
||||
w.println(" </table>");
|
||||
w.println(" </td>");
|
||||
w.println(" <td>");
|
||||
w.println(" <table class=\"tallys\" cellspacing=\"0\">");
|
||||
w.println(" <tr><th> </th><th>Suites</th><th>Tests</th>");
|
||||
w.println(" <tr class=\"" + passClass
|
||||
+ "\"><td>Passed</td><td>" + dataModel.getPassingSuiteCount()
|
||||
+ "</td><td>" + dataModel.getPassingTestCount() + "</td>");
|
||||
w.println(" <tr class=\"" + failClass
|
||||
+ "\"><td>Failed</td><td>" + dataModel.getFailingSuiteCount()
|
||||
+ "</td><td>" + dataModel.getFailingTestCount() + "</td>");
|
||||
w.println(" <tr class=\"" + ignoreClass
|
||||
+ "\"><td>Ignored</td><td>" + dataModel.getIgnoredSuiteCount()
|
||||
+ "</td><td>" + dataModel.getIgnoredTestCount() + "</td>");
|
||||
if (dataModel.isAnyPending()) {
|
||||
w.println(" <tr class=\"" + Status.PENDING.getHtmlClass()
|
||||
+ "\"><td>Pending</td><td>"
|
||||
+ dataModel.getPendingSuiteCount() + "</td><td>"
|
||||
+ dataModel.getPendingTestCount() + "</td>");
|
||||
}
|
||||
w.println(" <tr><td class=\"total\">Total</td><td>"
|
||||
+ dataModel.getTotalSuiteCount() + "</td><td>"
|
||||
+ dataModel.getTotalTestCount() + "</td>");
|
||||
w.println(" </table>");
|
||||
w.println(" </td>");
|
||||
w.println(" </tr>");
|
||||
w.println(" </table>");
|
||||
w.println();
|
||||
}
|
||||
|
||||
private void writeErrorMessagesSection(PrintWriter w) {
|
||||
String errorClass = Status.ERROR.getHtmlClass();
|
||||
|
||||
w.println(" <div class=section>Errors and warnings</div>");
|
||||
w.println();
|
||||
w.println(" <table cellspacing=\"0\">");
|
||||
|
||||
if ((!logStats.hasErrors()) && (!logStats.hasWarnings())) {
|
||||
w.println(" <tr><td colspan=\"2\">No errors or warnings</td></tr>");
|
||||
} else {
|
||||
for (String e : logStats.getErrors()) {
|
||||
w.println(" <tr class=\"" + errorClass
|
||||
+ "\"><td>ERROR</td><td>" + e + "</td></tr>");
|
||||
}
|
||||
}
|
||||
w.println(" </table>");
|
||||
w.println();
|
||||
}
|
||||
|
||||
private void writeCondensedTable(PrintWriter w) {
|
||||
w.println(" <div class=section>Condensed List</div>");
|
||||
w.println();
|
||||
w.println(" <table class=\"condensed\" cellspacing=\"0\">");
|
||||
for (SuiteData s : dataModel.getAllSuites()) {
|
||||
String sReason = "";
|
||||
if (s.getStatus() == Status.IGNORED) {
|
||||
sReason = dataModel.getReasonForIgnoring(s.getName(), "*");
|
||||
} else if (s.getFailureMessages() != null) {
|
||||
sReason = s.getFailureMessages().getErrout();
|
||||
} else if (s.getStatus() == Status.PENDING) {
|
||||
sReason = Status.PENDING.toString();
|
||||
}
|
||||
|
||||
w.println(" <tr>");
|
||||
w.println(" <td class=\"" + s.getStatus().getHtmlClass()
|
||||
+ "\">");
|
||||
w.println(" <div class=\"suite\">" + outputLink(s)
|
||||
+ "</div>");
|
||||
if (!sReason.isEmpty()) {
|
||||
// The entire class is either failed or pending or ignored.
|
||||
w.println(" <div class=\"reason\">" + sReason + "</div>");
|
||||
} else {
|
||||
// Show the individual tests.
|
||||
for (TestData t : s.getTestMap().values()) {
|
||||
String tClass = t.getStatus().getHtmlClass();
|
||||
String tReason = dataModel.getReasonForIgnoring(
|
||||
s.getName(), t.getTestName());
|
||||
|
||||
w.println(" <div class=\"test " + tClass + "\">");
|
||||
w.println(" " + outputLink(t));
|
||||
if (!tReason.isEmpty()) {
|
||||
w.println(" <div class=\"tReason\">" + tReason
|
||||
+ "</div>");
|
||||
}
|
||||
w.println(" </div>");
|
||||
}
|
||||
}
|
||||
w.println(" </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)
|
||||
+ "\">");
|
||||
w.println(" <table cellspacing=\"0\">");
|
||||
w.println(" <tr><th>Standard Output</th></tr>\n");
|
||||
w.println(" <tr><td><pre>" + output.getStdout()
|
||||
+ "</pre></td></tr>\n");
|
||||
w.println(" </table>");
|
||||
w.println("<br/> <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/> <br/>");
|
||||
w.println();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeIgnoreSection(PrintWriter w) {
|
||||
String warnClass = Status.IGNORED.getHtmlClass();
|
||||
Collection<IgnoredTestInfo> ignoredTests = dataModel
|
||||
.getIgnoredTestInfo();
|
||||
|
||||
w.println(" <div class=section>Ignored</div>");
|
||||
w.println();
|
||||
w.println(" <table class=\"ignored\" cellspacing=\"0\">");
|
||||
w.println(" <tr><th>Suite name</th><th>Test name</th>"
|
||||
+ "<th>Reason for ignoring</th></tr>\n");
|
||||
if (ignoredTests.isEmpty()) {
|
||||
w.println(" <tr><td colspan=\"3\">No tests ignored.</td>"
|
||||
+ "</tr>");
|
||||
} else {
|
||||
for (IgnoredTestInfo info : ignoredTests) {
|
||||
String suiteName = info.suiteName;
|
||||
String testName = info.testName;
|
||||
String reason = dataModel.getReasonForIgnoring(suiteName,
|
||||
testName);
|
||||
|
||||
w.println(" <tr class=\"" + warnClass + "\">");
|
||||
w.println(" <td>" + suiteName + "</td>");
|
||||
w.println(" <td>" + testName + "</td>");
|
||||
w.println(" <td>" + reason + "</td>");
|
||||
w.println(" </tr>");
|
||||
}
|
||||
}
|
||||
w.println(" </table>");
|
||||
w.println();
|
||||
}
|
||||
|
||||
private void writeFooter(PrintWriter w) {
|
||||
w.println(" <div class=section>Log</div>");
|
||||
w.println(" <pre class=\"log\">");
|
||||
|
||||
Reader reader = null;
|
||||
try {
|
||||
reader = new FileReader(parms.getLogFile());
|
||||
char[] buffer = new char[4096];
|
||||
int howMany;
|
||||
while (-1 != (howMany = reader.read(buffer))) {
|
||||
w.write(buffer, 0, howMany);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.println(" </pre>");
|
||||
w.println("</body>");
|
||||
w.println("</html>");
|
||||
}
|
||||
|
||||
private String formatElapsedTime(long elapsed) {
|
||||
if (elapsed == 0) {
|
||||
return "---";
|
||||
}
|
||||
|
||||
long elapsedSeconds = elapsed / 1000L;
|
||||
long seconds = elapsedSeconds % 60L;
|
||||
long elapsedMinutes = elapsedSeconds / 60L;
|
||||
long minutes = elapsedMinutes % 60L;
|
||||
long hours = elapsedMinutes / 60L;
|
||||
|
||||
String elapsedTime = "";
|
||||
if (hours > 0) {
|
||||
elapsedTime += hours + "h ";
|
||||
}
|
||||
if (minutes > 0 || hours > 0) {
|
||||
elapsedTime += minutes + "m ";
|
||||
}
|
||||
elapsedTime += seconds + "s";
|
||||
|
||||
return elapsedTime;
|
||||
}
|
||||
|
||||
private String formatDateTime(long dateTime) {
|
||||
if (dateTime == 0) {
|
||||
return "---";
|
||||
}
|
||||
|
||||
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>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue