com.greenpepper.maven.plugin.SpecificationRunnerMojo Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2007 Pyxis Technologies inc.
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA,
* or see the FSF site: http://www.fsf.org.
*/
package com.greenpepper.maven.plugin;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.greenpepper.Statistics;
import com.greenpepper.document.GreenPepperInterpreterSelector;
import com.greenpepper.maven.plugin.runner.Runner;
import com.greenpepper.maven.plugin.runner.RunnerTask;
import com.greenpepper.maven.plugin.utils.NullStatisticsListener;
import com.greenpepper.maven.plugin.utils.ProjectsIndex;
import com.greenpepper.maven.plugin.utils.RepositoryIndex;
import com.greenpepper.maven.plugin.utils.TestResultsIndex;
import com.greenpepper.repository.DocumentRepository;
import com.greenpepper.repository.FileSystemRepository;
import com.greenpepper.runner.CompositeSpecificationRunnerMonitor;
import com.greenpepper.runner.RecorderMonitor;
import com.greenpepper.runner.RunnerStatistics;
import com.greenpepper.runner.SpecificationRunnerMonitor;
import com.greenpepper.runner.repository.DocumentNeverImplementedException;
import com.greenpepper.server.domain.*;
import com.greenpepper.util.IOUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.jsoup.Jsoup;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.concurrent.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import static com.greenpepper.maven.plugin.runner.Runner.DEFAULT_RUNNER_NAME;
import static com.greenpepper.util.URIUtil.escapeFileSystemForbiddenCharacters;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static org.apache.commons.io.FileUtils.*;
import static org.apache.commons.lang3.StringUtils.*;
/**
* Run the specification testing.
*
* @goal run
* @phase integration-test
* @requiresDependencyResolution test
* @description Runs GreenPepper specifications
* @author oaouattara
* @version $Id: $Id
*/
@SuppressWarnings("JavaDoc")
public class SpecificationRunnerMojo extends SpecificationNavigatorMojo {
private static final Logger LOGGER = LoggerFactory.getLogger(SpecificationRunnerMojo.class);
private static final String HTML_EXTENSION = ".html";
private class RunnerResult {
private final Future> future;
private final RunnerTask runnerTask;
RunnerResult(RunnerTask runnerTask, Future> future) {
this.runnerTask = runnerTask;
this.future = future;
}
}
/**
* Set this to 'true' to bypass greenpepper tests entirely.
* Its use is NOT RECOMMENDED, but quite convenient on occasion.
*
* @parameter property="maven.greenpepper.test.skip" default-value="false"
*/
@SuppressWarnings("unused")
private boolean skip;
/**
* Project fixture classpath.
* @parameter property="project.runtimeClasspathElements"
* @required
* @readonly
*/
List classpathElements;
/**
* The directory where compiled fixture classes go.
*
* @parameter default-value="${project.build.directory}/fixture-test-classes"
* @required
*/
@SuppressWarnings("unused")
private File fixtureOutputDirectory;
/**
* The SystemUnderDevelopment class to use
* @parameter default-value="com.greenpepper.systemunderdevelopment.DefaultSystemUnderDevelopment"
* @required
*/
String systemUnderDevelopment;
/**
* The {@link com.greenpepper.systemunderdevelopment.SystemUnderDevelopment} constructor args.
* This parameter is optionnal and can be achieved by appending them to the systemUnderDevelopment parameter.
* @parameter
*/
String systemUnderDevelopmentArgs;
/**
* @parameter property="plugin.artifacts"
* @required
* @readonly
*/
List pluginDependencies;
/**
* Set this to 'true' to stop the execution on a failure.
* @parameter property="maven.greenpepper.test.stop" default-value="false"
*/
@SuppressWarnings("unused")
private boolean stopOnFirstFailure;
/**
* Set the locale for the execution.
* @parameter property="maven.greenpepper.locale"
*/
String locale;
/**
* Set the Selector class.
* @parameter property="maven.greenpepper.selector"
* default-value="com.greenpepper.document.GreenPepperInterpreterSelector"
*/
String selector;
/**
* Set the Debug mode.
*
* @parameter property="maven.greenpepper.debug" default-value="false"
*/
boolean debug;
/**
* Set this to true to ignore a failure during testing.
* Its use is NOT RECOMMENDED, but quite convenient on occasion.
*
* @parameter property="maven.greenpepper.test.failure.ignore" default-value="false"
*/
@SuppressWarnings("unused")
private boolean testFailureIgnore;
/**
* Set this to true to output the logs only in the log file and not in the console.
*
* @parameter property="maven.greenpepper.redirect.output" default-value="false"
*/
@SuppressWarnings("unused")
private boolean redirectOutputToFile;
/**
* Set this property to true to launch only new specifications + failed ones.
*
* @parameter property="maven.greenpepper.resume" default-value="false"
*/
boolean resume;
/**
* Set this to a Specification name to run only this test.
* The test is searched inside the default repository.
*
* @parameter property="gp.test"
*/
String testSpecification;
/**
* Set this to a Specification name to run only this test.
* The test is searched inside the default repository.
*
* @parameter property="gp.testOutput"
*/
String testSpecificationOutput;
/**
* Set this to a Repository name defined in the pom.xml.
* This option is only used in case -Dgp.test
is used.
*
* @parameter property="gp.repo"
*/
String selectedRepository;
/**
* Launch the test in the Maven process if false. Or fork a java process if true.
*
* @parameter property="maven.greenpepper.fork" default-value="false"
*/
boolean fork;
/**
* The maximum number of default runner processes that needs to be spawn;
*
* @parameter property="maven.greenpepper.forkcount" default=1
*/
Integer forkCount;
/**
* The Java Virtual Machine path to use for the default runner in fork mode.
*
* @parameter property="maven.greenpepper.jvm" default-value="java"
*/
@SuppressWarnings("FieldCanBeLocal")
private String jvm = "java";
/**
* Additionnal JAVA Options to be added to the java command in fork mode.
*
* This is only used in FORK mode and for the default runner.
*
* @parameter property="maven.greenpepper.javaoptions"
*/
String javaOptions;
/**
* When launching the tests in a fork, we create a default runner. You can exclude this default runner from the
* testing process if you want to configure your owns.
*
* @parameter property="maven.greenpepper.excludedefaultrunner" default-value=false
*/
boolean excludeDefaultRunner;
/**
* The list of runners that can be associated to repositories for testing.
*
* @parameter
*/
List runners;
/**
* @component
*/
protected MavenProject project;
private HashMap executorMap = new HashMap();
HashMap runnerMap = new HashMap();
private LinkedHashSet runnerResults = new LinkedHashSet();
RunnerStatistics runnerStatistics;
private boolean testFailed;
private boolean exceptionOccured;
HashMap testResultsIndexes = new HashMap();
/**
* Constructor for SpecificationRunnerMojo.
*/
public SpecificationRunnerMojo() {
this.runnerStatistics = new RunnerStatistics();
}
/**
* execute.
*
* @throws org.apache.maven.plugin.MojoExecutionException if any.
* @throws org.apache.maven.plugin.MojoFailureException if any.
*/
public void execute() throws MojoExecutionException, MojoFailureException {
if (skip) {
getLog().info("Not executing specifications.");
} else {
prepareReportsDir();
prepareRunnerExecutors();
printBanner();
try {
runAllTests();
} finally {
printFooter();
for (TestResultsIndex testResultsIndex : testResultsIndexes.values()) {
testResultsIndex.dump();
}
}
checkTestsResults();
}
}
private void prepareRunnerExecutors() {
if (runners == null || runners.isEmpty()) {
runners = new ArrayList();
if ( !excludeDefaultRunner ) {
runners.add(getDefaultRunner());
}
}
for (Runner runner : runners) {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(runner.getName()+ "-%d").build();
ExecutorService executorService = Executors.newFixedThreadPool(runner.getForkCount(), threadFactory);
executorMap.put(runner.getName(), executorService);
runnerMap.put(runner.getName(), runner);
if (fork) {
runner.setRedirectOutputToFile(true);
}
}
}
private void checkTestsResults() throws MojoExecutionException, MojoFailureException {
if (exceptionOccured)
notifyExceptionsOccured();
if (testFailed)
notifyTestsFailed();
}
private void notifyExceptionsOccured() throws MojoExecutionException {
if (testFailureIgnore) {
getLog().error("Some greenpepper tests did not run\n");
} else {
throw new MojoExecutionException("Some greenpepper tests did not run");
}
}
private void notifyTestsFailed() throws MojoFailureException {
if (!testResultsIndexes.isEmpty()) {
System.out.println("List of failed tests");
for (Map.Entry indexEntry : testResultsIndexes.entrySet()) {
String repoName = indexEntry.getKey();
TestResultsIndex testResultsIndex = indexEntry.getValue();
for (Map.Entry entry : testResultsIndex.getNameToInfo().entrySet()) {
Statistics statistics = entry.getValue().getStatistics();
if (statistics.hasFailed()) {
System.out.println(format("\t %s (Repository: %s) (%s)", entry.getKey(), repoName, statistics));
}
}
}
System.out.println();
System.out.println("You can run the failed tests using the resume option '-Dmaven.greenpepper.resume=true'");
System.out.println();
}
if (testFailureIgnore) {
getLog().error("There were greenpepper tests failures\n");
} else {
throw new MojoFailureException("There were greenpepper tests failures");
}
}
private void printBanner() {
System.out.println();
System.out.println("-----------------------------------------------------");
System.out.println(" G R E E N P E P P E R S P E C I F I C A T I O N S ");
System.out.println("-----------------------------------------------------");
System.out.println();
}
private void runAllTests() throws MojoExecutionException, MojoFailureException {
if (StringUtils.isNotEmpty(testSpecification)) {
// Locate default repository
Repository defaultRepository = null;
if (repositories.size() == 1) {
defaultRepository = repositories.get(0);
} else {
boolean repositorySelected = StringUtils.isNotEmpty(selectedRepository);
if (repositorySelected) {
defaultRepository = extractSelectedRepository();
} else {
for (Repository repository : repositories) {
if (repository.isDefault()) {
defaultRepository = repository;
break;
}
}
}
}
if (defaultRepository == null) {
throw new MojoExecutionException("A default repository should be set when using '-Dgp.test='. Use '-Dgp.repo=' or specify it in the pom.xml");
}
// Run the test
runSingleTest(defaultRepository, testSpecification);
checkAsynchTasks();
} else {
boolean repositorySelected = StringUtils.isNotEmpty(selectedRepository);
if (repositorySelected) {
runAllIn(extractSelectedRepository());
} else {
for (Repository repository : repositories) {
if (shouldStop()) {
break;
}
runAllIn(repository);
}
}
}
}
private Repository extractSelectedRepository() throws MojoExecutionException {
for (Repository repository : repositories) {
if (StringUtils.equalsIgnoreCase(selectedRepository, repository.getName())) {
return repository;
}
}
throw new MojoExecutionException(format("Repository '%s' not found in the list of repository.", selectedRepository));
}
private void checkAsynchTasks() {
for (RunnerResult runnerResult : runnerResults) {
try {
// This will wait for this Task completion
runnerResult.future.get();
RecorderMonitor recorder = runnerResult.runnerTask.getRecorder();
exceptionOccured |= recorder.hasException();
testFailed |= recorder.hasTestFailures();
registerStatistics(recorder);
} catch (InterruptedException e) {
getLog().error(format("The task [%s] has been interrupted", runnerResult.runnerTask.getSpecification().getName()));
} catch (ExecutionException e) {
getLog().error(format("The task [%s] has failed", runnerResult.runnerTask.getSpecification().getName()), e);
}
}
runnerResults.clear();
}
private void runAllIn(Repository repository) throws MojoExecutionException, MojoFailureException {
List repositorySpecifications;
try {
repositorySpecifications = listRepositorySpecifications(repository);
} catch (Exception e) {
throw new MojoExecutionException(format("Couldn't list repository '%s' specifications", repository.getName()), e);
}
try {
extractHtmlReportSummary();
prepareProjectIndex(repository);
prepareTestResultsIndex(repository);
} catch (URISyntaxException e) {
throw new MojoExecutionException(format("Couldn't prepare the report for repository '%s'", repository.getName()), e);
} catch (IOException e) {
throw new MojoExecutionException(format("Couldn't prepare the report for repository '%s'", repository.getName()), e);
}
repository.getTests().clear();
repository.getTests().addAll(repositorySpecifications);
runTestsIn(repository);
checkAsynchTasks();
System.out.println();
System.out.println(format("See the report at %s", getFile(reportsDirectory, "index" + HTML_EXTENSION).getAbsolutePath()));
System.out.println();
}
private void prepareProjectIndex(Repository repository) throws IOException {
File storage = new File(reportsDirectory, "index.json");
ProjectsIndex projectsIndex = new ProjectsIndex(storage);
try {
projectsIndex.load();
} catch (IOException e) {
getLog().warn(format("index.json is corrupted. Start a new one. Cause: %s", e.getMessage()));
FileUtils.moveFile(storage, new File(reportsDirectory, "index.json.orig"));
}
ProjectsIndex.ProjectInfo projectInfo = projectsIndex.getNameToInfo().get(repository.getName());
if (projectInfo == null) {
projectInfo = new ProjectsIndex.ProjectInfo();
projectInfo.projectName = repository.getProjectName();
projectInfo.repoName = repository.getName();
projectInfo.repoId = getRepositoryMetaName(repository);
projectInfo.systemUnderTest = repository.getSystemUnderTest();
projectInfo.startDate = ProjectsIndex.ProjectInfo.SIMPLE_DATE_FORMAT.format(new Date());
projectsIndex.getNameToInfo().put(repository.getName(), projectInfo);
} else {
projectInfo.startDate = ProjectsIndex.ProjectInfo.SIMPLE_DATE_FORMAT.format(new Date());
}
projectsIndex.dump();
}
private void prepareTestResultsIndex(Repository repository) throws IOException {
File storage = getResultsIndexFile(repository);
TestResultsIndex testResultsIndex = TestResultsIndex.newInstance(storage);
testResultsIndexes.put(repository.getName(),testResultsIndex);
}
private void extractHtmlReportSummary() throws IOException, URISyntaxException {
final String path = "html-summary-report";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
forceMkdir(reportsDirectory);
if(jarFile.isFile()) { // Run with JAR file
JarFile jar = new JarFile(jarFile);
Enumeration entries = jar.entries(); //gives ALL entries in jar
while(entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String name = jarEntry.getName();
if (name.startsWith(path)) { //filter according to the path
File file = getFile(reportsDirectory, substringAfter(name, path));
if (jarEntry.isDirectory()) {
forceMkdir(file);
} else {
forceMkdir(file.getParentFile());
if (!file.exists()) {
copyInputStreamToFile(jar.getInputStream(jarEntry), file);
}
}
}
}
jar.close();
} else { // Run with IDE
URL url = getClass().getResource("/" + path);
if (url != null) {
File apps = FileUtils.toFile(url);
if (apps.isDirectory()) {
copyDirectory(apps, reportsDirectory);
} else {
throw new IllegalStateException(format("Internal resource '%s' should be a directory.", apps.getAbsolutePath()));
}
} else {
throw new IllegalStateException(format("Internal resource '/%s' should be here.", path));
}
}
}
File getResultsIndexFile(Repository repository) throws UnsupportedEncodingException {
return new File(reportsDirectory, getRepositoryMetaName(repository) + ".results");
}
private void runTestsIn(Repository repository) throws MojoExecutionException, MojoFailureException {
if (!resume) {
TestResultsIndex testResultsIndex = testResultsIndexes.get(repository.getName());
if (testResultsIndex != null) {
testResultsIndex.getNameToInfo().clear();
}
}
for (String test : repository.getTests()) {
if (shouldStop()) {
break;
}
runSingleTest(repository, test);
}
}
private void runSingleTest(Repository repository, String test) throws MojoExecutionException, MojoFailureException {
if (resume) {
// Resume set, we will run the test only if it's new or failing;
TestResultsIndex testResultsIndex = testResultsIndexes.get(repository.getName());
if (testResultsIndex != null) {
Map results = testResultsIndex.getNameToInfo();
if (results.containsKey(test) && !results.get(test).getStatistics().hasFailed()) {
getLog().info(format("Skipping %s due to 'resume' option set", test));
return;
}
}
}
String repoCmdOption;
boolean managingFileSystem;
try {
DocumentRepository documentRepository = repository.getDocumentRepository();
managingFileSystem = FileSystemRepository.class.isAssignableFrom(documentRepository.getClass());
} catch (Exception e) {
throw new MojoFailureException("Unable to get the document repository", e);
}
if (managingFileSystem) {
File projectBasedir = project.getBasedir();
repoCmdOption = repository.getType() + ";";
if (repository.getRoot() != null){
File relativeRoot = new File(repository.getRoot());
File absoluteDir;
if (relativeRoot.getAbsoluteFile().compareTo(relativeRoot) == 0) {
absoluteDir = relativeRoot;
} else {
absoluteDir = new File(projectBasedir,repository.getRoot());
}
repoCmdOption += absoluteDir.getAbsolutePath();
} else {
repoCmdOption += projectBasedir.getAbsolutePath();
}
} else {
repoCmdOption = repository.getType() + (repository.getRoot() != null ? ";" + repository.getRoot() : "");
}
File repositoryReportsFolder = new File(reportsDirectory, repository.getName());
runnerStatistics.addToTotal(1);
if (fork || isNotBlank(repository.getRunnerName())) {
// If the repository specified a runner
runInForkedRunner(repository, test, repositoryReportsFolder);
} else {
runInEmbeddedRunner(repository, test, repoCmdOption, repositoryReportsFolder);
}
}
private void runInForkedRunner(Repository repository, String test, File repositoryReportsFolder) throws MojoExecutionException {
File outputFile = new File(repositoryReportsFolder, test);
SystemUnderTest systemUnderTest = new SystemUnderTest();
systemUnderTest.setName(repository.getSystemUnderTest());
systemUnderTest.setProject(Project.newInstance(repository.getProjectName()));
Specification specification = Specification.newInstance(test);
com.greenpepper.server.domain.Repository repositoryRunner = com.greenpepper.server.domain.Repository.newInstance(repository.getName());
RepositoryType repositoryType = RepositoryType.newInstance("FILE");
repositoryType.setRepositoryClass(repository.getType());
EnvironmentType java = EnvironmentType.newInstance("JAVA");
repositoryType.registerClassForEnvironment(repository.getType(), java);
repositoryRunner.setBaseTestUrl(repository.getRoot());
repositoryRunner.setType(repositoryType);
specification.setRepository(repositoryRunner);
systemUnderTest.setFixtureFactory(systemUnderDevelopment);
systemUnderTest.setFixtureFactoryArgs(systemUnderDevelopmentArgs);
specification.setDialectClass(repository.getDialect());
String runnerName = repository.getRunnerName();
if (runnerName == null) {
if (runnerMap.size() == 1) {
runnerName = runnerMap.keySet().iterator().next();
} else {
runnerName = DEFAULT_RUNNER_NAME;
}
}
Runner defaultRunner = runnerMap.get(runnerName);
ExecutorService executorService = executorMap.get(runnerName);
if (defaultRunner != null && executorService != null ) {
if (defaultRunner.getForkCount() == 1) {
defaultRunner.setRedirectOutputToFile(redirectOutputToFile);
}
// We will try to get the external-link after the test (for version of Greepepper < 4.1 )
defaultRunner.setRepositoryIndex(this.repositoryIndexes.get(repository.getName()));
TestResultsIndex testResultsIndex = testResultsIndexes.get(repository.getName());
RunnerTask runnerTask = new RunnerTask(specification, systemUnderTest, outputFile.getAbsolutePath(), defaultRunner, getLog(), testResultsIndex);
if (defaultRunner.isIncludeProjectClasspath()) {
TreeSet classpath = new TreeSet();
for (URL url : createClasspath()) {
classpath.add(FileUtils.toFile(url).getAbsolutePath());
}
systemUnderTest.setSutClasspaths(classpath);
}
Future> future = executorService.submit(runnerTask);
RunnerResult runnerResult = new RunnerResult(runnerTask, future);
runnerResults.add(runnerResult);
} else {
getLog().warn(format("No runner found for executing %s in repository %s. Runner '%s' was specified (or falled back to)",
specification.getName(), repository.getName(), runnerName));
}
}
private Runner getDefaultRunner() {
List optionsList = new ArrayList();
appendOptionsList(optionsList);
Runner defaultRunner = Runner.createDefault(jvm, javaOptions, optionsList);
if (forkCount != null) {
defaultRunner.setForkCount(forkCount);
}
return defaultRunner;
}
private void runInEmbeddedRunner(Repository repository, String test, String repoCmdOption, File repositoryReportsFolder) throws MojoExecutionException, MojoFailureException {
String outputDir = repositoryReportsFolder.getAbsolutePath();
String systemUnderDevelopmentWithArgs = getFixtureFactoryWithArgs();
List args = new ArrayList();
args.addAll(asList("-f", systemUnderDevelopmentWithArgs, "-r", repoCmdOption, "-o", outputDir));
if (isNotBlank(repository.getDialect())) {
List dialectOption = asList("-d", repository.getDialect());
args.addAll(dialectOption);
}
appendOptionsList(args);
args.add(test);
// Add the target output (which might have the same name as the test
String output = null;
if (StringUtils.isNoneEmpty(testSpecification, testSpecificationOutput)) {
output = testSpecificationOutput;
} else if (endsWithIgnoreCase(test, HTML_EXTENSION)) {
output = test;
}
if (isNotBlank(output)) {
args.add(output);
}
// try to define the output file
File outputFile;
if (isNotBlank(output)) {
outputFile = getFile(outputDir, escapeFileSystemForbiddenCharacters(output));
} else {
outputFile = getFile(outputDir, escapeFileSystemForbiddenCharacters(test));
}
File outLogFile = new File(outputFile.getAbsolutePath() + "-output.log");
File errLogFile = new File(outputFile.getAbsolutePath() + "-err.log");
LogWriterMonitor logWriterMonitor = new LogWriterMonitor(outLogFile, errLogFile);
TestMonitor testMonitor = new TestMonitor(getLog(), new NullStatisticsListener());
RecorderMonitor recorderMonitor = run(args, logWriterMonitor, testMonitor);
TestResultsIndex testResultsIndex = testResultsIndexes.get(repository.getName());
if (testResultsIndex != null) {
testResultsIndex.notify(test, recorderMonitor.getStatistics(), testMonitor.duration);
}
RepositoryIndex repositoryIndex = repositoryIndexes.get(repository.getName());
if (repositoryIndex != null && repositoryIndex.getNameToInfo().containsKey(test)
&& isBlank(repositoryIndex.getNameToInfo().get(test).getLink())) {
if (!endsWithIgnoreCase(test, HTML_EXTENSION)) {
outputFile = getFile(outputDir, escapeFileSystemForbiddenCharacters(test) + HTML_EXTENSION);
}
if (outputFile.isFile() && outputFile.canRead()) {
try {
String outputHTML = readFileToString(outputFile);
recoverLinkInResult(test, outputHTML, repositoryIndex);
} catch (Exception e) {
getLog().debug(format("Could not read the output file '%s'. Cause : %s", outputFile.getAbsolutePath(), e.getMessage()));
LOGGER.trace("full trace: ", e);
}
}
}
}
private String getFixtureFactoryWithArgs() {
String systemUnderDevelopmentWithArgs = systemUnderDevelopment;
if (isNotBlank(systemUnderDevelopmentArgs)) {
systemUnderDevelopmentWithArgs = systemUnderDevelopment + ";" + systemUnderDevelopmentArgs;
}
return systemUnderDevelopmentWithArgs;
}
public static void recoverLinkInResult(String specification, String htmlString, RepositoryIndex repositoryIndex) throws IOException {
RepositoryIndex.SpecificationInfo specificationInfo = repositoryIndex.getNameToInfo().get(specification);
if (isBlank(specificationInfo.getLink()) && isNotBlank(htmlString)) {
LOGGER.trace("got new missing link in index for '{}'. trying to find it in the result output", specification);
org.jsoup.nodes.Document resultOutput = Jsoup.parse(htmlString);
Elements metaTags = resultOutput.head().getElementsByTag("meta");
String link = metaTags.select("[name=\"external-link\"]").attr("content");
if (isNotBlank(link)) {
LOGGER.trace("Found {}", link);
specificationInfo.setLink(link);
repositoryIndex.dump();
}
}
}
private RecorderMonitor run(List args, SpecificationRunnerMonitor... testMonitors) throws MojoExecutionException, MojoFailureException {
DynamicCoreInvoker runner = new DynamicCoreInvoker(createClassLoader());
CompositeSpecificationRunnerMonitor monitors = new CompositeSpecificationRunnerMonitor();
for (SpecificationRunnerMonitor specificationRunnerMonitor : testMonitors) {
monitors.add(specificationRunnerMonitor);
}
RecorderMonitor recorder = new RecorderMonitor();
monitors.add(recorder);
runner.setMonitor(monitors);
try {
LOGGER.debug("Launching the test: {}", args);
runner.run(toArray(args));
} catch (DocumentNeverImplementedException e) {
LOGGER.info(DocumentRepository.THIS_SPECIFICATION_WAS_NEVER_SET_AS_IMPLEMENTED);
} catch (Exception e) {
exceptionOccured = true;
throw new MojoExecutionException("Unable to run tests", e);
}
exceptionOccured |= recorder.hasException();
testFailed |= recorder.hasTestFailures();
registerStatistics(recorder);
return recorder;
}
private void registerStatistics(RecorderMonitor recorder) {
runnerStatistics.tally(recorder.getStatistics());
}
private void printFooter() {
System.out.println();
System.out.println(runnerStatistics);
System.out.println();
}
private ClassLoader createClassLoader() throws MojoExecutionException {
URL[] classpath = createClasspath();
return new URLClassLoader(classpath, ClassLoader.getSystemClassLoader());
}
private URL[] createClasspath() throws MojoExecutionException {
List urls = new ArrayList();
if (classpathElements != null) {
for (String classpathElement : classpathElements) {
urls.add(toURL(new File(classpathElement)));
}
}
urls.add(toURL(fixtureOutputDirectory));
if (!containsGreenPepperCore(urls)) {
urls.add(getDependencyURL("greenpepper-core"));
}
urls.add(getDependencyURL("greenpepper-extensions-java"));
urls.add(getDependencyURL("slf4j-api"));
urls.add(getDependencyURL("jcl-over-slf4j"));
return urls.toArray(new URL[urls.size()]);
}
private URL getDependencyURL(String name) throws MojoExecutionException {
if (pluginDependencies != null && !pluginDependencies.isEmpty()) {
for (Artifact artifact : pluginDependencies) {
if (artifact.getArtifactId().equals(name) && artifact.getType().equals("jar"))
return toURL(artifact.getFile());
}
}
throw new MojoExecutionException("Dependency not found: " + name);
}
private URL toURL(File f) throws MojoExecutionException {
try {
return f.toURI().toURL();
} catch (MalformedURLException e) {
throw new MojoExecutionException("Invalid dependency: " + f.getAbsolutePath(), e);
}
}
private boolean containsGreenPepperCore(List urls) {
for (URL url : urls) {
if (url.getFile().contains("greenpepper-core") && url.getFile().endsWith(".jar")) {
return true;
}
}
return false;
}
private void prepareReportsDir() throws MojoExecutionException {
if (StringUtils.isAnyEmpty(testSpecification, testSpecificationOutput)) {
try {
IOUtil.createDirectoryTree(reportsDirectory);
} catch (IOException e) {
throw new MojoExecutionException("Could not create reports directory: " + reportsDirectory.getAbsolutePath());
}
}
}
private boolean shouldStop() {
return stopOnFirstFailure && runnerStatistics.hasFailure();
}
private void appendOptionsList(List arguments) {
if (!StringUtils.isEmpty(locale)) {
arguments.add("--locale");
arguments.add(locale);
}
if (!StringUtils.isEmpty(selector) && !GreenPepperInterpreterSelector.class.getName().equals(selector)) {
arguments.add("--selector");
arguments.add(selector);
}
if (stopOnFirstFailure) {
arguments.add("--stop");
}
if (debug) {
arguments.add("--debug");
}
}
private String[] toArray(List args) {
String[] arguments = new String[args.size()];
args.toArray(arguments);
return arguments;
}
private class LogWriterMonitor implements SpecificationRunnerMonitor {
private final File outLogFile;
private final File errLogFile;
private PrintStream sysout;
private PrintStream syserr;
LogWriterMonitor(File outLogFile, File errLogFile) {
this.outLogFile = outLogFile;
this.errLogFile = errLogFile;
}
@Override
public void testRunning(String location) {
getLog().debug(format("Creating Log files (by redirecting sysout and stderr) for: %s", location));
sysout = System.out;
syserr = System.err;
syserr.flush();
sysout.flush();
try {
FileOutputStream output = openOutputStream(outLogFile);
FileOutputStream err = openOutputStream(errLogFile);
if (redirectOutputToFile) {
System.setOut(new PrintStream(output));
System.setErr(new PrintStream(err));
} else {
TeePrintStream teeoutput = new TeePrintStream(output, sysout);
System.setOut(teeoutput);
TeePrintStream teeErr = new TeePrintStream(err, syserr);
System.setErr(teeErr);
}
} catch (IOException e) {
System.setOut(sysout);
System.setErr(syserr);
getLog().warn(format("Could not create the log files. Cause: %s", e.getMessage()));
getLog().debug("Could not create the log files.", e);
}
}
@Override
public void testDone(int rightCount, int wrongCount, int exceptionCount, int ignoreCount) {
getLog().debug("Restoring stdout and stderr");
System.setOut(sysout);
System.setErr(syserr);
}
@Override
public void exceptionOccured(Throwable t) {
}
}
private class TeePrintStream extends PrintStream {
private final PrintStream second;
TeePrintStream(OutputStream main, PrintStream second) {
super(main);
this.second = second;
}
/**
* Closes the main stream.
* The second stream is just flushed but not closed.
* @see java.io.PrintStream#close()
*/
@Override
public void close() {
// just for documentation
super.close();
}
@Override
public void flush() {
super.flush();
second.flush();
}
@SuppressWarnings("NullableProblems")
@Override
public void write(byte[] buf, int off, int len) {
super.write(buf, off, len);
second.write(buf, off, len);
}
@Override
public void write(int b) {
super.write(b);
second.write(b);
}
@SuppressWarnings("NullableProblems")
@Override
public void write(byte[] b) throws IOException {
super.write(b);
second.write(b);
}
}
}