Merge branch 'maint-rel-1.6' into develop

Conflicts:
	utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/CommandRunnerException.java
	utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/FatalException.java
	utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/SeleniumRunnerParameters.java
	utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/datamodel/DataModel.java
	utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/output/OutputSummaryFormatter.java
This commit is contained in:
Jim Blake 2014-04-04 16:23:57 -04:00
commit cfce2f5a5c
30 changed files with 0 additions and 3574 deletions

View file

@ -1,137 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<!-- ======================================================================
Build script for the Selenium test runner.
====================================================================== -->
<project name="acceptanceTests" default="describe" basedir=".">
<property name="source.dir" location="${basedir}/src" />
<property name="lib.dir" location="${basedir}/lib" />
<property name="test.dir" location="${basedir}/test" />
<property name="build.dir" value="${basedir}/.build" />
<!-- =================================
target: describe
================================= -->
<target name="describe" description="--> Describe the targets (this is the default).">
<echo>
all - Runs "clean", then "run".
clean - Delete all artifacts so the next build will be from scratch.
compile - Compile the Java source files.
test - Compile and run the JUnit tests.
run - Run the tester.
</echo>
</target>
<!-- =================================
target: all
================================= -->
<target name="all"
depends="clean, run"
description="--> Do a clean build and execute the selenium runner.">
</target>
<!-- =================================
target: clean
================================= -->
<target name="clean" description="--> Remove any artifacts from previous builds.">
<delete dir="${build.dir}" />
</target>
<!-- - - - - - - - - - - - - - - - - -
target: prepare
- - - - - - - - - - - - - - - - - -->
<target name="prepare">
<mkdir dir="${build.dir}" />
</target>
<!-- =================================
target: compile
================================= -->
<target name="compile" depends="prepare" description="--> Compile the selenium runner.">
<javac srcdir="${source.dir}"
destdir="${build.dir}"
debug="true"
deprecation="true"
encoding="UTF8"
optimize="true"
source="1.6">
<classpath>
<pathelement location="${lib.dir}/commons-httpclient-3.1.jar" />
</classpath>
</javac>
</target>
<!-- =================================
target: test
================================= -->
<target name="test"
depends="compile"
description="--> Run unit tests against the selenium runner code.">
<javac srcdir="${test.dir}"
destdir="${build.dir}"
debug="true"
deprecation="true"
encoding="UTF8"
optimize="false"
source="1.6">
<classpath>
<pathelement location="${build.dir}" />
<pathelement location="${lib.dir}/junit-4.8.1.jar" />
</classpath>
</javac>
<junit printsummary="no" haltonfailure="yes">
<classpath>
<pathelement location="${build.dir}" />
<pathelement location="${lib.dir}/junit-4.8.1.jar" />
</classpath>
<formatter type="brief" />
<batchtest fork="yes" todir="${build.dir}">
<fileset dir="test">
<include name="**/*Test*.java" />
</fileset>
</batchtest>
</junit>
</target>
<!-- =================================
target: run
================================= -->
<target name="run" depends="test" description="--> Execute the selenium runner.">
<property name="acceptance.dir" location="${basedir}" />
<property name="acceptance.properties.file"
location="${acceptance.dir}/acceptance_tests.properties" />
<condition property="acceptance.interactive.arg" value="interactive" else="">
<not>
<istrue value="${acceptance.batch}" />
</not>
</condition>
<java classname="edu.cornell.mannlib.vitro.utilities.testrunner.SeleniumRunner"
fork="yes"
dir="${acceptance.dir}"
failonerror="true">
<classpath>
<pathelement location="${build.dir}" />
<pathelement location="${source.dir}" />
<pathelement location="${lib.dir}/commons-httpclient-3.1.jar" />
<pathelement location="${lib.dir}/commons-logging-1.1.1.jar" />
<pathelement location="${lib.dir}/commons-codec-1.3.jar" />
</classpath>
<arg file="${acceptance.properties.file}" />
<arg value="${acceptance.interactive.arg}" />
</java>
</target>
</project>

View file

@ -1,36 +0,0 @@
#
# These properties tell how to set up Selenium to run a test suite.
#
website_url = http://localhost:8080/vivo/
suite_parent_directories = suites
output_directory = output
user_extensions_path = selenium/user-extensions.js
firefox_profile_template_path =
suite_timeout_limit = 240
selenium_jar_path = selenium/selenium-server.jar
#
# These properties are needed to cleanse the data model between test suites.
#
# -- Windows commands for Tomcat - require startup.bat and shutdown.bat, which may not be part of the installation.
#tomcat_stop_command = "C:\\Program Files\\Apache Software Foundation\\Tomcat 6.0\\bin\\shutdown.bat"
#tomcat_start_command = "C:\\Program Files\\Apache Software Foundation\\Tomcat 6.0\\bin\\startup.bat"
#
# -- Linux commands for Tomcat
#tomcat_stop_command = /usr/local/tomcat/bin/shutdown.sh
#tomcat_start_command = /usr/local/tomcat/bin/startup.bat
tomcat_stop_command = "C:\\Program Files\\Apache Software Foundation\\Tomcat 6.0\\bin\\shutdown.bat"
tomcat_start_command = "C:\\Program Files\\Apache Software Foundation\\Tomcat 6.0\\bin\\startup.bat"
tomcat_stop_delay = 10
tomcat_start_delay = 30
mysql_username = vivoUser
mysql_password = vivoPass
mysql_dumpfile = /eclipseVitroWorkspace/vivo/utilities/acceptance-tests/test-model/testmodeldump.sql
mysql_db_name = vivo
upload_directory = /Vivoweb_Stuff/test_deploy/uploads
#
# These properties control the output formatting of the tests.
#
ignored_tests_file = /eclipseVitroWorkspace/vivo/utilities/acceptance-tests/suites/ignored_tests.txt

View file

@ -1,3 +0,0 @@
Here is an excellent description of how to create a Firefox profile for use when running Selenium tests.
http://girliemangalo.wordpress.com/2009/02/05/creating-firefox-profile-for-your-selenium-rc-tests/

View file

@ -1,184 +0,0 @@
/* $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.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
/**
* <p>
* A harness that runs a system-level command.
* </p>
* <p>
* No provision is made for standard input.
* </p>
* <p>
* The standard output and standard error streams are asynchronously read, so
* the sub-process will not block on full buffers. Warning: if either of these
* streams contain more data than can fit into a String, then we will have a
* problem.
* </p>
*
* @author jblake
*/
public class CommandRunner {
private Integer returnCode;
private String stdOut = "";
private String stdErr = "";
private File workingDirectory;
/* Gets informed of output as it arrives. Never null. */
private final Listener listener;
private final Map<String, String> environmentAdditions = new HashMap<String, String>();
public CommandRunner(SeleniumRunnerParameters parms) {
this.listener = parms.getListener();
}
/** Set the directory that the command will run in. */
public void setWorkingDirectory(File workingDirectory) {
this.workingDirectory = workingDirectory;
}
/** Add (or replace) any environment variable. */
public void setEnvironmentVariable(String key, String value) {
this.environmentAdditions.put(key, value);
}
/**
* Run the command.
*
* @param command
* a list containing the operating system program and its
* arguments. See
* {@link java.lang.ProcessBuilder#ProcessBuilder(List)}.
*/
public void run(List<String> command) throws CommandRunnerException {
listener.subProcessStart(command);
try {
ProcessBuilder builder = new ProcessBuilder(command);
if (workingDirectory != null) {
builder.directory(workingDirectory);
}
if (!environmentAdditions.isEmpty()) {
builder.environment().putAll(this.environmentAdditions);
}
Process process = builder.start();
StreamEater outputEater = new StreamEater(process.getInputStream(),
false);
StreamEater errorEater = new StreamEater(process.getErrorStream(),
true);
this.returnCode = process.waitFor();
outputEater.join(1000);
outputEater.stopRunning();
this.stdOut = outputEater.getContents();
errorEater.join(1000);
errorEater.stopRunning();
this.stdErr = errorEater.getContents();
} catch (IOException e) {
throw new CommandRunnerException(
"Exception when handling sub-process:", e);
} catch (InterruptedException e) {
throw new CommandRunnerException(
"Exception when handling sub-process:", e);
}
listener.subProcessStop(command);
}
public int getReturnCode() {
if (returnCode == null) {
throw new IllegalStateException("Return code is not available.");
}
return returnCode;
}
public String getStdErr() {
return stdErr;
}
public String getStdOut() {
return stdOut;
}
/**
* A thread that reads an InputStream until it reaches end of file, then
* closes the stream. Designated as error stream or not, so it can tell the
* logger.
*/
private class StreamEater extends Thread {
private final InputStream stream;
private final boolean isError;
private volatile boolean running;
private final StringWriter contents = new StringWriter();
private final byte[] buffer = new byte[4096];
public StreamEater(InputStream stream, boolean isError) {
this.stream = stream;
this.isError = isError;
this.running = true;
this.start();
}
public void stopRunning() {
this.running = false;
}
@Override
public void run() {
try {
int howMany = 0;
while (running) {
howMany = stream.read(buffer);
if (howMany > 0) {
String string = new String(buffer, 0, howMany);
contents.write(string);
if (isError) {
listener.subProcessErrout(string);
} else {
listener.subProcessStdout(string);
}
} else if (howMany == 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String getContents() {
return contents.toString();
}
}
}

View file

@ -1,167 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
/**
* Some utility methods for dealing with files and directories.
*/
public class FileHelper {
/**
* Delete a file. If it can't be deleted, complain.
*/
public static void deleteFile(File file) throws IOException {
if (file.exists()) {
file.delete();
}
if (!file.exists()) {
return;
}
/*
* If we were unable to delete the file, is it because it's a non-empty
* directory?
*/
if (file.isDirectory()) {
final StringBuffer message = new StringBuffer(
"Can't delete directory '" + file.getPath() + "'\n");
file.listFiles(new FileFilter() {
public boolean accept(File pathname) {
message.append(" contains file '" + pathname + "'\n");
return true;
}
});
throw new IOException(message.toString().trim());
} else {
throw new IOException("Unable to delete file '" + file.getPath()
+ "'");
}
}
/**
* Delete all of the files in a directory, any sub-directories, and the
* directory itself.
*/
public static void purgeDirectoryRecursively(File directory)
throws IOException {
File[] files = directory.listFiles();
for (File file : files) {
if (file.isDirectory()) {
purgeDirectoryRecursively(file);
} else {
deleteFile(file);
}
}
deleteFile(directory);
}
/**
* Confirm that this is an existing, readable file.
*/
public static void checkReadableFile(File file, String label) {
if (!file.exists()) {
throw new IllegalArgumentException(label + " does not exist.");
}
if (!file.isFile()) {
throw new IllegalArgumentException(label + " is not a file.");
}
if (!file.canRead()) {
throw new IllegalArgumentException(label + " is not readable.");
}
}
/**
* Get the name of this file, without the extension.
*/
public static String baseName(File file) {
String name = file.getName();
int periodHere = name.indexOf('.');
if (periodHere == -1) {
return name;
} else {
return name.substring(0, periodHere);
}
}
/**
* Copy the contents of a file to a new location. If the target file already
* exists, it will be over-written.
*/
public static void copy(File source, File target) throws IOException {
InputStream input = null;
try {
input = new FileInputStream(source);
copy(input, target);
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
/**
* Copy the contents of an InputStream to a file. If the target file already
* exists, it will be over-written. Doesn't close the input stream.
*/
public static void copy(InputStream input, File target) throws IOException {
OutputStream output = null;
try {
output = new FileOutputStream(target);
int howMany;
byte[] buffer = new byte[4096];
while (-1 != (howMany = input.read(buffer))) {
output.write(buffer, 0, howMany);
}
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
/**
* Suck all the data from a {@link Reader} into a {@link String}.
*/
public static String readAll(Reader reader) throws IOException {
StringBuilder result = new StringBuilder();
BufferedReader buffered = new BufferedReader(reader);
char[] chunk = new char[4096];
int howMany;
try {
while (-1 != (howMany = buffered.read(chunk))) {
result.append(chunk, 0, howMany);
}
} finally {
reader.close();
}
return result.toString();
}
/** No need to instantiate this, since all methods are static. */
private FileHelper() {
// Nothing to initialize.
}
}

View file

@ -1,216 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A list of tests to be ignored - they are expected to fail, and their failure
* is logged with a warning, not an error.
*/
public class IgnoredTests {
public static final IgnoredTests EMPTY_LIST = new IgnoredTests();
private final String filePath;
private final List<IgnoredTestInfo> tests;
/**
* Create an empty instance.
*/
private IgnoredTests() {
this.filePath = "NO FILE";
this.tests = Collections.emptyList();
}
/**
* <p>
* Parse the file of ignored tests.
* </p>
* <p>
* Ignore any blank line, or any line starting with '#' or '!'
* </p>
* <p>
* Each other line describes an ignored test. The line contains the suite
* name, a comma (with optional space), the test name (with optional space)
* and optionally a comment, starting with a '#'.
* </p>
* If the test name is an asterisk '*', then the entire suite will be
* ignored.
* <p>
* </p>
*/
public IgnoredTests(File file) {
this.filePath = file.getAbsolutePath();
List<IgnoredTestInfo> tests = new ArrayList<IgnoredTestInfo>();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
String line;
while (null != (line = reader.readLine())) {
line = line.trim();
if ((line.length() == 0) || (line.charAt(0) == '#')
|| (line.charAt(0) == '!')) {
continue;
}
Pattern p = Pattern.compile("([^,#]+),([^,#]+)(#(.*))?");
Matcher m = p.matcher(line);
if (m.matches()) {
tests.add(new IgnoredTestInfo(m.group(1), m.group(2), m
.group(4)));
} else {
throw new FatalException(
"Bad format on ignored test description: '" + line
+ "', should be "
+ "<suite name>, <test name> [# comment]");
}
}
} catch (IOException e) {
throw new FatalException(
"Failed to parse the list of ignored tests: '"
+ file.getPath() + "'", e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
this.tests = Collections.unmodifiableList(tests);
}
/**
* Get a copy of the whole list.
*/
public List<IgnoredTestInfo> getList() {
return new ArrayList<IgnoredTestInfo>(tests);
}
/**
* Is this test ignored or not?
*/
public boolean isIgnored(String suiteName, String testName) {
for (IgnoredTestInfo test : tests) {
if (test.matches(suiteName, testName)) {
return true;
}
}
return false;
}
/**
* Is this entire suite ignored?
*/
public boolean isIgnored(String suiteName) {
for (IgnoredTestInfo test : tests) {
if (test.matchesEntireSuite(suiteName)) {
return true;
}
}
return false;
}
/**
* If this test is ignored, what is the reason? If not, return an empty
* string.
*/
public String getReasonForIgnoring(String suiteName, String testName) {
for (IgnoredTestInfo test : tests) {
if (test.matches(suiteName, testName)) {
return test.comment;
}
}
return "";
}
/**
* If this suite is ignored, what is the reason? If not, return an empty
* string.
*/
public String getReasonForIgnoring(String suiteName) {
for (IgnoredTestInfo test : tests) {
if (test.matchesEntireSuite(suiteName)) {
return test.comment;
}
}
return "";
}
public String toString() {
String s = " ignored tests from " + this.filePath + "\n";
for (IgnoredTestInfo test : tests) {
s += " " + test.suiteName + ", " + test.testName + "\n";
}
return s;
}
/**
* Encapsulates a line from the file with suite name, test name, and
* comment.
*/
public static class IgnoredTestInfo {
public final String suiteName;
public final String testName;
public final String comment;
public IgnoredTestInfo(String suiteName, String testName, String comment) {
this.suiteName = suiteName.trim();
this.testName = testName.trim();
this.comment = (comment == null) ? "" : comment.trim();
}
public boolean matches(String suiteName, String testName) {
return this.suiteName.equals(suiteName)
&& (this.testName.equals(testName) || this.testName
.equals("*"));
}
public boolean matchesEntireSuite(String suiteName) {
return this.suiteName.equals(suiteName)
&& this.testName.equals("*");
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
if (!obj.getClass().equals(this.getClass())) {
return false;
}
IgnoredTestInfo that = (IgnoredTestInfo) obj;
return this.suiteName.equals(that.suiteName)
&& this.testName.equals(that.testName)
&& this.comment.equals(that.comment);
}
@Override
public int hashCode() {
return suiteName.hashCode() ^ testName.hashCode()
^ comment.hashCode();
}
@Override
public String toString() {
return "IgnoredTestInfo['" + suiteName + "', '" + testName + "', '"
+ comment + "']";
}
}
}

View file

@ -1,98 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Extract any summary information from the log file.
*/
public class LogStats {
public static LogStats EMPTY_LOG_STATS = new LogStats();
private static final Pattern SUITE_NAME_PATTERN = Pattern
.compile("Running suite (.*)");
private static final Pattern ERROR_PATTERN = Pattern
.compile("ERROR\\s+(.*)");
private static final Pattern WARNING_PATTERN = Pattern
.compile("WARN\\s+(.*)");
/**
* Factory method.
*/
public static LogStats parse(File logFile) {
return new LogStats(logFile);
}
private final List<String> suiteNames = new ArrayList<String>();
private final List<String> errors = new ArrayList<String>();
private final List<String> warnings = new ArrayList<String>();
private LogStats() {
// Nothing to initialize for empty instance.
}
private LogStats(File logFile) {
BufferedReader reader = null;
String line;
try {
reader = new BufferedReader(new FileReader(logFile));
while (null != (line = reader.readLine())) {
Matcher m;
m = SUITE_NAME_PATTERN.matcher(line);
if (m.matches()) {
suiteNames.add(m.group(1));
} else {
m = ERROR_PATTERN.matcher(line);
if (m.matches()) {
errors.add(m.group(1));
} else {
m = WARNING_PATTERN.matcher(line);
if (m.matches()) {
warnings.add(m.group(1));
}
}
}
}
} catch (IOException e) {
// Can't give up - I need to create as much output as I can.
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public boolean hasErrors() {
return !errors.isEmpty();
}
public Collection<String> getErrors() {
return Collections.unmodifiableCollection(errors);
}
public boolean hasWarnings() {
return !warnings.isEmpty();
}
public Collection<String> getWarnings() {
return Collections.unmodifiableCollection(warnings);
}
}

View file

@ -1,157 +0,0 @@
/* $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.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
/**
* Resets the RDF-Model to a known state, in preparation for the next Selenium
* test suite.
*/
public class ModelCleaner {
public static final String TEST_USER_ONTOLOGY_FILENAME = "test-user-model.owl";
private final ModelCleanerProperties properties;
private final TomcatController tomcatController;
private final CommandRunner runner;
private final Listener listener;
public ModelCleaner(SeleniumRunnerParameters parms,
TomcatController tomcatController) {
this.properties = parms.getModelCleanerProperties();
this.listener = parms.getListener();
this.runner = new CommandRunner(parms);
this.tomcatController = tomcatController;
sanityCheck();
}
private void sanityCheck() {
executeMysqlStatement("show databases;");
int returnCode = runner.getReturnCode();
if (returnCode != 0) {
throw new FatalException(
"sanityCheck: Failed to execute a MySQL statement: "
+ "return code=" + returnCode);
}
}
/**
* Reset the RDF-Model to a known state, according to the parameters in the
* properties file.
*
* @throws CommandRunnerException
* if a problem occurs in a sub-process.
*/
public void clean() throws CommandRunnerException {
tomcatController.stopTheWebapp();
insertTheUserFile();
recreateTheDatabase();
tomcatController.startTheWebapp();
removeTheUserFile();
}
/**
* Copy the test data ontology file into the auth area, so we get our
* pre-defined admin user.
*/
private void insertTheUserFile() {
InputStream userOntologyStream = this.getClass().getResourceAsStream(
TEST_USER_ONTOLOGY_FILENAME);
if (userOntologyStream == null) {
throw new FatalException(
"Couldn't find the Test User Ontology file: '"
+ TEST_USER_ONTOLOGY_FILENAME + "'");
}
File userOntologyTarget = figureUserOntologyTarget();
try {
FileHelper.copy(userOntologyStream, userOntologyTarget);
userOntologyStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Drop the database and create it again, empty.
*/
private void recreateTheDatabase() {
String mysqlStatement = "drop database " + properties.getMysqlDbName()
+ "; create database " + properties.getMysqlDbName()
+ " character set utf8;";
listener.dropDatabaseStarting(mysqlStatement);
executeMysqlStatement(mysqlStatement);
int returnCode = runner.getReturnCode();
if (returnCode != 0) {
listener.dropDatabaseFailed(returnCode);
throw new FatalException("dropDatabase() failed: return code="
+ returnCode);
}
listener.dropDatabaseComplete();
}
/**
* Remove the test data ontology file, so we leave no trace.
*/
private void removeTheUserFile() {
File userOntologyTarget = figureUserOntologyTarget();
userOntologyTarget.delete();
if (userOntologyTarget.exists()) {
listener.logWarning("Failed to delete the test data ontology "
+ "file: '" + TEST_USER_ONTOLOGY_FILENAME + "'");
}
}
/**
* Tell MySQL to execute this statement. If it fails, throw a fatal
* exception.
*/
private void executeMysqlStatement(String mysqlStatement) {
List<String> cmd = new ArrayList<String>();
cmd.add("mysql");
cmd.add("--user=" + properties.getMysqlUsername());
cmd.add("--password=" + properties.getMysqlPassword());
cmd.add("--database=" + properties.getMysqlDbName());
cmd.add("--execute=" + mysqlStatement);
try {
runner.run(cmd);
} catch (CommandRunnerException e) {
throw new FatalException(e);
}
}
/**
* Figure out where the test data ontology file should go. C:\Program
* Files\Apache Software Foundation\Tomcat
* 6.0\webapps\vivo\WEB-INF\ontologies\auth
*/
private File figureUserOntologyTarget() {
File webappDirectory = properties.getWebappDirectory();
File authDirectory = new File(webappDirectory,
"WEB-INF/ontologies/auth");
if (!authDirectory.exists()) {
throw new FatalException("Target directory for the test data "
+ "ontology file doesn't exist. Webapp directory is '"
+ webappDirectory + "', target directory is '"
+ authDirectory + "'");
}
return new File(authDirectory, TEST_USER_ONTOLOGY_FILENAME);
}
static String convertBackslashes(File file) {
return file.getPath().replace("\\", "/");
}
}

View file

@ -1,120 +0,0 @@
/* $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.util.Properties;
/**
* Hold the runtime properties that pertain specifically to cleaning the data
* model.
*/
public class ModelCleanerProperties {
public static final String PROP_MYSQL_USERNAME = "mysql_username";
public static final String PROP_MYSQL_PASSWORD = "mysql_password";
public static final String PROP_MYSQL_DB_NAME = "mysql_db_name";
public static final String PROP_WEBAPP_DIRECTORY = "vivo_webapp_directory";
public static final String PROP_TOMCAT_CHECK_READY_COMMAND = "tomcat_check_ready_command";
public static final String PROP_TOMCAT_STOP_COMMAND = "tomcat_stop_command";
public static final String PROP_TOMCAT_START_COMMAND = "tomcat_start_command";
private final String mysqlUsername;
private final String mysqlPassword;
private final String mysqlDbName;
private final File webappDirectory;
private final String tomcatCheckReadyCommand;
private final String tomcatStopCommand;
private final String tomcatStartCommand;
/**
* Confirm that we have the expected properties, and that their values seem
* reasonable.
*/
public ModelCleanerProperties(Properties props) {
this.mysqlUsername = getRequiredProperty(props, PROP_MYSQL_USERNAME);
this.mysqlPassword = getRequiredProperty(props, PROP_MYSQL_PASSWORD);
this.mysqlDbName = getRequiredProperty(props, PROP_MYSQL_DB_NAME);
this.webappDirectory = confirmWebappDirectory(props);
this.tomcatCheckReadyCommand = getRequiredProperty(props,
PROP_TOMCAT_CHECK_READY_COMMAND);
this.tomcatStopCommand = getRequiredProperty(props,
PROP_TOMCAT_STOP_COMMAND);
this.tomcatStartCommand = getRequiredProperty(props,
PROP_TOMCAT_START_COMMAND);
}
public String getMysqlUsername() {
return mysqlUsername;
}
public String getMysqlPassword() {
return mysqlPassword;
}
public String getMysqlDbName() {
return mysqlDbName;
}
public File getWebappDirectory() {
return webappDirectory;
}
public String getTomcatCheckReadyCommand() {
return tomcatCheckReadyCommand;
}
public String getTomcatStopCommand() {
return tomcatStopCommand;
}
public String getTomcatStartCommand() {
return tomcatStartCommand;
}
/**
* 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;
}
/**
* The dumpfile parameter must point to an existing directory.
*/
private File confirmWebappDirectory(Properties props) {
String filename = getRequiredProperty(props, PROP_WEBAPP_DIRECTORY);
File webappDirectory = new File(filename);
if (!webappDirectory.exists()) {
throw new IllegalArgumentException("Invalid value for '"
+ PROP_WEBAPP_DIRECTORY + "': directory '" + filename
+ "' does not exist.");
}
if (!webappDirectory.isDirectory()) {
throw new IllegalArgumentException("Invalid value for '"
+ PROP_WEBAPP_DIRECTORY + "': '" + filename
+ "' is not a directory.");
}
if (!webappDirectory.canWrite()) {
throw new IllegalArgumentException("Invalid value for '"
+ PROP_WEBAPP_DIRECTORY + "': directory '" + filename
+ "' is not writeable.");
}
return webappDirectory;
}
public String toString() {
return "\n mysqlUsername: " + mysqlUsername
+ "\n mysqlPassword: " + mysqlPassword
+ "\n mysqlDbName: " + mysqlDbName
+ "\n webappDirectory: " + webappDirectory;
}
}

View file

@ -1,205 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner;
import static edu.cornell.mannlib.vitro.utilities.testrunner.SeleniumRunnerParameters.LOGFILE_NAME;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.DataModel;
import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteContents;
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputManager;
/**
* Run the Selenium test suites. Provide the properties file and perhaps an
* "interactive" flag.
*/
public class SeleniumRunner {
private final SeleniumRunnerParameters parms;
private final Listener listener;
private final UploadAreaCleaner uploadCleaner;
private final TomcatController tomcatController;
private final ModelCleaner modelCleaner;
private final SuiteRunner suiteRunner;
private final OutputManager outputManager;
private final DataModel dataModel;
public SeleniumRunner(SeleniumRunnerParameters parms) {
this.parms = parms;
this.listener = parms.getListener();
this.uploadCleaner = new UploadAreaCleaner(parms);
this.tomcatController = new TomcatController(parms);
this.modelCleaner = new ModelCleaner(parms, this.tomcatController);
this.suiteRunner = new SuiteRunner(parms);
this.outputManager = new OutputManager(parms);
this.dataModel = new DataModel();
this.dataModel.setIgnoredTestList(parms.getIgnoredTests());
}
/**
* Set up the run, run the selected suites, summarize the output, and clean
* up afterwards.
*
* @return <code>true</code> iff all tests passed.
*/
public boolean run() {
boolean success;
try {
listener.runStarted();
outputManager.cleanOutputDirectory();
parseSuites();
selectSuites();
runSelectedSuites();
tomcatController.cleanup();
listener.runEndTime();
outputManager.summarizeOutput(dataModel);
success = Status.isSuccess(dataModel.getRunStatus());
} catch (IOException e) {
listener.runFailed(e);
success = false;
throw new FatalException(e);
} catch (FatalException e) {
listener.runFailed(e);
success = false;
throw e;
} finally {
listener.runStopped();
outputManager.summarizeOutput(dataModel);
}
return success;
}
/**
* Scan the suite directories in the suite files.
*/
public void parseSuites() {
List<SuiteContents> allContents = new ArrayList<SuiteContents>();
for (File parentDir : parms.getSuiteParentDirectories()) {
for (File suiteDir : parms.findSuiteDirs(parentDir)) {
SuiteContents contents = SuiteContents.parse(suiteDir);
if (contents != null) {
allContents.add(contents);
}
}
}
dataModel.setSuiteContents(allContents);
}
/**
* Select all test suites which aren't explicitly ignored.
*/
public void selectSuites() {
Listener listener = parms.getListener();
IgnoredTests ignored = parms.getIgnoredTests();
List<File> suites = new ArrayList<File>();
for (File parentDir : parms.getSuiteParentDirectories()) {
for (File suite : parms.findSuiteDirs(parentDir)) {
String suiteName = suite.getName();
if (ignored.isIgnored(suiteName)) {
listener.suiteIgnored(suite);
} else {
listener.suiteAdded(suite);
suites.add(suite);
}
}
}
dataModel.setSelectedSuites(suites);
}
public void runSelectedSuites() {
for (File suiteDir : dataModel.getSelectedSuites()) {
outputManager.summarizeOutput(dataModel);
listener.suiteStarted(suiteDir);
try {
if (parms.isCleanModel()) {
modelCleaner.clean();
}
if (parms.isCleanUploads()) {
uploadCleaner.clean();
}
suiteRunner.runSuite(suiteDir);
} catch (IOException e) {
listener.suiteFailed(suiteDir, e);
} catch (CommandRunnerException e) {
listener.suiteFailed(suiteDir, e);
}
listener.suiteStopped(suiteDir);
}
}
private static void usage(String message) {
System.out.println(message);
System.out.println("Usage is: SeleniumRunner <parameters_file> "
+ "[\"interactive\"]");
System.exit(1);
}
public static void main(String[] args) {
SeleniumRunnerParameters parms = null;
boolean interactive = false;
boolean success = false;
if ((args.length != 1) && (args.length != 2)) {
usage("Wrong number of arguments.");
}
if (args.length == 2) {
String option = args[1].trim();
if (option.length() > 0) {
if (!"interactive".equalsIgnoreCase(args[1])) {
usage("Invalid argument '" + args[1] + "'");
}
interactive = true;
}
}
try {
try {
parms = new SeleniumRunnerParameters(args[0]);
} catch (IOException e) {
usage("Can't read properties file: " + e.getMessage());
} catch (IllegalArgumentException e) {
throw new FatalException(e);
}
if (interactive) {
// TODO hook up the GUI.
throw new FatalException("interactive mode not implemented. "
+ "use 'ant acceptance -Dacceptance.batch=true'");
} else {
File logFile = new File(parms.getOutputDirectory(),
LOGFILE_NAME);
System.out.println("Log file is '" + logFile.getPath() + "'");
// Run all of the suites.
// For each suite, clean the model and the upload area.
parms.setCleanModel(true);
parms.setCleanUploads(true);
System.out.println(parms);
success = new SeleniumRunner(parms).run();
}
} catch (FatalException e) {
System.err.println("\n\n-----------------\n"
+ "| FATAL ERROR | " + e.getMessage()
+ "\n-----------------\n\n");
e.printStackTrace();
}
System.out.println("Exiting SeleniumRunner");
System.exit(success ? 0 : -1);
}
}

View file

@ -1,52 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner;
/**
* Status for each test, each suite, and the entire run.
*/
public enum Status {
/** All tests passed, and there were no warnings or errors. */
OK("good"),
/**
* One or more tests have not been run yet.
*/
PENDING("pending"),
/**
* Will not run because it is ignored, or has run and failed but the failure
* is ignored.
*/
IGNORED("fair"),
/**
* A test failed and could not be ignored, or an error message was
* generated.
*/
ERROR("bad");
private final String htmlClass;
private Status(String htmlClass) {
this.htmlClass = htmlClass;
}
public String getHtmlClass() {
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;
}
}
/** Anything except ERROR is considered to be a success. */
public static boolean isSuccess(Status status) {
return status != Status.ERROR;
}
}

View file

@ -1,74 +0,0 @@
/* $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.util.ArrayList;
import java.util.List;
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
/**
* Run a Selenium TestSuite in a sub-process.
*/
public class SuiteRunner {
private final SeleniumRunnerParameters parms;
private final CommandRunner runner;
private final Listener listener;
public SuiteRunner(SeleniumRunnerParameters parms) {
this.parms = parms;
this.runner = new CommandRunner(parms);
this.listener = parms.getListener();
}
/**
* Run the suite.
*/
public void runSuite(File suiteDir) {
listener.suiteTestingStarted(suiteDir);
List<String> cmd = new ArrayList<String>();
cmd.add("java");
cmd.add("-jar");
cmd.add(parms.getSeleniumJarPath().getPath());
cmd.add("-singleWindow");
cmd.add("-timeout");
cmd.add(String.valueOf(parms.getSuiteTimeoutLimit()));
cmd.add("-userExtensions");
cmd.add(parms.getUserExtensionsFile().getPath());
// TODO - figure out why the use of a template means running the test
// twice in simultaneous tabs.
// if (parms.hasFirefoxProfileDir()) {
// cmd.add("-firefoxProfileTemplate");
// cmd.add(parms.getFirefoxProfileDir().getPath());
// }
String suiteName = suiteDir.getName();
File outputFile = new File(parms.getOutputDirectory(), suiteName
+ ".html");
File suiteFile = new File(suiteDir, "Suite.html");
cmd.add("-htmlSuite");
cmd.add("*firefox");
cmd.add(parms.getWebsiteUrl());
cmd.add(suiteFile.getPath());
cmd.add(outputFile.getPath());
try {
runner.run(cmd);
} catch (CommandRunnerException e) {
throw new FatalException(e);
}
int returnCode = runner.getReturnCode();
if (returnCode != 0) {
listener.suiteFailed(suiteDir, returnCode);
}
listener.suiteTestingStopped(suiteDir);
}
}

View file

@ -1,160 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner;
import java.util.ArrayList;
import java.util.List;
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
/**
* Start and stop the webapp, so we can clean the database.
*/
public class TomcatController {
private final SeleniumRunnerParameters parms;
private final ModelCleanerProperties properties;
private final Listener listener;
public TomcatController(SeleniumRunnerParameters parms) {
this.parms = parms;
this.properties = parms.getModelCleanerProperties();
this.listener = parms.getListener();
try {
checkThatTomcatIsReady();
} catch (CommandRunnerException e) {
throw new FatalException(e);
}
}
/**
* Insure that Tomcat is ready and that we can start and stop VIVO.
*/
private void checkThatTomcatIsReady() throws CommandRunnerException {
String tomcatCheckReadyCommand = properties
.getTomcatCheckReadyCommand();
CommandRunner runner = new CommandRunner(parms);
listener.webappCheckingReady(tomcatCheckReadyCommand);
runner.run(parseCommandLine(tomcatCheckReadyCommand));
int returnCode = runner.getReturnCode();
if (returnCode != 0) {
listener.webappCheckReadyFailed(returnCode);
throw new CommandRunnerException("Tomcat is not ready: code="
+ returnCode);
}
listener.webappCheckedReady();
}
public void stopTheWebapp() throws CommandRunnerException {
String tomcatStopCommand = properties.getTomcatStopCommand();
CommandRunner runner = new CommandRunner(parms);
listener.webappStopping(tomcatStopCommand);
runner.run(parseCommandLine(tomcatStopCommand));
int returnCode = runner.getReturnCode();
if (returnCode != 0) {
listener.webappStopFailed(returnCode);
throw new CommandRunnerException("Failed to stop Tomcat: code="
+ returnCode);
}
listener.webappStopped();
}
/**
* Start Tomcat and wait for it to initialize.
*/
public void startTheWebapp() throws CommandRunnerException {
String tomcatStartCommand = properties.getTomcatStartCommand();
CommandRunner runner = new CommandRunner(parms);
listener.webappStarting(tomcatStartCommand);
try {
// Stupid Windows won't allow us to start Tomcat as an independent
// process (except if its installed as a service).
runner.run(parseCommandLine(tomcatStartCommand));
} catch (CommandRunnerException e) {
throw new FatalException(e);
}
int returnCode = runner.getReturnCode();
if (returnCode != 0) {
listener.webappStartFailed(returnCode);
throw new CommandRunnerException("Failed to start Tomcat: code="
+ returnCode);
}
listener.webappStarted();
}
/**
* A command line must be broken into separate arguments, where arguments
* are delimited by blanks unless the blank (and the argument) is enclosed
* in quotes.
*/
static List<String> parseCommandLine(String commandLine) {
List<String> pieces = new ArrayList<String>();
StringBuilder piece = null;
boolean inDelimiter = true;
boolean inQuotes = false;
for (int i = 0; i < commandLine.length(); i++) {
char thisChar = commandLine.charAt(i);
if ((thisChar == ' ') && !inQuotes) {
if (inDelimiter) {
// No effect.
} else {
inDelimiter = true;
pieces.add(piece.toString());
}
} else if (thisChar == '"') {
// Quotes are not carried into the parsed strings.
inQuotes = !inQuotes;
} else { // Not a blank or a quote.
if (inDelimiter) {
inDelimiter = false;
piece = new StringBuilder();
}
piece.append(thisChar);
}
}
// There is an implied delimiter at the end of the command line.
if (!inDelimiter) {
pieces.add(piece.toString());
}
// Quotes must appear in pairs
if (inQuotes) {
throw new IllegalArgumentException(
"Command line contains mismatched quotes: " + commandLine);
}
return pieces;
}
/**
* The run is finished. Do we need to do anything?
*/
public void cleanup() {
// Don't need to do anything.
// If we've been starting and stopping Tomcat,
// stop it one more time.
if (parms.isCleanModel()) {
try {
stopTheWebapp();
} catch (CommandRunnerException e) {
throw new FatalException(e);
}
}
}
}

View file

@ -1,51 +0,0 @@
/* $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.IOException;
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
/**
* Clean out the file upload area, so the next suite will start with no uploads.
*/
public class UploadAreaCleaner {
private final SeleniumRunnerParameters parms;
private final Listener listener;
public UploadAreaCleaner(SeleniumRunnerParameters parms) {
this.parms = parms;
this.listener = parms.getListener();
}
/**
* Delete all of the directories and files in the upload directory. Don't
* delete the upload directory itself.
*/
public void clean() throws IOException {
File uploadDirectory = parms.getUploadDirectory();
if (!uploadDirectory.isDirectory()) {
throw new IllegalArgumentException("'" + uploadDirectory.getPath()
+ "' is not a directory.");
}
listener.cleanUploadStart(uploadDirectory);
try {
for (File file : uploadDirectory.listFiles()) {
if (file.isFile()) {
FileHelper.deleteFile(file);
} else {
FileHelper.purgeDirectoryRecursively(file);
}
}
} catch (IOException e) {
listener.cleanUploadFailed(uploadDirectory, e);
throw e;
} finally {
listener.cleanUploadStop(uploadDirectory);
}
}
}

View file

@ -1,100 +0,0 @@
/* $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.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper;
/**
* Parses the actual HTML test suite file, and holds the results.
*/
public class SuiteContents {
/**
* If the file doesn't contain a line that includes this pattern, it is not
* a suite contents file.
*/
private static final Pattern TITLE_LINE_PATTERN = Pattern
.compile("<title>Test Suite</title>");
private static final Pattern TEST_PATTERN = Pattern
.compile("<tr><td><a\\s+href=[^>]*>([^<]+)</a></td></tr>(?m)");
private static final String SUITE_FILE_NAME = "Suite.html";
/**
* Parse the test names fields from this file and attempt to produce a
* {@link SuiteContents} object. If this is not an appropriate file, just
* return null.
*/
public static SuiteContents parse(File suiteDirectory) {
StringBuilder fileContents = new StringBuilder();
BufferedReader reader = null;
try {
File suiteFile = new File(suiteDirectory, SUITE_FILE_NAME);
if (!suiteFile.exists()) {
return null;
}
reader = new BufferedReader(new FileReader(suiteFile));
String line;
while (null != (line = reader.readLine())) {
fileContents.append(line).append('\n');
}
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// If it doesn't contain the title line, it's not a suite file.
if (!TITLE_LINE_PATTERN.matcher(fileContents).find()) {
return null;
}
// Accumulate all of the test names.
List<String> testNames = new ArrayList<String>();
Matcher m = TEST_PATTERN.matcher(fileContents);
int lookHere = 0;
while (m.find(lookHere)) {
testNames.add(m.group(1));
lookHere = m.end();
}
return new SuiteContents(FileHelper.baseName(suiteDirectory), testNames);
}
private final String name;
private final Collection<String> testNames;
public SuiteContents(String name, Collection<String> testNames) {
this.name = name;
this.testNames = Collections
.unmodifiableCollection(new ArrayList<String>(testNames));
}
public String getName() {
return name;
}
public Collection<String> getTestNames() {
return testNames;
}
}

View file

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

View file

@ -1,92 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner.listener;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* A listener for all events that occur during the run.
*/
public interface Listener {
void suiteIgnored(File suite);
void suiteAdded(File suite);
void runStarted();
void runFailed(Exception e);
void runEndTime();
void runStopped();
void cleanOutputStart(File outputDirectory);
void cleanOutputFailed(File outputDirectory, IOException e);
void cleanOutputStop(File outputDirectory);
void webappStopping(String tomcatStopCommand);
void webappStopFailed(int returnCode);
void webappStopped();
void dropDatabaseStarting(String statement);
void dropDatabaseFailed(int returnCode);
void dropDatabaseComplete();
void loadDatabaseStarting(String statement);
void loadDatabaseFailed(int returnCode);
void loadDatabaseComplete();
void webappCheckingReady(String command);
void webappCheckReadyFailed(int returnCode);
void webappCheckedReady();
void webappStarting(String command);
void webappStartFailed(int returnCode);
void webappStarted();
void subProcessStart(List<String> command);
void subProcessStartInBackground(List<String> command);
void subProcessStdout(String string);
void subProcessErrout(String string);
void subProcessStop(List<String> command);
void suiteStarted(File suiteDir);
void suiteTestingStarted(File suiteDir);
void suiteFailed(File suiteDir, int returnCode);
void suiteFailed(File suiteDir, Exception e);
void suiteTestingStopped(File suiteDir);
void suiteStopped(File suiteDir);
void cleanUploadStart(File uploadDirectory);
void cleanUploadFailed(File uploadDirectory, IOException e);
void cleanUploadStop(File uploadDirectory);
void logWarning(String message);
}

View file

@ -1,281 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner.listener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* A listener for all events that occur during the run. In this basic
* implementation, each event is simply formatted and written to a log file or
* {@link PrintStream}.
*/
public class LoggingListener implements Listener {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss.SSS");
private final Writer writer;
// ----------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------
public LoggingListener(PrintStream out) {
this.writer = new OutputStreamWriter(out);
}
public LoggingListener(File logFile) throws IOException {
this.writer = new FileWriter(logFile);
}
// ----------------------------------------------------------------------
// Listener methods
// ----------------------------------------------------------------------
@Override
public void suiteIgnored(File suite) {
log("Suite '" + suite.getName() + "' ignored.");
}
@Override
public void suiteAdded(File suite) {
log("Suite '" + suite.getName() + "' added.");
}
@Override
public void runStarted() {
log("Run started.");
}
@Override
public void runFailed(Exception e) {
log("Run failed - fatal error");
log(e);
}
@Override
public void runEndTime() {
log("Testing complete.");
}
@Override
public void runStopped() {
log("Run stopped.");
}
@Override
public void cleanOutputStart(File outputDirectory) {
log("Output area cleaning started: " + outputDirectory.getPath());
}
@Override
public void cleanOutputFailed(File outputDirectory, IOException e) {
log("Output area cleaning failed: " + outputDirectory.getPath());
log(e);
}
@Override
public void cleanOutputStop(File outputDirectory) {
log("Output area cleaning stopped: " + outputDirectory.getPath());
}
@Override
public void webappCheckingReady(String command) {
log("Checking if Tomcat is ready: " + command);
}
@Override
public void webappCheckReadyFailed(int returnCode) {
log("Tomcat is not ready: " + returnCode);
}
@Override
public void webappCheckedReady() {
log("Checked that Tomcat is ready.");
}
@Override
public void webappStopping(String command) {
log("Stopping tomcat: " + command);
}
@Override
public void webappStopFailed(int returnCode) {
log("Failed to stop tomcat; return code was " + returnCode);
}
@Override
public void webappStopped() {
log("Tomcat stopped.");
}
@Override
public void dropDatabaseStarting(String statement) {
log("Dropping database: " + statement);
}
@Override
public void dropDatabaseFailed(int returnCode) {
log("Failed to drop the database; return code was " + returnCode);
}
@Override
public void dropDatabaseComplete() {
log("Dropped database.");
}
@Override
public void loadDatabaseStarting(String statement) {
log("Loading the database: " + statement);
}
@Override
public void loadDatabaseFailed(int returnCode) {
log("Failed to load the database; return code was " + returnCode);
}
@Override
public void loadDatabaseComplete() {
log("Loaded the database.");
}
@Override
public void webappStarting(String tomcatStartCommand) {
log("Starting tomcat: " + tomcatStartCommand);
}
@Override
public void webappStartFailed(int returnCode) {
log("Failed to start tomcat; return code was " + returnCode);
}
@Override
public void webappStarted() {
log("Tomcat started.");
}
@Override
public void subProcessStart(List<String> command) {
log("Subprocess started: " + command);
}
@Override
public void subProcessStartInBackground(List<String> command) {
log("Subprocess started in background: " + command);
}
@Override
public void subProcessStdout(String string) {
logRawText(string);
}
@Override
public void subProcessErrout(String string) {
logRawText(string);
}
@Override
public void subProcessStop(List<String> command) {
log("Subprocess stopped: " + command);
}
@Override
public void suiteStarted(File suiteDir) {
log("Suite started: " + suiteDir.getName());
}
@Override
public void suiteTestingStarted(File suiteDir) {
log("Suite testing started: " + suiteDir.getName());
}
@Override
public void suiteFailed(File suiteDir, int returnCode) {
log("Suite failed: " + suiteDir.getName() + ", returnCode="
+ returnCode);
}
@Override
public void suiteFailed(File suiteDir, Exception e) {
log("Suite failed: " + suiteDir.getName());
log(e);
}
@Override
public void suiteTestingStopped(File suiteDir) {
log("Suite testing stopped: " + suiteDir.getName());
}
@Override
public void suiteStopped(File suiteDir) {
log("Suite stopped: " + suiteDir.getName());
}
@Override
public void cleanUploadStart(File uploadDirectory) {
log("Upload cleaning started: " + uploadDirectory.getPath());
}
@Override
public void cleanUploadFailed(File uploadDirectory, IOException e) {
log("Upload cleaning failed: " + uploadDirectory.getPath());
log(e);
}
@Override
public void cleanUploadStop(File uploadDirectory) {
log("Upload cleaning stopped: " + uploadDirectory.getPath());
}
@Override
public void logWarning(String message) {
log("WARNING: " + message);
}
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
private void logRawText(String rawText) {
try {
writer.write(rawText);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private void log(String message) {
try {
writer.write(timeStamp() + " " + message + "\n");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private void log(Throwable t) {
try {
t.printStackTrace(new PrintWriter(writer));
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Convert the current date and time to a string for the log.
*/
private String timeStamp() {
return DATE_FORMAT.format(new Date());
}
}

View file

@ -1,298 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner.listener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* A {@link Listener} implementation that holds a list of {@link Listener}s and
* sends each message to all of them.
*/
public class MulticastListener implements Listener {
private final List<Listener> listeners = new ArrayList<Listener>();
public void addListener(Listener l) {
listeners.add(l);
}
// ----------------------------------------------------------------------
// Listener methods
// ----------------------------------------------------------------------
@Override
public void suiteIgnored(File suite) {
for (Listener l : listeners) {
l.suiteIgnored(suite);
}
}
@Override
public void suiteAdded(File suite) {
for (Listener l : listeners) {
l.suiteAdded(suite);
}
}
@Override
public void runStarted() {
for (Listener l : listeners) {
l.runStarted();
}
}
@Override
public void runFailed(Exception e) {
for (Listener l : listeners) {
l.runFailed(e);
}
}
@Override
public void runEndTime() {
for (Listener l : listeners) {
l.runEndTime();
}
}
@Override
public void runStopped() {
for (Listener l : listeners) {
l.runStopped();
}
}
@Override
public void cleanOutputStart(File outputDirectory) {
for (Listener l : listeners) {
l.cleanOutputStart(outputDirectory);
}
}
@Override
public void cleanOutputFailed(File outputDirectory, IOException e) {
for (Listener l : listeners) {
l.cleanOutputFailed(outputDirectory, e);
}
}
@Override
public void cleanOutputStop(File outputDirectory) {
for (Listener l : listeners) {
l.cleanOutputStop(outputDirectory);
}
}
@Override
public void webappStopping(String tomcatStopCommand) {
for (Listener l : listeners) {
l.webappStopping(tomcatStopCommand);
}
}
@Override
public void webappStopFailed(int returnCode) {
for (Listener l : listeners) {
l.webappStopFailed(returnCode);
}
}
@Override
public void webappStopped() {
for (Listener l : listeners) {
l.webappStopped();
}
}
@Override
public void dropDatabaseStarting(String statement) {
for (Listener l : listeners) {
l.dropDatabaseStarting(statement);
}
}
@Override
public void dropDatabaseFailed(int returnCode) {
for (Listener l : listeners) {
l.dropDatabaseFailed(returnCode);
}
}
@Override
public void dropDatabaseComplete() {
for (Listener l : listeners) {
l.dropDatabaseComplete();
}
}
@Override
public void loadDatabaseStarting(String statement) {
for (Listener l : listeners) {
l.loadDatabaseStarting(statement);
}
}
@Override
public void loadDatabaseFailed(int returnCode) {
for (Listener l : listeners) {
l.loadDatabaseFailed(returnCode);
}
}
@Override
public void loadDatabaseComplete() {
for (Listener l : listeners) {
l.loadDatabaseComplete();
}
}
@Override
public void webappCheckingReady(String command) {
for (Listener l : listeners) {
l.webappCheckingReady(command);
}
}
@Override
public void webappCheckReadyFailed(int returnCode) {
for (Listener l : listeners) {
l.webappCheckReadyFailed(returnCode);
}
}
@Override
public void webappCheckedReady() {
for (Listener l : listeners) {
l.webappCheckedReady();
}
}
@Override
public void webappStarting(String tomcatStartCommand) {
for (Listener l : listeners) {
l.webappStarting(tomcatStartCommand);
}
}
@Override
public void webappStartFailed(int returnCode) {
for (Listener l : listeners) {
l.webappStartFailed(returnCode);
}
}
@Override
public void webappStarted() {
for (Listener l : listeners) {
l.webappStarted();
}
}
@Override
public void subProcessStart(List<String> command) {
for (Listener l : listeners) {
l.subProcessStart(command);
}
}
@Override
public void subProcessStartInBackground(List<String> command) {
for (Listener l : listeners) {
l.subProcessStartInBackground(command);
}
}
@Override
public void subProcessStdout(String string) {
for (Listener l : listeners) {
l.subProcessStdout(string);
}
}
@Override
public void subProcessErrout(String string) {
for (Listener l : listeners) {
l.subProcessErrout(string);
}
}
@Override
public void subProcessStop(List<String> command) {
for (Listener l : listeners) {
l.subProcessStop(command);
}
}
@Override
public void suiteStarted(File suiteDir) {
for (Listener l : listeners) {
l.suiteStarted(suiteDir);
}
}
@Override
public void suiteTestingStarted(File suiteDir) {
for (Listener l : listeners) {
l.suiteTestingStarted(suiteDir);
}
}
@Override
public void suiteFailed(File suiteDir, int returnCode) {
for (Listener l : listeners) {
l.suiteFailed(suiteDir, returnCode);
}
}
@Override
public void suiteFailed(File suiteDir, Exception e) {
for (Listener l : listeners) {
l.suiteFailed(suiteDir, e);
}
}
@Override
public void suiteTestingStopped(File suiteDir) {
for (Listener l : listeners) {
l.suiteTestingStopped(suiteDir);
}
}
@Override
public void suiteStopped(File suiteDir) {
for (Listener l : listeners) {
l.suiteStopped(suiteDir);
}
}
@Override
public void cleanUploadStart(File uploadDirectory) {
for (Listener l : listeners) {
l.cleanUploadStart(uploadDirectory);
}
}
@Override
public void cleanUploadFailed(File uploadDirectory, IOException e) {
for (Listener l : listeners) {
l.cleanUploadFailed(uploadDirectory, e);
}
}
@Override
public void cleanUploadStop(File uploadDirectory) {
for (Listener l : listeners) {
l.cleanUploadStop(uploadDirectory);
}
}
@Override
public void logWarning(String message) {
for (Listener l : listeners) {
l.logWarning(message);
}
}
}

View file

@ -1,312 +0,0 @@
/* $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.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper;
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
public class OutputDataListener implements Listener {
private boolean runCompleted;
private long startTime;
private long endTime;
private ProcessOutput currentSuiteOutput = new ProcessOutput("");
private Map<String, ProcessOutput> failureMessages = new HashMap<String, ProcessOutput>();
// ----------------------------------------------------------------------
// Listener methods that affect the data model
// ----------------------------------------------------------------------
@Override
public void runStarted() {
System.out.println("run started");
startTime = new Date().getTime();
}
@Override
public void runEndTime() {
endTime = new Date().getTime();
}
@Override
public void runStopped() {
runCompleted = true;
}
@Override
public void suiteStarted(File suiteDir) {
currentSuiteOutput = new ProcessOutput(FileHelper.baseName(suiteDir));
}
@Override
public void suiteStopped(File suiteDir) {
if (currentSuiteOutput.isSuiteFailure()) {
failureMessages.put(currentSuiteOutput.suiteName,
currentSuiteOutput);
}
currentSuiteOutput = new ProcessOutput("");
}
@Override
public void subProcessStdout(String string) {
currentSuiteOutput.addStdout(string);
}
@Override
public void subProcessErrout(String string) {
currentSuiteOutput.addErrout(string);
}
// ----------------------------------------------------------------------
// A class that holds a snapshot of the data.
// ----------------------------------------------------------------------
/**
* A snapshot of the data that the listener has accumulated so far.
*/
public static class Info {
public static Info EMPTY_INFO = new Info();
private final boolean runCompleted;
private final long startTime;
private final long endTime;
private final Map<String, ProcessOutput> failureMessages;
Info() {
this.runCompleted = false;
this.startTime = 0;
this.endTime = 0;
this.failureMessages = Collections.emptyMap();
}
Info(OutputDataListener parent) {
this.runCompleted = parent.runCompleted;
this.startTime = parent.startTime;
this.endTime = parent.endTime;
this.failureMessages = Collections
.unmodifiableMap(new HashMap<String, ProcessOutput>(
parent.failureMessages));
}
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 Map<String, ProcessOutput> getFailureMessages() {
return failureMessages;
}
}
/**
* Get a snapshot of the data.
*/
public Info getInfo() {
return new Info(this);
}
// ----------------------------------------------------------------------
// A class that summarized the sub-process output from a test suite.
// ----------------------------------------------------------------------
/**
* The output from a subprocess that runs a test suite. It's only
* interesting if it indicates a suite failure.
*/
public static class ProcessOutput {
private static final String SUITE_FAILURE_PATTERN = "exception|error(?i)";
private final String suiteName;
private final StringBuilder stdout = new StringBuilder();
private final StringBuilder errout = new StringBuilder();
public ProcessOutput(String suiteName) {
this.suiteName = suiteName;
}
public void addStdout(String string) {
stdout.append(string);
}
public void addErrout(String string) {
errout.append(string);
}
public String getSuiteName() {
return suiteName;
}
public String getStdout() {
return stdout.toString();
}
public String getErrout() {
return errout.toString();
}
public boolean isSuiteFailure() {
Pattern p = Pattern.compile(SUITE_FAILURE_PATTERN);
Matcher m = p.matcher(errout);
return m.find();
}
}
// ----------------------------------------------------------------------
// Listener methods that don't affect the data model
// ----------------------------------------------------------------------
@Override
public void suiteAdded(File suite) {
}
@Override
public void suiteIgnored(File suite) {
}
@Override
public void runFailed(Exception e) {
}
@Override
public void cleanOutputStart(File outputDirectory) {
}
@Override
public void cleanOutputFailed(File outputDirectory, IOException e) {
}
@Override
public void cleanOutputStop(File outputDirectory) {
}
@Override
public void webappCheckingReady(String command) {
}
@Override
public void webappCheckReadyFailed(int returnCode) {
}
@Override
public void webappCheckedReady() {
}
@Override
public void webappStopping(String tomcatStopCommand) {
}
@Override
public void webappStopFailed(int returnCode) {
}
@Override
public void webappStopped() {
}
@Override
public void dropDatabaseStarting(String statement) {
}
@Override
public void dropDatabaseFailed(int returnCode) {
}
@Override
public void dropDatabaseComplete() {
}
@Override
public void loadDatabaseStarting(String statement) {
}
@Override
public void loadDatabaseFailed(int returnCode) {
}
@Override
public void loadDatabaseComplete() {
}
@Override
public void webappStarting(String tomcatStartCommand) {
}
@Override
public void webappStartFailed(int returnCode) {
}
@Override
public void webappStarted() {
}
@Override
public void subProcessStart(List<String> command) {
}
@Override
public void subProcessStartInBackground(List<String> command) {
}
@Override
public void subProcessStop(List<String> command) {
}
@Override
public void suiteTestingStarted(File suiteDir) {
}
@Override
public void suiteFailed(File suiteDir, int returnCode) {
}
@Override
public void suiteFailed(File suiteDir, Exception e) {
}
@Override
public void suiteTestingStopped(File suiteDir) {
}
@Override
public void cleanUploadStart(File uploadDirectory) {
}
@Override
public void cleanUploadFailed(File uploadDirectory, IOException e) {
}
@Override
public void cleanUploadStop(File uploadDirectory) {
}
@Override
public void logWarning(String message) {
}
}

View file

@ -1,98 +0,0 @@
/* $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.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper;
import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats;
import edu.cornell.mannlib.vitro.utilities.testrunner.SeleniumRunnerParameters;
import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.DataModel;
/**
* Manages the contents of the output area. Removes old files prior to a run.
* Creates a unified summary of the test suite outputs.
*/
public class OutputManager {
private final SeleniumRunnerParameters parms;
private final OutputDataListener dataListener;
public OutputManager(SeleniumRunnerParameters parms) {
this.parms = parms;
this.dataListener = new OutputDataListener();
parms.addListener(this.dataListener);
}
/**
* Delete any output files from previous runs.
*/
public void cleanOutputDirectory() throws IOException {
File outputDirectory = parms.getOutputDirectory();
parms.getListener().cleanOutputStart(outputDirectory);
try {
for (File file : outputDirectory.listFiles()) {
// Skip the log file, since we are already over-writing it.
if (file.equals(parms.getLogFile())) {
continue;
}
// Skip any hidden files (like .svn)
if (file.getPath().startsWith(".")) {
continue;
}
// Delete all of the others.
if (file.isFile()) {
FileHelper.deleteFile(file);
} else {
FileHelper.purgeDirectoryRecursively(file);
}
}
} catch (IOException e) {
parms.getListener().cleanOutputFailed(outputDirectory, e);
throw e;
} finally {
parms.getListener().cleanOutputStop(outputDirectory);
}
}
/**
* Parse each of the output files from the test suites, and create a unified
* output file.
*/
public void summarizeOutput(DataModel dataModel) {
try {
LogStats log = LogStats.parse(parms.getLogFile());
List<SuiteResults> suiteResults = new ArrayList<SuiteResults>();
for (File outputFile : parms.getOutputDirectory().listFiles(
new HtmlFileFilter())) {
SuiteResults suite = SuiteResults.parse(parms, outputFile);
if (suite != null) {
suiteResults.add(suite);
}
}
dataModel.setSuiteResults(suiteResults);
dataModel.captureDataListener(dataListener);
OutputSummaryFormatter formatter = new OutputSummaryFormatter(parms);
formatter.format(log, dataModel);
} catch (Exception e) {
// It must be impossible to throw an exception from here, so just
// print it to sysout.
e.printStackTrace();
}
}
private static class HtmlFileFilter implements FileFilter {
public boolean accept(File path) {
return path.getName().endsWith(".html")
|| path.getName().endsWith(".htm");
}
}
}

View file

@ -1,188 +0,0 @@
/* $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.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
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 java.util.regex.Matcher;
import java.util.regex.Pattern;
import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper;
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests;
import edu.cornell.mannlib.vitro.utilities.testrunner.SeleniumRunnerParameters;
import edu.cornell.mannlib.vitro.utilities.testrunner.Status;
/**
* Extract any summary information from an HTML output file, produced by a test
* suite.
*/
public class SuiteResults {
/**
* If the file doesn't contain a line that includes this pattern, it is not
* a suite output file.
*/
private static final Pattern TITLE_LINE_PATTERN = Pattern
.compile("<title>Test suite results</title>");
/**
* A test line looks something like this example:
*/
public static final String EXAMPLE_TEST_LINE = ""
+ "<pre><tr class=\" status_passed\"><td><a href=\"#testresult0\">MyTest</a></td></tr></pre>";
/**
* So here is the pattern to match it:
*/
private static final Pattern TEST_LINE_PATTERN = Pattern
.compile("<tr class=\"\\s*(\\w+)\"><td><a href=\"(#\\w+)\">([^<]*)</a></td></tr>");
/**
* Parse the fields from this file and attempt to produce a
* {@link SuiteResults} object. If this is not an appropriate file, just
* return null.
*/
public static SuiteResults parse(SeleniumRunnerParameters parms,
File outputFile) {
IgnoredTests ignoredTests = parms.getIgnoredTests();
boolean isSuiteOutputFile = false;
Status status = Status.ERROR;
List<TestResults> tests = new ArrayList<TestResults>();
String suiteName = FileHelper.baseName(outputFile);
String outputLink = outputFile.getName();
BufferedReader reader = null;
String line;
try {
reader = new BufferedReader(new FileReader(outputFile));
while (null != (line = reader.readLine())) {
if (TITLE_LINE_PATTERN.matcher(line).find()) {
isSuiteOutputFile = true;
}
Matcher m;
m = TEST_LINE_PATTERN.matcher(line);
if (m.matches()) {
String testName = m.group(3);
String testLink = outputLink + m.group(2);
Status testStatus;
if ("status_passed".equals(m.group(1))) {
testStatus = Status.OK;
} else if (ignoredTests.isIgnored(suiteName, testName)) {
testStatus = Status.IGNORED;
} else {
testStatus = Status.ERROR;
}
tests.add(new TestResults(testName, suiteName, testLink,
testStatus));
}
}
status = Status.OK;
for (TestResults t : tests) {
status = Status.combine(status, t.status);
}
if (isSuiteOutputFile) {
return new SuiteResults(suiteName, outputLink, tests, status);
} else {
return null;
}
} catch (IOException e) {
// Can't give up - I need to create as much output as I can.
e.printStackTrace();
return null;
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private final String suiteName;
private final String outputLink;
private final Map<String, TestResults> testMap;
private final Status status;
public SuiteResults(String suiteName, String outputLink,
List<TestResults> tests, Status status) {
this.suiteName = suiteName;
this.outputLink = outputLink;
this.status = status;
Map<String, TestResults> map = new HashMap<String, TestResults>();
for (TestResults t : tests) {
map.put(t.getTestName(), t);
}
testMap = Collections.unmodifiableMap(map);
}
public String getName() {
return suiteName;
}
public Status getStatus() {
return status;
}
public String getOutputLink() {
return outputLink;
}
public Collection<TestResults> getTests() {
return Collections.unmodifiableCollection(testMap.values());
}
public TestResults getTest(String testName) {
return testMap.get(testName);
}
public static class TestResults {
private final String name;
private final String suite;
private final String outputLink;
private final Status status;
public TestResults(String name, String suite, String outputLink,
Status status) {
this.name = name;
this.suite = suite;
this.outputLink = outputLink;
this.status = status;
}
public Status getStatus() {
return status;
}
public String getSuiteName() {
return suite;
}
public String getTestName() {
return name;
}
public String getOutputLink() {
return outputLink;
}
}
}

View file

@ -1,115 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
/*
Formats for the output summary from the acceptance tests.
*/
body {
background: rgb(95%, 95%, 95%);
font-family: sans-serif;
}
.heading {
border: groove;
background: white;
padding: 10px 20px 8px 20px;
margin-top: 50px;
font-size: large;
}
table {
border: thin double gray;
background: white;
}
td,th {
padding: 4px 12px 2px 12px;
}
th {
border-bottom: 1px solid black;
}
table.summary {
border: none;
background: inherit;
}
table.summary td {
padding-right: 30;
border: none;
vertical-align: top;
}
table.tallys td {
align: right;
}
table.tallys td.total {
font.weight: bold;
}
.section {
background: rgb(70%, 85%, 85%);
font-size: larger;
margin: 50px 0px 15px 0px;
padding: 4px 12px 2px 12px;
}
.good {
background: rgb(60%, 100%, 60%);
}
.bad {
background: rgb(100%, 60%, 60%);
}
.fair {
background: rgb(100%, 100%, 60%);
}
.pending {
background: rgb(90%, 90%, 100%);
}
.one-word {
width: 20%;
text-align: center;
margin: 15px 0px 0px 0px;
border: 1px solid black;
}
table.condensed td {
border: 1px solid grey;
padding: 10px 5px 10px 5px;
}
table.condensed div.suite {
font-size: 85%;
font-weight: bold;
padding: 3px 3px 3px 3px;
}
table.condensed div.reason {
font-size: 70%;
font-style: italic;
padding: 3px 3px 3px 30px;
}
table.condensed div.test {
font-size: 70%;
padding: 3px 3px 3px 15px;
}
table.condensed div.test div.tReason{
font-style: italic;
padding: 3px 3px 3px 10px;
}
table.ignored td {
font-size: 80%;
}
pre.log {
font-family: monospace;
font-size: 80%;
}

View file

@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:vitro="http://vitro.mannlib.cornell.edu/ns/vitro/0.7#"
xmlns:auth="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#"
xmlns="http://vitro.mannlib.cornell.edu/ns/vitro/default#"
xml:base="http://vitro.mannlib.cornell.edu/ns/vitro/default">
<auth:UserAccount rdf:about="http://vivo.mydomain.edu/individual/TestAdmin">
<auth:emailAddress rdf:datatype="http://www.w3.org/2001/XMLSchema#string">testAdmin@cornell.edu</auth:emailAddress>
<auth:externalAuthId rdf:datatype="http://www.w3.org/2001/XMLSchema#string">testAdmin</auth:externalAuthId>
<auth:firstName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Test</auth:firstName>
<auth:lastName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Admin</auth:lastName>
<auth:md5password rdf:datatype="http://www.w3.org/2001/XMLSchema#string">DC647EB65E6711E155375218212B3964</auth:md5password>
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#ADMIN"/>
</auth:UserAccount>
<auth:UserAccount rdf:about="http://vivo.mydomain.edu/individual/JohnCurator">
<auth:emailAddress rdf:datatype="http://www.w3.org/2001/XMLSchema#string">johnCurator@cornell.edu</auth:emailAddress>
<auth:externalAuthId rdf:datatype="http://www.w3.org/2001/XMLSchema#string">johnCurator</auth:externalAuthId>
<auth:firstName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">John</auth:firstName>
<auth:lastName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Curator</auth:lastName>
<auth:md5password rdf:datatype="http://www.w3.org/2001/XMLSchema#string">DC647EB65E6711E155375218212B3964</auth:md5password>
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#CURATOR"/>
</auth:UserAccount>
<auth:UserAccount rdf:about="http://vivo.mydomain.edu/individual/SallyEditor">
<auth:emailAddress rdf:datatype="http://www.w3.org/2001/XMLSchema#string">sallyEditor@cornell.edu</auth:emailAddress>
<auth:externalAuthId rdf:datatype="http://www.w3.org/2001/XMLSchema#string">sallyEditor</auth:externalAuthId>
<auth:firstName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Sally</auth:firstName>
<auth:lastName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Editor</auth:lastName>
<auth:md5password rdf:datatype="http://www.w3.org/2001/XMLSchema#string">DC647EB65E6711E155375218212B3964</auth:md5password>
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#EDITOR"/>
</auth:UserAccount>
<auth:UserAccount rdf:about="http://vivo.mydomain.edu/individual/JoeUser">
<auth:emailAddress rdf:datatype="http://www.w3.org/2001/XMLSchema#string">joeUser@cornell.edu</auth:emailAddress>
<auth:externalAuthId rdf:datatype="http://www.w3.org/2001/XMLSchema#string">joeUser</auth:externalAuthId>
<auth:firstName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Joe</auth:firstName>
<auth:lastName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">User</auth:lastName>
<auth:md5password rdf:datatype="http://www.w3.org/2001/XMLSchema#string">DC647EB65E6711E155375218212B3964</auth:md5password>
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#SELF_EDITOR"/>
</auth:UserAccount>
</rdf:RDF>

View file

@ -1,191 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.utilities.testrunner;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashSet;
import java.util.Set;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo;
/**
* Check that the ignored_tests.txt file is being parsed correctly.
*/
public class IgnoredTestsTest {
private static final String TEST_WITH_REASON = "test with reason";
private static final String TEST_NO_REASON = "test with no reason";
private static final String TEST_NOT_IGNORED = "test not ignored";
private static final String TEST_FROM_IGNORED_SUITE = "test from ignored suite";
private static final String SUITE_WITH_TESTS = "suite with tests";
private static final String SUITE_WITH_REASON = "entire suite with reason";
private static final String SUITE_NO_REASON = "entire suite with no reason";
private static final String SUITE_NOT_IGNORED = "entire suite not ignored";
private static final String REASON_FOR_TEST = "the reason for the test";
private static final String REASON_FOR_SUITE = "the reason for the suite";
private static final String NO_REASON = "";
private static final String FILE_CONTENTS = "# This line is a comment \n"
+ "! This line is also, and so is the blank one\n"
+ "\n"
+ (SUITE_WITH_TESTS + ", " + TEST_NO_REASON + "\n")
+ (SUITE_WITH_TESTS + ", " + TEST_WITH_REASON + " # "
+ REASON_FOR_TEST + "\n") + (SUITE_NO_REASON + ", *\n")
+ (SUITE_WITH_REASON + ", * # " + REASON_FOR_SUITE + "\n");
private static final String BAD_CONTENTS = "suite but no test # doesn't match.";
/*
* Ignore any blank line, or any line starting with '#' or '!' </p> <p> Each
* other line describes an ignored test. The line contains the suite name, a
* comma (with optional space), the test name (with optional space) and
* optionally a comment, starting with a '#'. </p> If the test name is an
* asterisk '*', then the entire suite will be ignored. <p>
*/
private static File ignoreFile;
private static File badFile;
@BeforeClass
public static void initializeTheFile() throws IOException {
ignoreFile = File.createTempFile("IgnoredTestsTest", "");
Writer writer = new FileWriter(ignoreFile);
try {
writer.write(FILE_CONTENTS);
} finally {
writer.close();
}
}
@BeforeClass
public static void initializeBadFile() throws IOException {
badFile = File.createTempFile("IgnoredTestsTest", "");
Writer writer = new FileWriter(badFile);
try {
writer.write(BAD_CONTENTS);
} finally {
writer.close();
}
}
@AfterClass
public static void cleanup() throws IOException {
ignoreFile.delete();
badFile.delete();
}
private IgnoredTests ignored;
@Before
public void readTheFile() {
ignored = new IgnoredTests(ignoreFile);
}
@Test(expected = FatalException.class)
public void readBadFile() {
new IgnoredTests(badFile);
}
@Test
public void getList() {
Set<IgnoredTestInfo> expected = new HashSet<IgnoredTestInfo>();
expected.add(new IgnoredTestInfo(SUITE_WITH_TESTS, TEST_NO_REASON,
NO_REASON));
expected.add(new IgnoredTestInfo(SUITE_WITH_TESTS, TEST_WITH_REASON,
REASON_FOR_TEST));
expected.add(new IgnoredTestInfo(SUITE_NO_REASON, "*", NO_REASON));
expected.add(new IgnoredTestInfo(SUITE_WITH_REASON, "*",
REASON_FOR_SUITE));
Set<IgnoredTestInfo> actual = new HashSet<IgnoredTestInfo>(
ignored.getList());
assertEquals("list of tests", expected, actual);
}
@Test
public void isIgnoredTestYes() {
assertTrue("ignored test",
ignored.isIgnored(SUITE_WITH_TESTS, TEST_NO_REASON));
}
@Test
public void isIgnoredTestNo() {
assertFalse("not ignored test",
ignored.isIgnored(SUITE_WITH_TESTS, TEST_NOT_IGNORED));
}
@Test
public void isIgnoredTestFromSuite() {
assertTrue("test from ignored suite",
ignored.isIgnored(SUITE_WITH_REASON, TEST_FROM_IGNORED_SUITE));
}
@Test
public void getReasonTestYes() {
assertEquals(
"test with reason",
REASON_FOR_TEST,
ignored.getReasonForIgnoring(SUITE_WITH_TESTS, TEST_WITH_REASON));
}
@Test
public void getReasonTestNo() {
assertEquals(
"test not ignored",
NO_REASON,
ignored.getReasonForIgnoring(SUITE_WITH_TESTS, TEST_NOT_IGNORED));
}
@Test
public void getReasonTestNoReason() {
assertEquals("test with no reason", NO_REASON,
ignored.getReasonForIgnoring(SUITE_WITH_TESTS, TEST_NO_REASON));
}
@Test
public void getReasonTestFromSuite() {
assertEquals("test from ignored suite", REASON_FOR_SUITE,
ignored.getReasonForIgnoring(SUITE_WITH_REASON,
TEST_FROM_IGNORED_SUITE));
}
@Test
public void isIgnoredSuiteYes() {
assertTrue("ignored suite", ignored.isIgnored(SUITE_WITH_REASON));
}
@Test
public void isIgnoredSuiteNo() {
assertFalse("not ignored suite", ignored.isIgnored(SUITE_NOT_IGNORED));
}
@Test
public void getReasonSuiteYes() {
assertEquals("suite with reason", REASON_FOR_SUITE,
ignored.getReasonForIgnoring(SUITE_WITH_REASON));
}
@Test
public void getReasonSuiteNo() {
assertEquals("suite not ignored", NO_REASON,
ignored.getReasonForIgnoring(SUITE_NOT_IGNORED));
}
@Test
public void getReasonSuiteNoReason() {
assertEquals("suite with no reason", NO_REASON,
ignored.getReasonForIgnoring(SUITE_NO_REASON));
}
}