NIHVIVO-222 Second step at coding the Java-based SeleniumRunner.
This commit is contained in:
parent
1c5e7fdde8
commit
92f9280af0
11 changed files with 908 additions and 176 deletions
|
@ -34,12 +34,12 @@ public class CommandRunner {
|
||||||
private File workingDirectory;
|
private File workingDirectory;
|
||||||
|
|
||||||
/* Gets informed of output as it arrives. Never null. */
|
/* Gets informed of output as it arrives. Never null. */
|
||||||
private final Logger logger;
|
private final Listener listener;
|
||||||
|
|
||||||
private final Map<String, String> environmentAdditions = new HashMap<String, String>();
|
private final Map<String, String> environmentAdditions = new HashMap<String, String>();
|
||||||
|
|
||||||
public CommandRunner(SeleniumRunnerParameters parms) {
|
public CommandRunner(SeleniumRunnerParameters parms) {
|
||||||
this.logger = parms.getLogger();
|
this.listener = parms.getListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the directory that the command will run in. */
|
/** Set the directory that the command will run in. */
|
||||||
|
@ -61,7 +61,7 @@ public class CommandRunner {
|
||||||
* {@link java.lang.ProcessBuilder#ProcessBuilder(List)}.
|
* {@link java.lang.ProcessBuilder#ProcessBuilder(List)}.
|
||||||
*/
|
*/
|
||||||
public void run(List<String> command) throws CommandRunnerException {
|
public void run(List<String> command) throws CommandRunnerException {
|
||||||
logger.subProcessStart(command);
|
listener.subProcessStart(command);
|
||||||
try {
|
try {
|
||||||
ProcessBuilder builder = new ProcessBuilder(command);
|
ProcessBuilder builder = new ProcessBuilder(command);
|
||||||
|
|
||||||
|
@ -93,7 +93,43 @@ public class CommandRunner {
|
||||||
throw new CommandRunnerException(
|
throw new CommandRunnerException(
|
||||||
"Exception when handling sub-process:", e);
|
"Exception when handling sub-process:", e);
|
||||||
}
|
}
|
||||||
logger.subProcessStop(command);
|
listener.subProcessStop(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the command and don't wait for it to complete. {@link #stdErr} and
|
||||||
|
* {@link #stdOut} will not be set, but output from the process may be sent
|
||||||
|
* to the listener at any time.
|
||||||
|
*
|
||||||
|
* @param command
|
||||||
|
* a list containing the operating system program and its
|
||||||
|
* arguments. See
|
||||||
|
* {@link java.lang.ProcessBuilder#ProcessBuilder(List)}.
|
||||||
|
*/
|
||||||
|
public void runAsBackground(List<String> command)
|
||||||
|
throws CommandRunnerException {
|
||||||
|
listener.subProcessStartInBackground(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);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CommandRunnerException(
|
||||||
|
"Exception when handling sub-process:", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getReturnCode() {
|
public int getReturnCode() {
|
||||||
|
@ -136,11 +172,11 @@ public class CommandRunner {
|
||||||
if (howMany > 0) {
|
if (howMany > 0) {
|
||||||
String string = new String(buffer, 0, howMany);
|
String string = new String(buffer, 0, howMany);
|
||||||
contents.write(string);
|
contents.write(string);
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
logger.subProcessErrout(string);
|
listener.subProcessErrout(string);
|
||||||
} else {
|
} else {
|
||||||
logger.subProcessStdout(string);
|
listener.subProcessStdout(string);
|
||||||
}
|
}
|
||||||
} else if (howMany == 0) {
|
} else if (howMany == 0) {
|
||||||
Thread.yield();
|
Thread.yield();
|
||||||
|
|
|
@ -0,0 +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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
/* $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.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 Listener {
|
||||||
|
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
|
||||||
|
"yyyy-MM-dd HH:mm:ss.SSS");
|
||||||
|
|
||||||
|
private final Writer writer;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Listener methods
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
public Listener(PrintStream out) {
|
||||||
|
this.writer = new OutputStreamWriter(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Listener(File logFile) throws IOException {
|
||||||
|
this.writer = new FileWriter(logFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Listener methods
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
public void runStarted() {
|
||||||
|
log("Run started.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runFailed(Exception e) {
|
||||||
|
log("Run failed - fatal error");
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runStopped() {
|
||||||
|
log("Run stopped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void webappStopping(String tomcatStopCommand) {
|
||||||
|
log("Stopping tomcat: " + tomcatStopCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void webappStopFailed(int returnCode) {
|
||||||
|
log("Failed to stop tomcat; return code was " + returnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void webappWaitingForStop(int tomcatStopDelay) {
|
||||||
|
log("Waiting " + tomcatStopDelay + " seconds for tomcat to stop.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void webappStopped() {
|
||||||
|
log("Tomcat stopped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dropDatabaseStarting(String statement) {
|
||||||
|
log("Dropping database: " + statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dropDatabaseFailed(int returnCode) {
|
||||||
|
log("Failed to drop the database; return code was " + returnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dropDatabaseComplete() {
|
||||||
|
log("Dropped database.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadDatabaseStarting(String statement) {
|
||||||
|
log("Loading the database: " + statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadDatabaseFailed(int returnCode) {
|
||||||
|
log("Failed to load the database; return code was " + returnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadDatabaseComplete() {
|
||||||
|
log("Loaded the database.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void webappStarting(String tomcatStartCommand) {
|
||||||
|
log("Starting tomcat: " + tomcatStartCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void webappStartFailed(int returnCode) {
|
||||||
|
log("Failed to start tomcat; return code was " + returnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void webappWaitingForStart(int tomcatStartDelay) {
|
||||||
|
log("Waiting " + tomcatStartDelay + " seconds for tomcat to start.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void webappStarted() {
|
||||||
|
log("Tomcat started.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subProcessStart(List<String> command) {
|
||||||
|
log("Subprocess started: " + command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subProcessStartInBackground(List<String> command) {
|
||||||
|
log("Subprocess started in background: " + command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subProcessStdout(String string) {
|
||||||
|
logRawText(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subProcessErrout(String string) {
|
||||||
|
logRawText(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subProcessStop(List<String> command) {
|
||||||
|
log("Subprocess stopped: " + command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void suiteStarted(File suiteDir) {
|
||||||
|
log("Suite started: " + suiteDir.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void suiteTestingStarted(File suiteDir) {
|
||||||
|
log("Suite testing started: " + suiteDir.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void suiteFailed(File suiteDir, int returnCode) {
|
||||||
|
log("Suite failed: " + suiteDir.getName() + ", returnCode="
|
||||||
|
+ returnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void suiteFailed(File suiteDir, Exception e) {
|
||||||
|
log("Suite failed: " + suiteDir.getName());
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void suiteTestingStopped(File suiteDir) {
|
||||||
|
log("Suite testing stopped: " + suiteDir.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void suiteStopped(File suiteDir) {
|
||||||
|
log("Suite stopped: " + suiteDir.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanUploadStart(File uploadDirectory) {
|
||||||
|
log("Upload cleaning started: " + uploadDirectory.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanUploadFailed(File uploadDirectory, IOException e) {
|
||||||
|
log("Upload cleaning failed: " + uploadDirectory.getPath());
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanUploadStop(File uploadDirectory) {
|
||||||
|
log("Upload cleaning stopped: " + uploadDirectory.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,117 +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.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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
public class Logger {
|
|
||||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
|
|
||||||
"yyyy-MM-dd HH:mm:ss.SSS");
|
|
||||||
|
|
||||||
private final Writer writer;
|
|
||||||
|
|
||||||
public Logger(PrintStream out) {
|
|
||||||
this.writer = new OutputStreamWriter(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Logger(File logFile) throws IOException {
|
|
||||||
this.writer = new FileWriter(logFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void runStarted() {
|
|
||||||
log("Run started.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void runStopped() {
|
|
||||||
log("Run stopped.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void subProcessStart(List<String> command) {
|
|
||||||
log("Subprocess started: " + command);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void subProcessStdout(String string) {
|
|
||||||
logRawText(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void subProcessErrout(String string) {
|
|
||||||
logRawText(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void subProcessStop(List<String> command) {
|
|
||||||
log("Subprocess stopped: " + command);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void suiteStarted(File suiteDir) {
|
|
||||||
log("Suite started: " + suiteDir.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void suiteFailed(File suiteDir, IOException e) {
|
|
||||||
log("Suite failed: " + suiteDir.getName());
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void suiteStopped(File suiteDir) {
|
|
||||||
log("Suite stopped: " + suiteDir.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cleanUploadStart(File uploadDirectory) {
|
|
||||||
log("Upload cleaning started: " + uploadDirectory.getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cleanUploadFailed(File uploadDirectory, IOException e) {
|
|
||||||
log("Upload cleaning failed: " + uploadDirectory.getPath());
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cleanUploadStop(File uploadDirectory) {
|
|
||||||
log("Upload cleaning stopped: " + uploadDirectory.getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -2,25 +2,215 @@
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* Resets the RDF-Model to a known state, in preparation for the next Selenium
|
||||||
|
* test suite.
|
||||||
*/
|
*/
|
||||||
public class ModelCleaner {
|
public class ModelCleaner {
|
||||||
|
private final ModelCleanerProperties properties;
|
||||||
|
private final CommandRunner runner;
|
||||||
|
private final Listener listener;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param parms
|
|
||||||
*/
|
|
||||||
public ModelCleaner(SeleniumRunnerParameters parms) {
|
public ModelCleaner(SeleniumRunnerParameters parms) {
|
||||||
// TODO Auto-generated constructor stub
|
this.properties = parms.getModelCleanerProperties();
|
||||||
throw new RuntimeException("ModelCleaner Constructor not implemented.");
|
this.listener = parms.getListener();
|
||||||
|
this.runner = new CommandRunner(parms);
|
||||||
|
|
||||||
|
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() {
|
public void clean() throws CommandRunnerException {
|
||||||
// TODO Auto-generated method stub
|
stopTheWebapp();
|
||||||
throw new RuntimeException("ModelCleaner.clean() not implemented.");
|
dropDatabase();
|
||||||
|
createAndLoadDatabase();
|
||||||
|
startTheWebapp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop Tomcat and wait the prescribed number of seconds for it to clean up.
|
||||||
|
*/
|
||||||
|
private void stopTheWebapp() throws CommandRunnerException {
|
||||||
|
String tomcatStopCommand = properties.getTomcatStopCommand();
|
||||||
|
int tomcatStopDelay = properties.getTomcatStopDelay();
|
||||||
|
|
||||||
|
listener.webappStopping(tomcatStopCommand);
|
||||||
|
runner.run(parseCommandLine(tomcatStopCommand));
|
||||||
|
|
||||||
|
int returnCode = runner.getReturnCode();
|
||||||
|
if (returnCode != 0) {
|
||||||
|
listener.webappStopFailed(returnCode);
|
||||||
|
// Throw no exception - this can happen if Tomcat isn't running.
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.webappWaitingForStop(tomcatStopDelay);
|
||||||
|
try {
|
||||||
|
Thread.sleep(tomcatStopDelay * 1000L);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Just continue.
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.webappStopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the database.
|
||||||
|
*/
|
||||||
|
private void dropDatabase() {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuild the database.
|
||||||
|
*/
|
||||||
|
private void createAndLoadDatabase() {
|
||||||
|
String mysqlStatement = "source "
|
||||||
|
+ convertBackslashes(properties.getMysqlDumpfile()) + ";";
|
||||||
|
|
||||||
|
listener.loadDatabaseStarting(mysqlStatement);
|
||||||
|
executeMysqlStatement(mysqlStatement);
|
||||||
|
|
||||||
|
int returnCode = runner.getReturnCode();
|
||||||
|
if (returnCode != 0) {
|
||||||
|
listener.loadDatabaseFailed(returnCode);
|
||||||
|
throw new FatalException("loadDatabase() failed: return code="
|
||||||
|
+ returnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.loadDatabaseComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start Tomcat and wait for it to initialize.
|
||||||
|
*/
|
||||||
|
private void startTheWebapp() {
|
||||||
|
String tomcatStartCommand = properties.getTomcatStartCommand();
|
||||||
|
int tomcatStartDelay = properties.getTomcatStartDelay();
|
||||||
|
|
||||||
|
listener.webappStarting(tomcatStartCommand);
|
||||||
|
try {
|
||||||
|
runner.runAsBackground(parseCommandLine(tomcatStartCommand));
|
||||||
|
} catch (CommandRunnerException e) {
|
||||||
|
throw new FatalException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
int returnCode = runner.getReturnCode();
|
||||||
|
if (returnCode != 0) {
|
||||||
|
listener.webappStartFailed(returnCode);
|
||||||
|
throw new FatalException("startTheWebapp() failed: return code="
|
||||||
|
+ returnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.webappWaitingForStart(tomcatStartDelay);
|
||||||
|
try {
|
||||||
|
Thread.sleep(tomcatStartDelay * 1000L);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Just continue.
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.webappStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String convertBackslashes(File file) {
|
||||||
|
return file.getPath().replace("\\", "/");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
/* $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_TOMCAT_START_COMMAND = "tomcat_start_command";
|
||||||
|
public static final String PROP_TOMCAT_START_DELAY = "tomcat_start_delay";
|
||||||
|
public static final String PROP_TOMCAT_STOP_COMMAND = "tomcat_stop_command";
|
||||||
|
public static final String PROP_TOMCAT_STOP_DELAY = "tomcat_stop_delay";
|
||||||
|
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_MYSQL_DUMPFILE = "mysql_dumpfile";
|
||||||
|
|
||||||
|
private final String tomcatStartCommand;
|
||||||
|
private final int tomcatStartDelay;
|
||||||
|
private final String tomcatStopCommand;
|
||||||
|
private final int tomcatStopDelay;
|
||||||
|
private final String mysqlUsername;
|
||||||
|
private final String mysqlPassword;
|
||||||
|
private final String mysqlDbName;
|
||||||
|
private final File mysqlDumpfile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirm that we have the expected properties, and that their values seem
|
||||||
|
* reasonable.
|
||||||
|
*/
|
||||||
|
public ModelCleanerProperties(Properties props) {
|
||||||
|
this.tomcatStartCommand = getRequiredProperty(props,
|
||||||
|
PROP_TOMCAT_START_COMMAND);
|
||||||
|
this.tomcatStartDelay = getRequiredIntegerProperty(props,
|
||||||
|
PROP_TOMCAT_START_DELAY);
|
||||||
|
|
||||||
|
this.tomcatStopCommand = getRequiredProperty(props,
|
||||||
|
PROP_TOMCAT_STOP_COMMAND);
|
||||||
|
this.tomcatStopDelay = getRequiredIntegerProperty(props,
|
||||||
|
PROP_TOMCAT_STOP_DELAY);
|
||||||
|
|
||||||
|
this.mysqlUsername = getRequiredProperty(props, PROP_MYSQL_USERNAME);
|
||||||
|
this.mysqlPassword = getRequiredProperty(props, PROP_MYSQL_PASSWORD);
|
||||||
|
this.mysqlDbName = getRequiredProperty(props, PROP_MYSQL_DB_NAME);
|
||||||
|
|
||||||
|
this.mysqlDumpfile = confirmDumpfile(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTomcatStartCommand() {
|
||||||
|
return tomcatStartCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTomcatStartDelay() {
|
||||||
|
return tomcatStartDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTomcatStopCommand() {
|
||||||
|
return tomcatStopCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTomcatStopDelay() {
|
||||||
|
return tomcatStopDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMysqlUsername() {
|
||||||
|
return mysqlUsername;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMysqlPassword() {
|
||||||
|
return mysqlPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMysqlDbName() {
|
||||||
|
return mysqlDbName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getMysqlDumpfile() {
|
||||||
|
return mysqlDumpfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dumpfile parameter must point to an existing file.
|
||||||
|
*/
|
||||||
|
private File confirmDumpfile(Properties props) {
|
||||||
|
String filename = getRequiredProperty(props, PROP_MYSQL_DUMPFILE);
|
||||||
|
File dumpfile = new File(filename);
|
||||||
|
if (!dumpfile.exists()) {
|
||||||
|
throw new IllegalArgumentException("Invalid value for '"
|
||||||
|
+ PROP_MYSQL_DUMPFILE + "': file '" + filename
|
||||||
|
+ "' does not exist.");
|
||||||
|
}
|
||||||
|
if (!dumpfile.isFile()) {
|
||||||
|
throw new IllegalArgumentException("Invalid value for '"
|
||||||
|
+ PROP_MYSQL_DUMPFILE + "': '" + filename
|
||||||
|
+ "' is not a file.");
|
||||||
|
}
|
||||||
|
if (!dumpfile.canRead()) {
|
||||||
|
throw new IllegalArgumentException("Invalid value for '"
|
||||||
|
+ PROP_MYSQL_DUMPFILE + "': file '" + filename
|
||||||
|
+ "' is not readable.");
|
||||||
|
}
|
||||||
|
return dumpfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "\n tomcatStartCommand: " + tomcatStartCommand
|
||||||
|
+ "\n tomcatStartDelay: " + tomcatStartDelay
|
||||||
|
+ "\n tomcatStopCommand: " + tomcatStopCommand
|
||||||
|
+ "\n tomcatStopDelay: " + tomcatStopDelay;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -13,23 +15,23 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class SeleniumRunner {
|
public class SeleniumRunner {
|
||||||
private final SeleniumRunnerParameters parms;
|
private final SeleniumRunnerParameters parms;
|
||||||
private final Logger logger;
|
private final Listener listener;
|
||||||
private final UploadAreaCleaner uploadCleaner;
|
private final UploadAreaCleaner uploadCleaner;
|
||||||
private final ModelCleaner modelCleaner;
|
private final ModelCleaner modelCleaner;
|
||||||
private final SuiteRunner suiteRunner;
|
private final SuiteRunner suiteRunner;
|
||||||
|
|
||||||
public SeleniumRunner(SeleniumRunnerParameters parms) {
|
public SeleniumRunner(SeleniumRunnerParameters parms) {
|
||||||
this.parms = parms;
|
this.parms = parms;
|
||||||
this.logger = parms.getLogger();
|
this.listener = parms.getListener();
|
||||||
this.uploadCleaner = new UploadAreaCleaner(parms);
|
this.uploadCleaner = new UploadAreaCleaner(parms);
|
||||||
this.modelCleaner = new ModelCleaner(parms);
|
this.modelCleaner = new ModelCleaner(parms);
|
||||||
this.suiteRunner = new SuiteRunner(parms);
|
this.suiteRunner = new SuiteRunner(parms);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runSelectedSuites() {
|
public void runSelectedSuites() {
|
||||||
logger.runStarted();
|
listener.runStarted();
|
||||||
for (File suiteDir : parms.getSelectedSuites()) {
|
for (File suiteDir : parms.getSelectedSuites()) {
|
||||||
logger.suiteStarted(suiteDir);
|
listener.suiteStarted(suiteDir);
|
||||||
try {
|
try {
|
||||||
if (parms.isCleanModel()) {
|
if (parms.isCleanModel()) {
|
||||||
modelCleaner.clean();
|
modelCleaner.clean();
|
||||||
|
@ -39,11 +41,17 @@ public class SeleniumRunner {
|
||||||
}
|
}
|
||||||
suiteRunner.runSuite(suiteDir);
|
suiteRunner.runSuite(suiteDir);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.suiteFailed(suiteDir, e);
|
listener.suiteFailed(suiteDir, e);
|
||||||
|
} catch (CommandRunnerException e) {
|
||||||
|
listener.suiteFailed(suiteDir, e);
|
||||||
|
} catch (FatalException e) {
|
||||||
|
listener.runFailed(e);
|
||||||
|
e.printStackTrace();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
logger.suiteStopped(suiteDir);
|
listener.suiteStopped(suiteDir);
|
||||||
}
|
}
|
||||||
logger.runStopped();
|
listener.runStopped();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void selectAllSuites(SeleniumRunnerParameters parms) {
|
private static void selectAllSuites(SeleniumRunnerParameters parms) {
|
||||||
|
@ -54,9 +62,13 @@ public class SeleniumRunner {
|
||||||
parms.setSelectedSuites(suites);
|
parms.setSelectedSuites(suites);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static void usage(String message) {
|
||||||
* @param args
|
System.out.println(message);
|
||||||
*/
|
System.out.println("Usage is: SeleniumRunner <parameters_file> "
|
||||||
|
+ "[\"interactive\"]");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SeleniumRunnerParameters parms = null;
|
SeleniumRunnerParameters parms = null;
|
||||||
boolean interactive = false;
|
boolean interactive = false;
|
||||||
|
@ -82,20 +94,20 @@ public class SeleniumRunner {
|
||||||
// TODO hook up the GUI.
|
// TODO hook up the GUI.
|
||||||
throw new RuntimeException("interactive mode not implemented.");
|
throw new RuntimeException("interactive mode not implemented.");
|
||||||
} else {
|
} else {
|
||||||
|
File logFile = new File(parms.getOutputDirectory(), LOGFILE_NAME);
|
||||||
|
System.out.println("Log file is '" + logFile.getPath() + "'");
|
||||||
|
|
||||||
// Run all of the suites.
|
// Run all of the suites.
|
||||||
// For each suite, clean the model and the upload area.
|
// For each suite, clean the model and the upload area.
|
||||||
selectAllSuites(parms);
|
selectAllSuites(parms);
|
||||||
parms.setCleanModel(true);
|
parms.setCleanModel(true);
|
||||||
parms.setCleanUploads(true);
|
parms.setCleanUploads(true);
|
||||||
|
|
||||||
|
System.out.println(parms);
|
||||||
|
|
||||||
SeleniumRunner runner = new SeleniumRunner(parms);
|
SeleniumRunner runner = new SeleniumRunner(parms);
|
||||||
runner.runSelectedSuites();
|
runner.runSelectedSuites();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void usage(String message) {
|
|
||||||
System.out.println(message);
|
|
||||||
System.out.println("Usage is: SeleniumRunner <parameters_file> "
|
|
||||||
+ "[\"interactive\"]");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,22 +19,32 @@ import java.util.Properties;
|
||||||
* with modifications from the GUI if we are running interactively.
|
* with modifications from the GUI if we are running interactively.
|
||||||
*/
|
*/
|
||||||
public class SeleniumRunnerParameters {
|
public class SeleniumRunnerParameters {
|
||||||
private static final String PROP_OUTPUT_DIRECTORY = "output_directory";
|
public static final String PROP_OUTPUT_DIRECTORY = "output_directory";
|
||||||
private static final String PROP_UPLOAD_DIRECTORY = "upload_directory";
|
public static final String PROP_UPLOAD_DIRECTORY = "upload_directory";
|
||||||
private static final String PROP_SUITE_DIRECTORIES = "suite_parent_directories";
|
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";
|
||||||
|
|
||||||
private static final String LOGFILE_NAME = "log_file.txt";
|
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 uploadDirectory;
|
||||||
private final File outputDirectory;
|
private final File outputDirectory;
|
||||||
private final File logFile;
|
private final File logFile;
|
||||||
|
|
||||||
private final Collection<File> suiteParentDirectories;
|
private final Collection<File> suiteParentDirectories;
|
||||||
|
private final ModelCleanerProperties modelCleanerProperties;
|
||||||
|
|
||||||
private Collection<File> selectedSuites = Collections.emptySet();
|
private Collection<File> selectedSuites = Collections.emptySet();
|
||||||
private boolean cleanModel = true;
|
private boolean cleanModel = true;
|
||||||
private boolean cleanUploads = true;
|
private boolean cleanUploads = true;
|
||||||
private Logger logger = new Logger(System.out);
|
private Listener listener = new Listener(System.out);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the required properties from the property file, and do some checks
|
* Read the required properties from the property file, and do some checks
|
||||||
|
@ -48,15 +58,26 @@ public class SeleniumRunnerParameters {
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
props.load(propsReader);
|
props.load(propsReader);
|
||||||
|
|
||||||
|
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,
|
this.uploadDirectory = checkReadWriteDirectory(props,
|
||||||
PROP_UPLOAD_DIRECTORY);
|
PROP_UPLOAD_DIRECTORY);
|
||||||
|
|
||||||
this.outputDirectory = checkReadWriteDirectory(props,
|
this.outputDirectory = checkReadWriteDirectory(props,
|
||||||
PROP_OUTPUT_DIRECTORY);
|
PROP_OUTPUT_DIRECTORY);
|
||||||
this.logFile = new File(this.outputDirectory, LOGFILE_NAME);
|
this.logFile = new File(this.outputDirectory, LOGFILE_NAME);
|
||||||
this.logger = new Logger(this.logFile);
|
this.listener = new Listener(this.logFile);
|
||||||
|
|
||||||
this.suiteParentDirectories = checkSuiteParentDirectories(props);
|
this.suiteParentDirectories = checkSuiteParentDirectories(props);
|
||||||
|
|
||||||
|
this.modelCleanerProperties = new ModelCleanerProperties(props);
|
||||||
} finally {
|
} finally {
|
||||||
if (propsReader != null) {
|
if (propsReader != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -68,6 +89,36 @@ public class SeleniumRunnerParameters {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 output directory, and that it
|
* Check that there is a property for the output directory, and that it
|
||||||
* points to a valid directory.
|
* points to a valid directory.
|
||||||
|
@ -99,6 +150,26 @@ public class SeleniumRunnerParameters {
|
||||||
return dir;
|
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
|
||||||
|
+ ": '' does not exist.");
|
||||||
|
}
|
||||||
|
if (!file.isFile()) {
|
||||||
|
throw new IllegalArgumentException("File " + key
|
||||||
|
+ ": '' is not a file.");
|
||||||
|
}
|
||||||
|
if (!file.canRead()) {
|
||||||
|
throw new IllegalArgumentException("File " + key
|
||||||
|
+ ": '' is not readable.");
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the property for the suite directories and ensure that each one is
|
* Get the property for the suite directories and ensure that each one is
|
||||||
* indeed a readable directory.
|
* indeed a readable directory.
|
||||||
|
@ -123,6 +194,7 @@ public class SeleniumRunnerParameters {
|
||||||
throw new IllegalArgumentException("Suite directory '"
|
throw new IllegalArgumentException("Suite directory '"
|
||||||
+ dir.getPath() + "' is not readable.");
|
+ dir.getPath() + "' is not readable.");
|
||||||
}
|
}
|
||||||
|
dirs.add(dir);
|
||||||
}
|
}
|
||||||
return dirs;
|
return dirs;
|
||||||
}
|
}
|
||||||
|
@ -140,22 +212,67 @@ public class SeleniumRunnerParameters {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Logger getLogger() {
|
/**
|
||||||
return logger;
|
* 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 void setLogger(Logger logger) {
|
public String getWebsiteUrl() {
|
||||||
this.logger = logger;
|
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 Listener getListener() {
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListener(Listener logger) {
|
||||||
|
this.listener = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getUploadDirectory() {
|
public File getUploadDirectory() {
|
||||||
return uploadDirectory;
|
return uploadDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public File getOutputDirectory() {
|
||||||
|
return outputDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<File> getSuiteParentDirectories() {
|
public Collection<File> getSuiteParentDirectories() {
|
||||||
return suiteParentDirectories;
|
return suiteParentDirectories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ModelCleanerProperties getModelCleanerProperties() {
|
||||||
|
return modelCleanerProperties;
|
||||||
|
}
|
||||||
|
|
||||||
public void setSelectedSuites(Collection<File> selectedSuites) {
|
public void setSelectedSuites(Collection<File> selectedSuites) {
|
||||||
this.selectedSuites = selectedSuites;
|
this.selectedSuites = selectedSuites;
|
||||||
}
|
}
|
||||||
|
@ -180,22 +297,50 @@ public class SeleniumRunnerParameters {
|
||||||
this.cleanUploads = cleanUploads;
|
this.cleanUploads = cleanUploads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "Parameters:" + "\n websiteUrl: " + websiteUrl
|
||||||
|
+ "\n userExtensionsFile: " + userExtensionsFile.getPath()
|
||||||
|
+ "\n firefoxProfileDir: " + firefoxProfileDir.getPath()
|
||||||
|
+ "\n suiteTimeoutLimit: " + suiteTimeoutLimit
|
||||||
|
+ "\n seleniumJarPath: " + seleniumJarPath.getPath()
|
||||||
|
+ "\n uploadDirectory: " + uploadDirectory.getPath()
|
||||||
|
+ "\n outputDirectory: " + outputDirectory.getPath()
|
||||||
|
+ "\n suiteParentDirectories: " + suiteParentDirectories
|
||||||
|
+ "\n modelCleanerProperties: " + modelCleanerProperties
|
||||||
|
+ "\n\n selectedSuites: " + showSelectedSuites()
|
||||||
|
+ "\n cleanModel: " + cleanModel + "\n cleanUploads: "
|
||||||
|
+ cleanUploads;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String showSelectedSuites() {
|
||||||
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
for (File suite : selectedSuites) {
|
||||||
|
buffer.append("\n ").append(suite.getPath());
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look inside this parent directory and find any suite directories. You can
|
* Look inside this parent directory and find any suite directories. You can
|
||||||
* recognize a suite directory because it contains a file named Suite.html.
|
* recognize a suite directory because it contains a file named Suite.html.
|
||||||
*/
|
*/
|
||||||
public Collection<File> findSuiteDirs(File parentDir) {
|
public Collection<File> findSuiteDirs(File parentDir) {
|
||||||
|
System.out.println("parentDir: " + parentDir);
|
||||||
return Arrays.asList(parentDir.listFiles(new FileFilter() {
|
return Arrays.asList(parentDir.listFiles(new FileFilter() {
|
||||||
public boolean accept(File pathname) {
|
public boolean accept(File pathname) {
|
||||||
if (!pathname.isDirectory()) {
|
if (!pathname.isDirectory()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (pathname.getName().charAt(0) == '.') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
File suiteFile = new File(pathname, "Suite.html");
|
File suiteFile = new File(pathname, "Suite.html");
|
||||||
if (suiteFile.exists()) {
|
if (suiteFile.exists()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
logger.subProcessErrout("Warning: suite file '" + suiteFile.getPath()
|
listener.subProcessErrout("Warning: suite file '"
|
||||||
+ "' does not exist.\n");
|
+ suiteFile.getPath() + "' does not exist.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,26 +3,68 @@
|
||||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* Run a Selenium TestSuite in a sub-process.
|
||||||
*/
|
*/
|
||||||
public class SuiteRunner {
|
public class SuiteRunner {
|
||||||
|
|
||||||
/**
|
private final SeleniumRunnerParameters parms;
|
||||||
* @param parms
|
private final CommandRunner runner;
|
||||||
*/
|
private final Listener listener;
|
||||||
|
|
||||||
public SuiteRunner(SeleniumRunnerParameters parms) {
|
public SuiteRunner(SeleniumRunnerParameters parms) {
|
||||||
// TODO Auto-generated constructor stub
|
this.parms = parms;
|
||||||
throw new RuntimeException("SuiteRunner Constructor not implemented.");
|
this.runner = new CommandRunner(parms);
|
||||||
|
this.listener = parms.getListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param suiteDir
|
* Run the suite.
|
||||||
*/
|
*/
|
||||||
public void runSuite(File suiteDir) {
|
public void runSuite(File suiteDir) {
|
||||||
// TODO Auto-generated method stub
|
listener.suiteTestingStarted(suiteDir);
|
||||||
throw new RuntimeException("SuiteRunner.runSuite() not implemented.");
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,11 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public class UploadAreaCleaner {
|
public class UploadAreaCleaner {
|
||||||
private final SeleniumRunnerParameters parms;
|
private final SeleniumRunnerParameters parms;
|
||||||
private final Logger logger;
|
private final Listener listener;
|
||||||
|
|
||||||
public UploadAreaCleaner(SeleniumRunnerParameters parms) {
|
public UploadAreaCleaner(SeleniumRunnerParameters parms) {
|
||||||
this.parms = parms;
|
this.parms = parms;
|
||||||
this.logger = parms.getLogger();
|
this.listener = parms.getListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +29,7 @@ public class UploadAreaCleaner {
|
||||||
+ "' is not a directory.");
|
+ "' is not a directory.");
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.cleanUploadStart(uploadDirectory);
|
listener.cleanUploadStart(uploadDirectory);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (File file : uploadDirectory.listFiles()) {
|
for (File file : uploadDirectory.listFiles()) {
|
||||||
|
@ -40,10 +40,10 @@ public class UploadAreaCleaner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.cleanUploadFailed(uploadDirectory, e);
|
listener.cleanUploadFailed(uploadDirectory, e);
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
logger.cleanUploadStop(uploadDirectory);
|
listener.cleanUploadStop(uploadDirectory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
public class ModelCleanerTest {
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Tests for parseCommandLine()
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void oneArgument() {
|
||||||
|
assertExpectedParsing("oneArgument", "oneArgument");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleArguments() {
|
||||||
|
assertExpectedParsing("more than one", "more", "than", "one");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void quotedArgument() {
|
||||||
|
assertExpectedParsing("contains \"quoted blank\" string", "contains",
|
||||||
|
"quoted blank", "string");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void mismatchedQuotes() {
|
||||||
|
assertExpectedParsing("contains mismatched \"quote");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void emptyLine() {
|
||||||
|
assertExpectedParsing("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertExpectedParsing(String commandLine,
|
||||||
|
String... expectedPieces) {
|
||||||
|
assertEquals("parse", Arrays.asList(expectedPieces), ModelCleaner
|
||||||
|
.parseCommandLine(commandLine));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue