fitnesse.junit.FitNesseRunner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fitnesse Show documentation
Show all versions of fitnesse Show documentation
The fully integrated standalone wiki, and acceptance testing framework.
package fitnesse.junit;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import fitnesse.ConfigurationParameter;
import fitnesse.ContextConfigurator;
import fitnesse.FitNesseContext;
import fitnesse.plugins.PluginException;
import fitnesse.slim.instructions.SystemExitSecurityManager;
import fitnesse.testrunner.MultipleTestsRunner;
import fitnesse.testrunner.PagesByTestSystem;
import fitnesse.testrunner.SuiteContentsFinder;
import fitnesse.testsystems.ConsoleExecutionLogListener;
import fitnesse.testsystems.TestExecutionException;
import fitnesse.testsystems.TestSummary;
import fitnesse.wiki.PageCrawler;
import fitnesse.wiki.PathParser;
import fitnesse.wiki.WikiPage;
import fitnesse.wiki.WikiPagePath;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.InitializationError;
import static org.junit.Assert.*;
public class FitNesseRunner extends ParentRunner {
/**
* The Suite
annotation specifies the name of the Fitnesse suite
* (or single page) to be run, e.g.: MySuite.MySubSuite
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Suite {
String value() default "";
String systemProperty() default "";
}
/**
* The DebugMode
annotation specifies whether the test is run
* with the REST debug option. Default is true
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DebugMode {
boolean value();
}
/**
* The PreventSystemExit
annotation specifies whether the {@link SystemExitSecurityManager} must be to prevent {@link System#exit(int)} calls. Default is false
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PreventSystemExit {
boolean value() default true;
}
/**
* The SuiteFilter
annotation specifies the suite filter of the Fitnesse suite
* to be run, e.g.: fasttests
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SuiteFilter {
String value() default "";
String systemProperty() default "";
boolean andStrategy() default false;
}
/**
* The ExcludeSuiteFilter
annotation specifies a filter for excluding tests from the Fitnesse suite
* to be run, e.g.: slowtests
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ExcludeSuiteFilter {
String value();
String systemProperty() default "";
}
/**
* The FitnesseDir
annotation specifies the absolute or relative
* path to the directory in which FitNesseRoot can be found. You can either specify
*
* - a relative or absolute path directly, e.g.:
@FitnesseDir("/parentOfFitNesseRoot")
,
* or you can specify
* - a system property the content of which will be taken as base dir and
* optionally give a path extension, e.g.:
*
@FitnesseDir(systemProperty = "fitnesse.root.dir.parent")
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FitnesseDir {
String value() default "";
String systemProperty() default "";
String fitNesseRoot() default ContextConfigurator.DEFAULT_ROOT;
}
/**
* The OutputDir
annotation specifies where the html reports of
* run suites and tests will be found after running them. You can either specify
*
* - a relative or absolute path directly, e.g.:
@OutputDir("/tmp/trinidad-results")
,
* or you can specify
* - a system property the content of which will be taken as base dir and
* optionally give a path extension, e.g.:
*
@OutputDir(systemProperty = "java.io.tmpdir", pathExtension = "trinidad-results")
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface OutputDir {
String value() default "";
String systemProperty() default "";
String pathExtension() default "";
}
/**
* The Port
annotation specifies the port used by the FitNesse
* server. Default is the standard FitNesse port.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Deprecated
public @interface Port {
int value() default 0;
String systemProperty() default "";
}
/**
* The ConfigFile
annotation specifies the configuration file to load.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ConfigFile {
String value();
}
private Class> suiteClass;
private String suiteName;
private String outputDir;
private String suiteFilter;
private boolean suiteFilterAndStrategy;
private String excludeSuiteFilter;
private boolean debugMode;
private boolean preventSystemExit;
private FitNesseContext context;
private DescriptionFactory descriptionFactory;
private List children;
public FitNesseRunner(Class> suiteClass) throws InitializationError {
super(suiteClass);
descriptionFactory = new DescriptionFactory();
}
@Override
protected void collectInitializationErrors(List errors) {
// called by superclass' constructor
super.collectInitializationErrors(errors);
this.suiteClass = getTestClass().getJavaClass();
try {
this.suiteName = getSuiteName(suiteClass);
} catch (Exception e) {
errors.add(e);
}
try {
this.outputDir = getOutputDir(suiteClass);
} catch (Exception e) {
errors.add(e);
}
try {
this.suiteFilter = getSuiteFilter(suiteClass);
} catch (Exception e) {
errors.add(e);
}
try {
this.suiteFilterAndStrategy = getSuiteFilterAndStrategy(suiteClass);
} catch (Exception e) {
errors.add(e);
}
try {
this.excludeSuiteFilter = getExcludeSuiteFilter(suiteClass);
} catch (Exception e) {
errors.add(e);
}
try {
this.debugMode = useDebugMode(suiteClass);
} catch (Exception e) {
errors.add(e);
}
try {
this.preventSystemExit = shouldPreventSystemExit(suiteClass);
} catch (Exception e) {
errors.add(e);
}
try {
this.context = createContext(suiteClass);
} catch (Exception e) {
errors.add(e);
}
}
protected FitNesseContext createContext(Class> suiteClass) throws Exception {
return initContextConfigurator().makeFitNesseContext();
}
protected String getSuiteName(Class> klass) throws InitializationError {
Suite suiteAnnotation = klass.getAnnotation(Suite.class);
if (suiteAnnotation == null) {
throw new InitializationError("There must be a @Suite annotation");
}
if (!"".equals(suiteAnnotation.value())) {
return suiteAnnotation.value();
}
if (!"".equals(suiteAnnotation.systemProperty())) {
return System.getProperty(suiteAnnotation.systemProperty());
}
throw new InitializationError(
"In annotation @Suite you have to specify either 'value' or 'systemProperty'");
}
protected String getOutputDir(Class> klass) throws InitializationError {
OutputDir outputDirAnnotation = klass.getAnnotation(OutputDir.class);
if (outputDirAnnotation == null) {
throw new InitializationError("There must be a @OutputDir annotation");
}
if (!"".equals(outputDirAnnotation.value())) {
return outputDirAnnotation.value();
}
if (!"".equals(outputDirAnnotation.systemProperty())) {
String baseDir = System.getProperty(outputDirAnnotation.systemProperty());
File outputDir = new File(baseDir, outputDirAnnotation.pathExtension());
return outputDir.getAbsolutePath();
}
throw new InitializationError(
"In annotation @OutputDir you have to specify either 'value' or 'systemProperty'");
}
protected String getSuiteFilter(Class> klass)
throws Exception {
SuiteFilter suiteFilterAnnotation = klass.getAnnotation(SuiteFilter.class);
if (suiteFilterAnnotation == null) {
return null;
}
if (!"".equals(suiteFilterAnnotation.value())) {
return suiteFilterAnnotation.value();
}
if (!"".equals(suiteFilterAnnotation.systemProperty())) {
return System.getProperty(suiteFilterAnnotation.systemProperty());
}
throw new InitializationError(
"In annotation @SuiteFilter you have to specify either 'value' or 'systemProperty'");
}
protected boolean getSuiteFilterAndStrategy(Class> klass) throws Exception {
SuiteFilter suiteFilterAnnotation = klass.getAnnotation(SuiteFilter.class);
if (suiteFilterAnnotation == null) {
return false;
}
return suiteFilterAnnotation.andStrategy();
}
protected String getExcludeSuiteFilter(Class> klass)
throws Exception {
ExcludeSuiteFilter excludeSuiteFilterAnnotation = klass.getAnnotation(ExcludeSuiteFilter.class);
if (excludeSuiteFilterAnnotation == null) {
return null;
}
if (!"".equals(excludeSuiteFilterAnnotation.value())) {
return excludeSuiteFilterAnnotation.value();
}
if (!"".equals(excludeSuiteFilterAnnotation.systemProperty())) {
return System.getProperty(excludeSuiteFilterAnnotation.systemProperty());
}
throw new InitializationError(
"In annotation @ExcludeSuiteFilter you have to specify either 'value' or 'systemProperty'");
}
protected boolean useDebugMode(Class> klass) throws Exception {
DebugMode debugModeAnnotation = klass.getAnnotation(DebugMode.class);
if (null == debugModeAnnotation) {
return true;
}
return debugModeAnnotation.value();
}
protected boolean shouldPreventSystemExit(Class> klass) throws Exception {
PreventSystemExit preventSystemExitAnnotation = klass.getAnnotation(PreventSystemExit.class);
if (null == preventSystemExitAnnotation) {
return true;
}
return preventSystemExitAnnotation.value();
}
protected String getFitNesseDir(Class> klass)
throws InitializationError {
FitnesseDir fitnesseDirAnnotation = klass.getAnnotation(FitnesseDir.class);
if (fitnesseDirAnnotation == null) {
throw new InitializationError("There must be a @FitnesseDir annotation");
}
if (!"".equals(fitnesseDirAnnotation.value())) {
return fitnesseDirAnnotation.value();
}
if (!"".equals(fitnesseDirAnnotation.systemProperty())) {
String baseDir = System.getProperty(fitnesseDirAnnotation.systemProperty());
File outputDir = new File(baseDir);
return outputDir.getAbsolutePath();
}
throw new InitializationError(
"In annotation @FitnesseDir you have to specify either 'value' or 'systemProperty'");
}
protected String getFitNesseRoot(Class> klass) {
FitnesseDir fitnesseDirAnnotation = klass.getAnnotation(FitnesseDir.class);
return fitnesseDirAnnotation.fitNesseRoot();
}
public int getPort(Class> klass) {
Port portAnnotation = klass.getAnnotation(Port.class);
if (null == portAnnotation) {
return 0;
}
int lport = portAnnotation.value();
if (!"".equals(portAnnotation.systemProperty())) {
lport = Integer.getInteger(portAnnotation.systemProperty(), lport);
}
return lport;
}
protected File getConfigFile(String rootPath, Class> klass) {
ConfigFile configFileAnnotation = klass.getAnnotation(ConfigFile.class);
if (null == configFileAnnotation) {
return new File(rootPath, ContextConfigurator.DEFAULT_CONFIG_FILE);
}
return new File(configFileAnnotation.value());
}
@Override
protected Description describeChild(WikiPage child) {
return getDescriptionFactory().createDescription(suiteClass, child);
}
@Override
protected List getChildren() {
if (this.children == null) {
this.children = initChildren();
}
return this.children;
}
@Override
public void run(final RunNotifier notifier) {
if (isFilteredForChildTest()) {
super.run(notifier);
} else {
runPages(children, notifier);
}
}
private boolean isFilteredForChildTest() {
return getDescription().getChildren().size() < getChildren().size();
}
@Override
protected void runChild(WikiPage page, RunNotifier notifier) {
runPages(listOf(page), notifier);
}
protected void runPages(Listpages, final RunNotifier notifier) {
MultipleTestsRunner testRunner = createTestRunner(pages);
addTestSystemListeners(notifier, testRunner, suiteClass, getDescriptionFactory());
addExecutionLogListener(notifier, testRunner, suiteClass);
System.setProperty(SystemExitSecurityManager.PREVENT_SYSTEM_EXIT, String.valueOf(preventSystemExit));
try {
executeTests(testRunner);
} catch (AssertionError | Exception e) {
Description description = getDescriptionFactory().createSuiteDescription(suiteClass);
notifier.fireTestFailure(new Failure(description, e));
}
}
protected void addTestSystemListeners(RunNotifier notifier, MultipleTestsRunner testRunner, Class> suiteClass,
DescriptionFactory descriptionFactory) {
testRunner.addTestSystemListener(new JUnitRunNotifierResultsListener(notifier, suiteClass, descriptionFactory));
}
protected void addExecutionLogListener(RunNotifier notifier, MultipleTestsRunner testRunner, Class> suiteClass) {
testRunner.addExecutionLogListener(new ConsoleExecutionLogListener());
}
protected List initChildren() {
WikiPage suiteRoot = getSuiteRootPage();
if (suiteRoot == null) {
throw new IllegalArgumentException("No page " + this.suiteName);
}
List children;
if (suiteRoot.getData().hasAttribute("Suite")) {
children = new SuiteContentsFinder(suiteRoot, getSuiteFilter(), context.getRootPage()).getAllPagesToRunForThisSuite();
} else {
children = Collections.singletonList(suiteRoot);
}
return children;
}
private fitnesse.testrunner.SuiteFilter getSuiteFilter() {
return new fitnesse.testrunner.SuiteFilter(getOrSuiteFilter(), excludeSuiteFilter, getAndSuiteFilter(), null);
}
private String getOrSuiteFilter() {
return suiteFilterAndStrategy ? null : suiteFilter;
}
private String getAndSuiteFilter() {
return suiteFilterAndStrategy ? suiteFilter : null;
}
protected ContextConfigurator initContextConfigurator() throws InitializationError {
String rootPath = getFitNesseDir(suiteClass);
String fitNesseRoot = getFitNesseRoot(suiteClass);
int port = getPort(suiteClass);
File configFile = getConfigFile(rootPath, suiteClass);
return ContextConfigurator.systemDefaults()
.updatedWith(System.getProperties())
.updatedWith(ConfigurationParameter.loadProperties(configFile))
.updatedWith(ConfigurationParameter.makeProperties(
ConfigurationParameter.PORT, port,
ConfigurationParameter.ROOT_PATH, rootPath,
ConfigurationParameter.ROOT_DIRECTORY, fitNesseRoot,
ConfigurationParameter.OMITTING_UPDATES, true));
}
private WikiPage getSuiteRootPage() {
WikiPagePath path = PathParser.parse(this.suiteName);
PageCrawler crawler = context.getRootPage().getPageCrawler();
return crawler.getPage(path);
}
private MultipleTestsRunner createTestRunner(List pages) {
final PagesByTestSystem pagesByTestSystem = new PagesByTestSystem(pages, context.getRootPage());
MultipleTestsRunner runner = new MultipleTestsRunner(pagesByTestSystem,
context.testSystemFactory);
runner.setRunInProcess(debugMode);
return runner;
}
private void executeTests(MultipleTestsRunner testRunner) throws IOException, TestExecutionException {
JavaFormatter testFormatter = new JavaFormatter(suiteName);
testFormatter.setResultsRepository(new JavaFormatter.FolderResultsRepository(outputDir));
testRunner.addTestSystemListener(testFormatter);
testRunner.executeTestPages();
TestSummary summary = testFormatter.getTotalSummary();
assertTrue(msgAtLeastOneTest(suiteName, summary), summary.getRight() > 0 || summary.getWrong() > 0 || summary.getExceptions() > 0);
}
private String msgAtLeastOneTest(String pageName, TestSummary summary) {
return MessageFormat.format("at least one test executed in {0}\n{1}",
pageName, summary.toString());
}
private List listOf(WikiPage page) {
List list = new ArrayList<>(1);
list.add(page);
return list;
}
public DescriptionFactory getDescriptionFactory() {
return descriptionFactory;
}
public void setDescriptionFactory(DescriptionFactory descriptionFactory) {
this.descriptionFactory = descriptionFactory;
}
}