package fitnesse.junit;
import java.io.File;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
import junit.framework.AssertionFailedError;
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 org.junit.runners.model.RunnerBuilder;
import fitnesse.responders.run.JavaFormatter;
public class FitNesseSuite extends ParentRunner<String>{
/**
* The <code>Name</code> annotation specifies the name of the Fitnesse suite
* to be run, e.g.: MySuite.MySubSuite
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Name {
public String value();
}
/**
* The <code>DebugMode</code> annotation specifies whether the test is run
* with the REST debug option. Default is true
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DebugMode {
public boolean value();
}
/**
* The <code>SuiteFilter</code> annotation specifies the suite filter of the Fitnesse suite
* to be run, e.g.: fasttests
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SuiteFilter {
public String value();
}
/**
* The <code>ExcludeSuiteFilter</code> 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 {
public String value();
}
/**
* The <code>FitnesseDir</code> annotation specifies the absolute or relative
* path to the directory in which FitNesseRoot can be found
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FitnesseDir {
public String value();
}
/**
* The <code>OutputDir</code> 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.:
* <code>@OutputDir("/tmp/trinidad-results")</code>, 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.:
* <code>@OutputDir(systemProperty = "java.io.tmpdir", pathExtension = "trinidad-results")</code>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface OutputDir {
public String value() default "";
public String systemProperty() default "";
public String pathExtension() default "";
}
/**
* The <code>Port</code> annotation specifies the port used by the FitNesse
* server. Default is the standard FitNesse port.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Port {
public int value() default 0;
public String systemProperty() default "";
}
private final Class<?> suiteClass;
private final String suiteName;
private String fitNesseDir;
private String outputDir;
private String suiteFilter;
private String excludeSuiteFilter;
private boolean debugMode = false;
private int port = 0;
public FitNesseSuite(Class<?> suiteClass, RunnerBuilder builder) throws InitializationError {
super(suiteClass);
this.suiteClass = suiteClass;
this.suiteName=getSuiteName(suiteClass);
this.fitNesseDir=getFitnesseDir(suiteClass);
this.outputDir=getOutputDir(suiteClass);
this.suiteFilter=getSuiteFilter(suiteClass);
this.excludeSuiteFilter=getExcludeSuiteFilter(suiteClass);
this.debugMode=useDebugMode(suiteClass);
this.port=getPort(suiteClass);
}
@Override
protected Description describeChild(String child) {
return Description.createTestDescription(suiteClass, child);
}
@Override
protected List<String> getChildren() {
return JavaFormatter.getInstance(suiteName).getTestsExecuted();
}
static String getFitnesseDir(Class<?> klass)
throws InitializationError {
FitnesseDir fitnesseDirAnnotation = klass.getAnnotation(FitnesseDir.class);
if (fitnesseDirAnnotation == null) {
throw new InitializationError("There must be a @FitnesseDir annotation");
}
return fitnesseDirAnnotation.value();
}
static String getSuiteFilter(Class<?> klass)
throws InitializationError {
SuiteFilter suiteFilterAnnotation = klass.getAnnotation(SuiteFilter.class);
if (suiteFilterAnnotation == null) {
return null;
}
return suiteFilterAnnotation.value();
}
static String getExcludeSuiteFilter(Class<?> klass)
throws InitializationError {
ExcludeSuiteFilter excludeSuiteFilterAnnotation = klass.getAnnotation(ExcludeSuiteFilter.class);
if (excludeSuiteFilterAnnotation == null) {
return null;
}
return excludeSuiteFilterAnnotation.value();
}
static String getSuiteName(Class<?> klass) throws InitializationError {
Name nameAnnotation = klass.getAnnotation(Name.class);
if (nameAnnotation == null) {
throw new InitializationError("There must be a @Name annotation");
}
return nameAnnotation.value();
}
static 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'");
}
public static boolean useDebugMode(Class<?> klass) {
DebugMode debugModeAnnotation = klass.getAnnotation(DebugMode.class);
if (null == debugModeAnnotation) {
return true;
}
return debugModeAnnotation.value();
}
public static 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;
}
@Override
public void run(final RunNotifier notifier) {
JUnitHelper helper=createJUnitHelper(notifier);
try{
helper.assertSuitePasses(suiteName, suiteFilter, excludeSuiteFilter);
}catch(AssertionFailedError e){
notifier.fireTestFailure(new Failure(Description.createSuiteDescription(suiteClass),e));
} catch (Exception e) {
notifier.fireTestFailure(new Failure(Description.createSuiteDescription(suiteClass),e));
}
}
@Override
protected void runChild(String test, RunNotifier notifier) {
JUnitHelper helper=createJUnitHelper(notifier);
try{
helper.assertTestPasses(suiteName);
}catch(AssertionFailedError e){
notifier.fireTestFailure(new Failure(Description.createSuiteDescription(suiteClass),e));
} catch (Exception e) {
notifier.fireTestFailure(new Failure(Description.createSuiteDescription(suiteClass),e));
}
}
private JUnitHelper createJUnitHelper(final RunNotifier notifier) {
JUnitHelper jUnitHelper = new JUnitHelper(this.fitNesseDir, this.outputDir, new JUnitRunNotifierResultsListener(notifier,suiteClass));
jUnitHelper.setDebugMode(debugMode);
jUnitHelper.setPort(port);
return jUnitHelper;
}
}