now builds test descriptions recursively

This commit is contained in:
Wouter Groeneveld 2011-06-27 10:05:01 +02:00
parent 7ea8dc7ce4
commit 45d32509ef
3 changed files with 194 additions and 204 deletions

View File

@ -1,40 +1,24 @@
package be.klak.junit.jasmine; package be.klak.junit.jasmine;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import org.junit.runner.Description; import org.junit.runner.Description;
import be.klak.rhino.RhinoContext;
class JasmineDescriptions { class JasmineDescriptions {
private final Description rootDescription; private final Description rootDescription;
private final Map<String, JasmineSpec> specsMap; private final Collection<JasmineSpec> specs;
private final RhinoContext rhinoContext;
JasmineDescriptions(Description rootDescription, Map<String, JasmineSpec> specsMap, RhinoContext context) { public JasmineDescriptions(Description rootDescription, Collection<JasmineSpec> specs) {
this.rootDescription = rootDescription; this.rootDescription = rootDescription;
this.specsMap = specsMap; this.specs = specs;
this.rhinoContext = context; }
}
public Description getRootDescription() { public Description getRootDescription() {
return rootDescription; return rootDescription;
} }
public Collection<JasmineSpec> getAllSpecs() {
return specsMap.values();
}
public void executeSpec(Description description) {
getSpec(description).execute(rhinoContext);
}
public JasmineSpec getSpec(Description description) {
return specsMap.get(description.getDisplayName());
}
public Collection<JasmineSpec> getSpecs() {
return specs;
}
} }

View File

@ -1,9 +1,8 @@
package be.klak.junit.jasmine; package be.klak.junit.jasmine;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.HashMap; import java.util.ArrayList;
import java.util.Map; import java.util.List;
import org.junit.runner.Description; import org.junit.runner.Description;
import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.NativeArray;
@ -11,49 +10,58 @@ import org.mozilla.javascript.NativeObject;
import be.klak.rhino.RhinoContext; import be.klak.rhino.RhinoContext;
public class JasmineJSSuiteConverter {
class JasmineJSSuiteConverter { private final RhinoContext context;
private final NativeArray baseSuites; public JasmineJSSuiteConverter(RhinoContext context) {
private final RhinoContext context; this.context = context;
}
JasmineJSSuiteConverter(NativeArray baseSuites, RhinoContext context) { public JasmineDescriptions convertToJunitDescriptions(Class<?> testClass, NativeArray baseSuites) {
this.baseSuites = baseSuites; Description rootDescription = Description.createSuiteDescription(testClass);
this.context = context; List<JasmineSpec> specs = convertSuiteArrayToDescriptions(baseSuites, rootDescription, new ArrayList<String>());
} return new JasmineDescriptions(rootDescription, specs);
}
public JasmineDescriptions convertToJunitDescriptions(Class<?> testClass) { private List<JasmineSpec> convertSuiteArrayToDescriptions(NativeArray suiteArray, Description rootDescription,
Description rootDescription = Description.createSuiteDescription(testClass); List<String> processed) {
Map<String, JasmineSpec> specsMap = convertSuiteArrayToDescriptions(this.baseSuites, rootDescription); List<JasmineSpec> specs = new ArrayList<JasmineSpec>();
return new JasmineDescriptions(rootDescription, specsMap, context); for (Object idObj : suiteArray.getIds()) {
} NativeObject suite = (NativeObject) suiteArray.get((Integer) idObj, suiteArray);
private Map<String, JasmineSpec> convertSuiteArrayToDescriptions(NativeArray suiteArray, Description rootDescription) { String description = (String) suite.get("description", suite);
Map<String, JasmineSpec> specsMap = new HashMap<String, JasmineSpec>(); if (!processed.contains(description)) {
for (Object idObj : suiteArray.getIds()) { Description suiteDescription = addSuiteToDescription(rootDescription, processed, description);
NativeObject suite = (NativeObject) suiteArray.get((Integer) idObj, suiteArray); specs.addAll(convertToJunitDescription(suite, suiteDescription));
Description suiteDescription = Description NativeArray subSuites = (NativeArray) context.executeFunction(suite, "suites");
.createSuiteDescription((String) suite.get("description", suite), (Annotation[]) null); convertSuiteArrayToDescriptions(subSuites, suiteDescription, processed);
rootDescription.addChild(suiteDescription); }
specsMap.putAll(convertToJunitDescription(suite, suiteDescription)); }
}
return specsMap; return specs;
} }
private Map<String, JasmineSpec> convertToJunitDescription(NativeObject suite, Description description) { private Description addSuiteToDescription(Description description, List<String> processed, String suiteName) {
Map<String, JasmineSpec> specsMap = new HashMap<String, JasmineSpec>(); processed.add(suiteName);
NativeArray specsArray = (NativeArray) context.executeFunction(suite, "specs"); Description suiteDescription = Description.createSuiteDescription(suiteName, (Annotation[]) null);
for (Object idObj : specsArray.getIds()) { description.addChild(suiteDescription);
NativeObject spec = (NativeObject) specsArray.get((Integer) idObj, specsArray); return suiteDescription;
}
JasmineSpec jasmineSpec = new JasmineSpec(spec); private List<JasmineSpec> convertToJunitDescription(NativeObject suite, Description description) {
specsMap.put(jasmineSpec.toString(), jasmineSpec); List<JasmineSpec> specsMap = new ArrayList<JasmineSpec>();
description.addChild(jasmineSpec.getDescription()); NativeArray specsArray = (NativeArray) context.executeFunction(suite, "specs");
} for (Object idObj : specsArray.getIds()) {
NativeObject spec = (NativeObject) specsArray.get((Integer) idObj, specsArray);
return specsMap; JasmineSpec jasmineSpec = new JasmineSpec(spec);
} specsMap.add(jasmineSpec);
description.addChild(jasmineSpec.getDescription());
}
return specsMap;
}
} }

View File

@ -3,7 +3,6 @@ package be.klak.junit.jasmine;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@ -15,173 +14,172 @@ import org.mozilla.javascript.tools.debugger.Main;
import be.klak.rhino.RhinoContext; import be.klak.rhino.RhinoContext;
public class JasmineTestRunner extends Runner { public class JasmineTestRunner extends Runner {
private static final int SLEEP_TIME_MILISECONDS = 50; private static final int SLEEP_TIME_MILISECONDS = 50;
private static final String JASMINE_LIB_DIR = "/lib/jasmine-1.0.2/"; private static final String JASMINE_LIB_DIR = "/lib/jasmine-1.0.2/";
private JasmineDescriptions jasmineSuite; private JasmineDescriptions jasmineSuite;
private final RhinoContext rhinoContext; private final RhinoContext rhinoContext;
private final JasmineSuite suiteAnnotation; private final JasmineSuite suiteAnnotation;
private final Class<?> testClass; private final Class<?> testClass;
@JasmineSuite @JasmineSuite
private class DefaultSuite { private class DefaultSuite {
} }
public JasmineTestRunner(Class<?> testClass) { public JasmineTestRunner(Class<?> testClass) {
this.testClass = testClass; this.testClass = testClass;
this.suiteAnnotation = getJasmineSuiteAnnotationFromTestClass(); this.suiteAnnotation = getJasmineSuiteAnnotationFromTestClass();
Main debugger = null; Main debugger = null;
if (this.suiteAnnotation.debug()) { if (this.suiteAnnotation.debug()) {
debugger = this.rhinoContext.createDebugger(); debugger = this.rhinoContext.createDebugger();
} }
this.rhinoContext = setUpRhinoScope(); this.rhinoContext = setUpRhinoScope();
if (this.suiteAnnotation.debug()) { if (this.suiteAnnotation.debug()) {
debugger.doBreak(); debugger.doBreak();
} }
} }
private RhinoContext setUpRhinoScope() { private RhinoContext setUpRhinoScope() {
RhinoContext context = new RhinoContext(); RhinoContext context = new RhinoContext();
context.loadEnv(suiteAnnotation.jsRootDir()); context.loadEnv(suiteAnnotation.jsRootDir());
setUpJasmine(context); setUpJasmine(context);
context.load(suiteAnnotation.sourcesRootDir() + "/", suiteAnnotation.sources()); context.load(suiteAnnotation.sourcesRootDir() + "/", suiteAnnotation.sources());
context.load(suiteAnnotation.jsRootDir() + "/specs/", getJasmineSpecs(suiteAnnotation)); context.load(suiteAnnotation.jsRootDir() + "/specs/", getJasmineSpecs(suiteAnnotation));
return context; return context;
} }
private void setUpJasmine(RhinoContext context) { private void setUpJasmine(RhinoContext context) {
context.load(getJsLibDir() + "jasmine.js"); context.load(getJsLibDir() + "jasmine.js");
context.load(getJsLibDir() + "jasmine.delegator_reporter.js"); context.load(getJsLibDir() + "jasmine.delegator_reporter.js");
context.evalJS("jasmine.getEnv().addReporter(new jasmine.DelegatorJUnitReporter());"); context.evalJS("jasmine.getEnv().addReporter(new jasmine.DelegatorJUnitReporter());");
} }
private JasmineSuite getJasmineSuiteAnnotationFromTestClass() { private JasmineSuite getJasmineSuiteAnnotationFromTestClass() {
JasmineSuite suiteAnnotation = testClass.getAnnotation(JasmineSuite.class); JasmineSuite suiteAnnotation = testClass.getAnnotation(JasmineSuite.class);
if (suiteAnnotation == null) { if (suiteAnnotation == null) {
suiteAnnotation = DefaultSuite.class.getAnnotation(JasmineSuite.class); suiteAnnotation = DefaultSuite.class.getAnnotation(JasmineSuite.class);
} }
return suiteAnnotation; return suiteAnnotation;
} }
private String[] getJasmineSpecs(JasmineSuite suiteAnnotation) { private String[] getJasmineSpecs(JasmineSuite suiteAnnotation) {
if (suiteAnnotation.specs().length == 0) { if (suiteAnnotation.specs().length == 0) {
return new String[] { StringUtils.uncapitalize(testClass.getSimpleName()).replace("Test", "Spec") + ".js" }; return new String[] { StringUtils.uncapitalize(testClass.getSimpleName()).replace("Test", "Spec") + ".js" };
} }
return suiteAnnotation.specs(); return suiteAnnotation.specs();
} }
private void resetEnvjsWindowSpace() { private void resetEnvjsWindowSpace() {
this.rhinoContext.evalJS("window.location = '" + suiteAnnotation.jsRootDir() + "/lib/blank.html';"); this.rhinoContext.evalJS("window.location = '" + suiteAnnotation.jsRootDir() + "/lib/blank.html';");
} }
private String getJsLibDir() { private String getJsLibDir() {
return suiteAnnotation.jsRootDir() + JASMINE_LIB_DIR; return suiteAnnotation.jsRootDir() + JASMINE_LIB_DIR;
} }
private JasmineDescriptions getJasmineDescriptions() { private JasmineDescriptions getJasmineDescriptions() {
if (this.jasmineSuite == null) { if (this.jasmineSuite == null) {
NativeArray baseSuites = (NativeArray) rhinoContext.evalJS("jasmine.getEnv().currentRunner().suites()"); NativeArray baseSuites = (NativeArray) rhinoContext.evalJS("jasmine.getEnv().currentRunner().suites()");
this.jasmineSuite = new JasmineJSSuiteConverter(baseSuites, rhinoContext).convertToJunitDescriptions(testClass); this.jasmineSuite = new JasmineJSSuiteConverter(rhinoContext).convertToJunitDescriptions(testClass, baseSuites);
} }
return this.jasmineSuite; return this.jasmineSuite;
} }
@Override @Override
public Description getDescription() { public Description getDescription() {
return getJasmineDescriptions().getRootDescription(); return getJasmineDescriptions().getRootDescription();
} }
@Override @Override
public void run(RunNotifier notifier) { public void run(RunNotifier notifier) {
generateSpecRunnerIfNeeded(); generateSpecRunnerIfNeeded();
for (JasmineSpec spec : getJasmineDescriptions().getAllSpecs()) { for (JasmineSpec spec : getJasmineDescriptions().getSpecs()) {
Object testClassInstance = createTestClassInstance(); Object testClassInstance = createTestClassInstance();
fireMethodsWithSpecifiedAnnotationIfAny(testClassInstance, Before.class); fireMethodsWithSpecifiedAnnotationIfAny(testClassInstance, Before.class);
try { try {
notifier.fireTestStarted(spec.getDescription()); notifier.fireTestStarted(spec.getDescription());
spec.execute(rhinoContext); spec.execute(rhinoContext);
while (!spec.isDone()) { while (!spec.isDone()) {
waitALittle(); waitALittle();
} }
reportSpecResultToNotifier(notifier, spec); reportSpecResultToNotifier(notifier, spec);
resetEnvjsWindowSpace(); resetEnvjsWindowSpace();
} finally { } finally {
fireMethodsWithSpecifiedAnnotationIfAny(testClassInstance, After.class); fireMethodsWithSpecifiedAnnotationIfAny(testClassInstance, After.class);
} }
} }
this.rhinoContext.exit(); this.rhinoContext.exit();
} }
private Object createTestClassInstance() { private Object createTestClassInstance() {
try { try {
return testClass.newInstance(); return testClass.newInstance();
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("Unable to create a new instance of testClass " + testClass.getSimpleName() throw new RuntimeException("Unable to create a new instance of testClass " + testClass.getSimpleName()
+ " using a no-arg constructor", ex); + " using a no-arg constructor", ex);
} }
} }
private void fireMethodsWithSpecifiedAnnotationIfAny(Object testClassInstance, Class<? extends Annotation> annotation) { private void fireMethodsWithSpecifiedAnnotationIfAny(Object testClassInstance, Class<? extends Annotation> annotation) {
for (Method method : testClass.getMethods()) { for (Method method : testClass.getMethods()) {
try { try {
if (method.getAnnotation(annotation) != null) { if (method.getAnnotation(annotation) != null) {
method.setAccessible(true); method.setAccessible(true);
Class<?>[] parameterTypes = method.getParameterTypes(); Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 0) { if (parameterTypes.length == 0) {
method.invoke(testClassInstance, (Object[]) null); method.invoke(testClassInstance, (Object[]) null);
} else if (parameterTypes.length == 1 && RhinoContext.class.isAssignableFrom(parameterTypes[0])) { } else if (parameterTypes.length == 1 && RhinoContext.class.isAssignableFrom(parameterTypes[0])) {
method.invoke(testClassInstance, new Object[] { this.rhinoContext }); method.invoke(testClassInstance, new Object[] { this.rhinoContext });
} else { } else {
throw new IllegalStateException("Annotated method does not have zero or rhinoContext as parameterTypes"); throw new IllegalStateException("Annotated method does not have zero or rhinoContext as parameterTypes");
} }
} }
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException( throw new RuntimeException(
"Exception while firing " + annotation.getSimpleName() + " method: " + method.getName(), ex); "Exception while firing " + annotation.getSimpleName() + " method: " + method.getName(), ex);
} }
} }
} }
private void generateSpecRunnerIfNeeded() { private void generateSpecRunnerIfNeeded() {
if (suiteAnnotation.generateSpecRunner()) { if (suiteAnnotation.generateSpecRunner()) {
String[] jasmineSpecs = getJasmineSpecs(suiteAnnotation); String[] jasmineSpecs = getJasmineSpecs(suiteAnnotation);
new JasmineSpecRunnerGenerator(jasmineSpecs, suiteAnnotation, suiteAnnotation.jsRootDir() + "/runners", new JasmineSpecRunnerGenerator(jasmineSpecs, suiteAnnotation, suiteAnnotation.jsRootDir() + "/runners",
testClass.getSimpleName() testClass.getSimpleName()
+ "Runner.html") + "Runner.html")
.generate(); .generate();
} }
} }
private void reportSpecResultToNotifier(RunNotifier notifier, JasmineSpec spec) { private void reportSpecResultToNotifier(RunNotifier notifier, JasmineSpec spec) {
if (spec.isPassed(rhinoContext)) { if (spec.isPassed(rhinoContext)) {
notifier.fireTestFinished(spec.getDescription()); notifier.fireTestFinished(spec.getDescription());
} else if (spec.isFailed(rhinoContext)) { } else if (spec.isFailed(rhinoContext)) {
notifier.fireTestFailure(spec.getJunitFailure(rhinoContext)); notifier.fireTestFailure(spec.getJunitFailure(rhinoContext));
} else { } else {
throw new IllegalStateException("Unexpected spec status received: " + spec); throw new IllegalStateException("Unexpected spec status received: " + spec);
} }
} }
private void waitALittle() { private void waitALittle() {
try { try {
Thread.sleep(SLEEP_TIME_MILISECONDS); Thread.sleep(SLEEP_TIME_MILISECONDS);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
} }