Skip to content
Browse files

Moved JUnit support to new submodule cucumber-junit. JUnit runner wil…

…l now output results using the prettyformatter if the system property cucumber.reporter is set to pretty or pretty=somefile. The Backend interface has changed. Less methods to implement, and buildWorld() takes a World object where the Backend impl can register stepdefs and hooks. Currently this means scanning the fs/classpath for stepdefs/hooks for each scenario (slow), but this can be optimised later. This allows scoping of stepdefs on a per-feature (and later - per-scenario) basis.
  • Loading branch information...
1 parent e3b036c commit 633b5aeed12d8506491ae2a4c60ed8735fb7dab3 @aslakhellesoy aslakhellesoy committed Sep 11, 2011
Showing with 566 additions and 435 deletions.
  1. +13 −33 clojure/src/main/java/cucumber/runtime/clojure/ClojureBackend.java
  2. +10 −1 core/src/main/java/cucumber/cli/Runner.java
  3. +4 −7 core/src/main/java/cucumber/runtime/Backend.java
  4. +31 −31 core/src/main/java/cucumber/runtime/Runtime.java
  5. +59 −11 core/src/main/java/cucumber/runtime/World.java
  6. +0 −12 core/src/main/java/cucumber/runtime/model/CucumberFeature.java
  7. +10 −32 core/src/main/java/cucumber/runtime/model/CucumberScenario.java
  8. +15 −10 core/src/test/java/cucumber/runtime/HookOrderTest.java
  9. +2 −6 core/src/test/java/cucumber/runtime/HookTest.java
  10. +1 −0 cucumber-jvm.ipr
  11. +1 −0 examples/java-calculator/java-calculator.iml
  12. +5 −1 examples/java-calculator/pom.xml
  13. +1 −0 examples/java-webbit-websockets-selenium/java-webbit-websockets-selenium.iml
  14. +5 −1 examples/java-webbit-websockets-selenium/pom.xml
  15. +5 −1 examples/scala-calculator/pom.xml
  16. +1 −1 examples/scala-calculator/scala-calculator.iml
  17. +1 −0 groovy/groovy.iml
  18. +5 −0 groovy/pom.xml
  19. +21 −40 groovy/src/main/java/cucumber/runtime/groovy/GroovyBackend.java
  20. +1 −0 guice/guice.iml
  21. +5 −0 guice/pom.xml
  22. +24 −38 ioke/src/main/java/cucumber/runtime/ioke/IokeBackend.java
  23. +15 −29 java/src/main/java/cucumber/runtime/java/JavaBackend.java
  24. +5 −3 java/src/main/java/cucumber/runtime/java/JavaHookDefinition.java
  25. +3 −2 java/src/main/java/cucumber/runtime/java/JavaStepDefinition.java
  26. +22 −10 java/src/test/java/cucumber/runtime/java/JavaHookTest.java
  27. +6 −7 java/src/test/java/cucumber/runtime/java/JavaStepDefinitionDependencyInjectionTest.java
  28. +1 −0 java/src/test/resources/cucumber-features/cucumber_java_mappings.rb
  29. +1 −0 jruby/jruby.iml
  30. +5 −0 jruby/pom.xml
  31. +13 −32 jruby/src/main/java/cucumber/runtime/jruby/JRubyBackend.java
  32. +28 −0 junit/pom.xml
  33. +35 −39 {core → junit}/src/main/java/cucumber/junit/Cucumber.java
  34. +4 −0 {core → junit}/src/main/java/cucumber/junit/Feature.java
  35. 0 {core → junit}/src/main/java/cucumber/junit/JUnitReporter.java
  36. +65 −0 junit/src/main/java/cucumber/junit/JUnitReporterFactory.java
  37. +7 −5 {core → junit}/src/main/java/cucumber/junit/ScenarioRunner.java
  38. +57 −0 junit/src/main/java/cucumber/runtime/NullReporter.java
  39. +5 −0 jython/pom.xml
  40. +15 −38 jython/src/main/java/cucumber/runtime/jython/JythonBackend.java
  41. +5 −2 jython/src/main/java/cucumber/runtime/jython/JythonStepDefinition.java
  42. +1 −0 picocontainer/picocontainer.iml
  43. +5 −0 picocontainer/pom.xml
  44. +15 −4 pom.xml
  45. +20 −38 rhino/src/main/java/cucumber/runtime/rhino/RhinoBackend.java
  46. +1 −1 scala/scala.iml
  47. +5 −0 spring/pom.xml
  48. +1 −0 spring/spring.iml
  49. +5 −0 weld/pom.xml
  50. +1 −0 weld/weld.iml
View
46 clojure/src/main/java/cucumber/runtime/clojure/ClojureBackend.java
@@ -7,32 +7,27 @@
import cucumber.resources.Resources;
import cucumber.runtime.Backend;
import cucumber.runtime.CucumberException;
-import cucumber.runtime.HookDefinition;
-import cucumber.runtime.StepDefinition;
+import cucumber.runtime.World;
import gherkin.formatter.model.Step;
-import java.util.ArrayList;
+import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern;
public class ClojureBackend implements Backend {
private static ClojureBackend instance;
+ private World world;
- private final List<StepDefinition> stepDefinitions = new ArrayList<StepDefinition>();
-
- public ClojureBackend(List<String> scriptPaths) {
+ public ClojureBackend() throws ClassNotFoundException, IOException {
instance = this;
- try {
- defineStepDefinitions(scriptPaths);
- } catch (Exception e) {
- throw new CucumberException("Failed to define Cloure Step Definitions", e);
- }
+ RT.load("cucumber/runtime/clojure/dsl");
}
- private void defineStepDefinitions(List<String> scriptPaths) throws Exception {
- RT.load("cucumber/runtime/clojure/dsl");
- for (String scriptPath : scriptPaths) {
- Resources.scan(scriptPath.replace('.', '/'), ".clj", new Consumer() {
+ @Override
+ public void buildWorld(List<String> codePaths, World world) {
+ this.world = world;
+ for (String codePath : codePaths) {
+ Resources.scan(codePath.replace('.', '/'), ".clj", new Consumer() {
public void consume(Resource resource) {
try {
RT.load(resource.getPath().replaceAll(".clj$", ""));
@@ -44,16 +39,11 @@ public void consume(Resource resource) {
}
}
- public List<StepDefinition> getStepDefinitions() {
- return stepDefinitions;
- }
-
- public void newWorld() {
- }
-
+ @Override
public void disposeWorld() {
}
+ @Override
public String getSnippet(Step step) {
return new ClojureSnippetGenerator(step).getSnippet();
}
@@ -72,16 +62,6 @@ private StackTraceElement stepDefLocation(String interpreterClassName, String in
public static void addStepDefinition(Pattern regexp, AFunction body) {
StackTraceElement location = instance.stepDefLocation("clojure.lang.Compiler", "eval");
- instance.stepDefinitions.add(new ClojureStepDefinition(regexp, body, location));
- }
-
- @Override
- public List<HookDefinition> getBeforeHooks() {
- return new ArrayList<HookDefinition>();
- }
-
- @Override
- public List<HookDefinition> getAfterHooks() {
- return new ArrayList<HookDefinition>();
+ instance.world.addStepDefinition(new ClojureStepDefinition(regexp, body, location));
}
}
View
11 core/src/main/java/cucumber/cli/Runner.java
@@ -6,8 +6,10 @@
import cucumber.runtime.FeatureBuilder;
import cucumber.runtime.Runtime;
import cucumber.runtime.model.CucumberFeature;
+import cucumber.runtime.model.CucumberScenario;
import gherkin.formatter.Formatter;
import gherkin.formatter.Reporter;
+import gherkin.formatter.model.Step;
import java.util.ArrayList;
import java.util.List;
@@ -21,7 +23,14 @@ public Runner(Runtime runtime) {
public void run(List<String> filesOrDirs, final List<Object> filters, Formatter formatter, Reporter reporter) {
for (CucumberFeature cucumberFeature : load(filesOrDirs, filters)) {
- cucumberFeature.run(runtime, formatter, reporter);
+ for (CucumberScenario cucumberScenario : cucumberFeature.getCucumberScenarios()) {
+ // TODO: Maybe get extraPaths from scenario
+ runtime.prepareAndFormat(cucumberScenario, formatter, new ArrayList<String>());
+ for (Step step : cucumberScenario.getSteps()) {
+ runtime.runStep(cucumberScenario.getUri(), step, reporter, cucumberScenario.getLocale());
+ }
+ runtime.dispose();
+ }
}
}
View
11 core/src/main/java/cucumber/runtime/Backend.java
@@ -5,19 +5,16 @@
import java.util.List;
public interface Backend {
- List<StepDefinition> getStepDefinitions();
-
/**
* Invoked before a new scenario starts. Implementations should do any necessary
* setup of new, isolated state here.
+ *
+ * @param codePaths
+ * @param world
*/
- void newWorld();
+ void buildWorld(List<String> codePaths, World world);
void disposeWorld();
String getSnippet(Step step);
-
- List<HookDefinition> getBeforeHooks();
-
- List<HookDefinition> getAfterHooks();
}
View
62 core/src/main/java/cucumber/runtime/Runtime.java
@@ -1,48 +1,48 @@
package cucumber.runtime;
import cucumber.resources.Resources;
-import cucumber.runtime.converters.LocalizedXStreams;
-import cucumber.table.CamelCaseHeaderMapper;
-import cucumber.table.TableHeaderMapper;
-import gherkin.formatter.Argument;
+import cucumber.runtime.model.CucumberScenario;
+import gherkin.formatter.Formatter;
+import gherkin.formatter.Reporter;
import gherkin.formatter.model.Step;
import java.util.*;
+import static java.util.Arrays.asList;
+
public class Runtime {
private final List<Step> undefinedSteps = new ArrayList<Step>();
private final List<Backend> backends;
- private final LocalizedXStreams localizedXStreams = new LocalizedXStreams();
- private final TableHeaderMapper tableHeaderMapper = new CamelCaseHeaderMapper();
+ private final List<String> codePaths;
+ private World world;
- public Runtime(List<String> packageNamesOrScriptPaths) {
- backends = Resources.instantiateSubclasses(Backend.class, "cucumber.runtime", new Class[]{List.class}, new Object[]{packageNamesOrScriptPaths});
+ public Runtime() {
+ this(System.getProperty("cucumber.glue") != null ? asList(System.getProperty("cucumber.glue").split(",")) : new ArrayList<String>());
}
- public StepDefinitionMatch stepDefinitionMatch(String uri, Step step) {
- List<StepDefinitionMatch> matches = stepDefinitionMatches(uri, step);
- if (matches.size() == 0) {
- undefinedSteps.add(step);
- return null;
- }
- if (matches.size() == 1) {
- return matches.get(0);
- } else {
- throw new AmbiguousStepDefinitionsException(matches);
- }
+ public Runtime(List<String> codePaths) {
+ backends = Resources.instantiateSubclasses(Backend.class, "cucumber.runtime", new Class[0], new Object[0]);
+ this.codePaths = codePaths;
}
- private List<StepDefinitionMatch> stepDefinitionMatches(String uri, Step step) {
- List<StepDefinitionMatch> result = new ArrayList<StepDefinitionMatch>();
- for (Backend backend : backends) {
- for (StepDefinition stepDefinition : backend.getStepDefinitions()) {
- List<Argument> arguments = stepDefinition.matchedArguments(step);
- if (arguments != null) {
- result.add(new StepDefinitionMatch(arguments, stepDefinition, uri, step, localizedXStreams, tableHeaderMapper));
- }
- }
+ public void prepareAndFormat(CucumberScenario cucumberScenario, Formatter formatter, List<String> extraCodePaths) {
+ List<String> allCodePaths = new ArrayList<String>(codePaths);
+ allCodePaths.addAll(extraCodePaths);
+
+ world = new World(backends, this, cucumberScenario.tags());
+ world.prepare(allCodePaths);
+ formatter.scenario(cucumberScenario.getScenario());
+ for (Step step : cucumberScenario.getSteps()) {
+ formatter.step(step);
}
- return result;
+ }
+
+ public void runStep(String uri, Step step, Reporter reporter, Locale locale) {
+ world.runStep(uri, step, reporter, locale);
+ }
+
+ public void dispose() {
+ world.dispose();
}
/**
@@ -74,7 +74,7 @@ public int compare(Step a, Step b) {
return snippets;
}
- public World newWorld(Set<String> tags) {
- return new World(backends, this, tags);
+ public void undefinedStep(Step step) {
+ undefinedSteps.add(step);
}
}
View
70 core/src/main/java/cucumber/runtime/World.java
@@ -1,5 +1,9 @@
package cucumber.runtime;
+import cucumber.runtime.converters.LocalizedXStreams;
+import cucumber.table.CamelCaseHeaderMapper;
+import cucumber.table.TableHeaderMapper;
+import gherkin.formatter.Argument;
import gherkin.formatter.Reporter;
import gherkin.formatter.model.Match;
import gherkin.formatter.model.Result;
@@ -10,6 +14,12 @@
public class World {
private static final Object DUMMY_ARG = new Object();
+ private final LocalizedXStreams localizedXStreams = new LocalizedXStreams();
+ private final TableHeaderMapper tableHeaderMapper = new CamelCaseHeaderMapper();
+ private final List<StepDefinition> stepDefinitions = new ArrayList<StepDefinition>();
+ private final List<HookDefinition> beforeHooks = new ArrayList<HookDefinition>();
+ private final List<HookDefinition> afterHooks = new ArrayList<HookDefinition>();
+
private final List<Backend> backends;
private final Runtime runtime;
private final Collection<String> tags;
@@ -23,24 +33,30 @@ public World(List<Backend> backends, Runtime runtime, Collection<String> tags) {
this.tags = tags;
}
- public void prepare() {
- scenarioResult = new ScenarioResultImpl();
- List<HookDefinition> beforeHooks = new ArrayList<HookDefinition>();
+ public void prepare(List<String> codePaths) {
for (Backend backend : backends) {
- backend.newWorld();
- beforeHooks.addAll(backend.getBeforeHooks());
+ backend.buildWorld(codePaths, this);
}
+
+ scenarioResult = new ScenarioResultImpl();
Collections.sort(beforeHooks, new HookComparator(true));
for (HookDefinition hook : beforeHooks) {
runHookMaybe(hook, null);
}
}
- public void dispose() {
- List<HookDefinition> afterHooks = new ArrayList<HookDefinition>();
- for (Backend backend : backends) {
- afterHooks.addAll(backend.getAfterHooks());
+ private List<StepDefinitionMatch> stepDefinitionMatches(String uri, Step step) {
+ List<StepDefinitionMatch> result = new ArrayList<StepDefinitionMatch>();
+ for (StepDefinition stepDefinition : stepDefinitions) {
+ List<Argument> arguments = stepDefinition.matchedArguments(step);
+ if (arguments != null) {
+ result.add(new StepDefinitionMatch(arguments, stepDefinition, uri, step, localizedXStreams, tableHeaderMapper));
+ }
}
+ return result;
+ }
+
+ public void dispose() {
Collections.sort(afterHooks, new HookComparator(false));
for (HookDefinition hook : afterHooks) {
runHookMaybe(hook, scenarioResult);
@@ -62,7 +78,7 @@ private void runHookMaybe(HookDefinition hook, ScenarioResult scenarioResult) {
}
public void runStep(String uri, Step step, Reporter reporter, Locale locale) {
- StepDefinitionMatch match = runtime.stepDefinitionMatch(uri, step);
+ StepDefinitionMatch match = stepDefinitionMatch(uri, step);
if (match != null) {
reporter.match(match);
} else {
@@ -92,8 +108,40 @@ public void runStep(String uri, Step step, Reporter reporter, Locale locale) {
}
}
- private final class HookComparator implements Comparator<HookDefinition> {
+ private StepDefinitionMatch stepDefinitionMatch(String uri, Step step) {
+ List<StepDefinitionMatch> matches = stepDefinitionMatches(uri, step);
+ if (matches.size() == 0) {
+ runtime.undefinedStep(step);
+ return null;
+ }
+ if (matches.size() == 1) {
+ return matches.get(0);
+ } else {
+ throw new AmbiguousStepDefinitionsException(matches);
+ }
+ }
+
+ public void addStepDefinition(StepDefinition stepDefinition) {
+ stepDefinitions.add(stepDefinition);
+ }
+ public void addBeforeHook(HookDefinition hookDefinition) {
+ beforeHooks.add(hookDefinition);
+ }
+
+ public void addAfterHook(HookDefinition hookDefinition) {
+ afterHooks.add(hookDefinition);
+ }
+
+ public List<HookDefinition> getBeforeHooks() {
+ return beforeHooks;
+ }
+
+ public List<HookDefinition> getAfterHooks() {
+ return afterHooks;
+ }
+
+ private final class HookComparator implements Comparator<HookDefinition> {
final boolean ascending;
public HookComparator(boolean ascending) {
View
12 core/src/main/java/cucumber/runtime/model/CucumberFeature.java
@@ -1,8 +1,5 @@
package cucumber.runtime.model;
-import cucumber.runtime.Runtime;
-import gherkin.formatter.Formatter;
-import gherkin.formatter.Reporter;
import gherkin.formatter.model.Background;
import gherkin.formatter.model.Feature;
import gherkin.formatter.model.Scenario;
@@ -42,15 +39,6 @@ public Feature getFeature() {
return feature;
}
- public void run(Runtime runtime, Formatter formatter, Reporter reporter) {
- formatter.uri(featureUri);
- formatter.feature(feature);
- for (CucumberScenario cucumberScenario : cucumberScenarios) {
- cucumberScenario.prepareAndFormat(runtime, formatter);
- cucumberScenario.runAndDispose(reporter);
- }
- }
-
public List<CucumberScenario> getCucumberScenarios() {
return cucumberScenarios;
}
View
42 core/src/main/java/cucumber/runtime/model/CucumberScenario.java
@@ -1,17 +1,11 @@
package cucumber.runtime.model;
-import cucumber.runtime.Runtime;
import cucumber.runtime.World;
-import gherkin.formatter.Formatter;
-import gherkin.formatter.Reporter;
import gherkin.formatter.model.Scenario;
import gherkin.formatter.model.Step;
import gherkin.formatter.model.Tag;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
public class CucumberScenario {
private final List<Step> steps = new ArrayList<Step>();
@@ -35,35 +29,11 @@ public Scenario getScenario() {
return steps;
}
- public void prepareAndFormat(Runtime runtime, Formatter formatter) {
- world = runtime.newWorld(tags());
- world.prepare();
- formatter.scenario(scenario);
- for (Step step : steps) {
- formatter.step(step);
- }
- }
-
- public void runAndDispose(Reporter reporter) {
- for (Step step : steps) {
- runStep(step, reporter);
- }
- dispose();
- }
-
- public void dispose() {
- world.dispose();
- }
-
- public void runStep(Step step, Reporter reporter) {
- world.runStep(uri, step, reporter, cucumberFeature.getLocale());
- }
-
public void step(Step step) {
steps.add(step);
}
- private Set<String> tags() {
+ public Set<String> tags() {
Set<String> tags = new HashSet<String>();
for (Tag tag : cucumberFeature.getFeature().getTags()) {
tags.add(tag.getName());
@@ -73,4 +43,12 @@ public void step(Step step) {
}
return tags;
}
+
+ public String getUri() {
+ return uri;
+ }
+
+ public Locale getLocale() {
+ return cucumberFeature.getLocale();
+ }
}
View
25 core/src/test/java/cucumber/runtime/HookOrderTest.java
@@ -5,7 +5,6 @@
import org.mockito.InOrder;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import static org.mockito.Matchers.anyListOf;
@@ -14,20 +13,20 @@
public class HookOrderTest {
private World world;
- private Backend[] backends;
@Before
public void buildMockWorld() {
- backends = new Backend[]{mock(Backend.class), mock(Backend.class)};
- world = new World(Arrays.asList(backends), mock(Runtime.class), new ArrayList<String>());
+ world = new World(new ArrayList<Backend>(), mock(Runtime.class), new ArrayList<String>());
}
@Test
public void before_hooks_execute_in_order() throws Throwable {
List<HookDefinition> hooks = mockHooks(3, Integer.MAX_VALUE, 1);
- when(backends[0].getBeforeHooks()).thenReturn(hooks);
+ for (HookDefinition hook : hooks) {
+ world.addBeforeHook(hook);
+ }
- world.prepare();
+ world.prepare(new ArrayList<String>());
InOrder inOrder = inOrder(hooks.toArray());
inOrder.verify(hooks.get(2)).execute(null);
@@ -38,7 +37,9 @@ public void before_hooks_execute_in_order() throws Throwable {
@Test
public void after_hooks_execute_in_reverse_order() throws Throwable {
List<HookDefinition> hooks = mockHooks(2, Integer.MAX_VALUE, 4);
- when(backends[0].getAfterHooks()).thenReturn(hooks);
+ for (HookDefinition hook : hooks) {
+ world.addAfterHook(hook);
+ }
world.dispose();
@@ -51,11 +52,15 @@ public void after_hooks_execute_in_reverse_order() throws Throwable {
@Test
public void hooks_order_across_many_backends() throws Throwable {
List<HookDefinition> backend1Hooks = mockHooks(3, Integer.MAX_VALUE, 1);
+ for (HookDefinition hook : backend1Hooks) {
+ world.addBeforeHook(hook);
+ }
List<HookDefinition> backend2Hooks = mockHooks(2, Integer.MAX_VALUE, 4);
- when(backends[0].getBeforeHooks()).thenReturn(backend1Hooks);
- when(backends[1].getBeforeHooks()).thenReturn(backend2Hooks);
+ for (HookDefinition hook : backend2Hooks) {
+ world.addBeforeHook(hook);
+ }
- world.prepare();
+ world.prepare(new ArrayList<String>());
List<HookDefinition> allHooks = new ArrayList<HookDefinition>();
allHooks.addAll(backend1Hooks);
View
8 core/src/test/java/cucumber/runtime/HookTest.java
@@ -20,14 +20,10 @@ public void after_hooks_execute_before_objects_are_disposed() throws Throwable {
HookDefinition hook = mock(HookDefinition.class);
when(hook.matches(anyListOf(String.class))).thenReturn(true);
- List<HookDefinition> hookList = new ArrayList<HookDefinition>();
- hookList.add(hook);
- when(backend.getAfterHooks()).thenReturn(hookList);
-
List<Backend> backendList = new ArrayList<Backend>();
backendList.add(backend);
- World world = new World(backendList, mock(Runtime.class),
- new ArrayList<String>());
+ World world = new World(backendList, mock(Runtime.class), new ArrayList<String>());
+ world.addAfterHook(hook);
world.dispose();
View
1 cucumber-jvm.ipr
@@ -279,6 +279,7 @@
<module fileurl="file://$PROJECT_DIR$/examples/java-calculator/java-calculator.iml" filepath="$PROJECT_DIR$/examples/java-calculator/java-calculator.iml" />
<module fileurl="file://$PROJECT_DIR$/examples/java-webbit-websockets-selenium/java-webbit-websockets-selenium.iml" filepath="$PROJECT_DIR$/examples/java-webbit-websockets-selenium/java-webbit-websockets-selenium.iml" />
<module fileurl="file://$PROJECT_DIR$/jruby/jruby.iml" filepath="$PROJECT_DIR$/jruby/jruby.iml" />
+ <module fileurl="file://$PROJECT_DIR$/junit/junit.iml" filepath="$PROJECT_DIR$/junit/junit.iml" />
<module fileurl="file://$PROJECT_DIR$/jython/jython.iml" filepath="$PROJECT_DIR$/jython/jython.iml" />
<module fileurl="file://$PROJECT_DIR$/parent.iml" filepath="$PROJECT_DIR$/parent.iml" />
<module fileurl="file://$PROJECT_DIR$/picocontainer/picocontainer.iml" filepath="$PROJECT_DIR$/picocontainer/picocontainer.iml" />
View
1 examples/java-calculator/java-calculator.iml
@@ -29,6 +29,7 @@
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.8.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.googlecode.java-diff-utils:diffutils:1.2.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.picocontainer:picocontainer:2.13.6" level="project" />
+ <orderEntry type="module" module-name="junit" scope="TEST" />
</component>
</module>
View
6 examples/java-calculator/pom.xml
@@ -19,7 +19,11 @@
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-picocontainer</artifactId>
- <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>info.cukes</groupId>
+ <artifactId>cucumber-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
View
1 examples/java-webbit-websockets-selenium/java-webbit-websockets-selenium.iml
@@ -31,6 +31,7 @@
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.8.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.googlecode.java-diff-utils:diffutils:1.2.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.picocontainer:picocontainer:2.13.6" level="project" />
+ <orderEntry type="module" module-name="junit" scope="TEST" />
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-chrome-driver:2.5.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-remote-driver:2.5.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: cglib:cglib-nodep:2.1_3" level="project" />
View
6 examples/java-webbit-websockets-selenium/pom.xml
@@ -24,7 +24,11 @@
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-picocontainer</artifactId>
- <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>info.cukes</groupId>
+ <artifactId>cucumber-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
View
6 examples/scala-calculator/pom.xml
@@ -19,7 +19,11 @@
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-scala</artifactId>
- <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>info.cukes</groupId>
+ <artifactId>cucumber-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
View
2 examples/scala-calculator/scala-calculator.iml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="scala" name="Scala">
<configuration>
View
1 groovy/groovy.iml
@@ -25,6 +25,7 @@
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.8.2" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.java-diff-utils:diffutils:1.2.1" level="project" />
<orderEntry type="library" name="Maven: org.codehaus.groovy:groovy-all:1.9.0-beta-2" level="project" />
+ <orderEntry type="module" module-name="junit" scope="TEST" />
</component>
</module>
View
5 groovy/pom.xml
@@ -25,6 +25,11 @@
<artifactId>groovy-all</artifactId>
</dependency>
<dependency>
+ <groupId>info.cukes</groupId>
+ <artifactId>cucumber-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
View
61 groovy/src/main/java/cucumber/runtime/groovy/GroovyBackend.java
@@ -4,57 +4,48 @@
import cucumber.resources.Resource;
import cucumber.resources.Resources;
import cucumber.runtime.Backend;
-import cucumber.runtime.CucumberException;
-import cucumber.runtime.HookDefinition;
import cucumber.runtime.StepDefinition;
+import cucumber.runtime.World;
import gherkin.formatter.model.Step;
import groovy.lang.Binding;
import groovy.lang.Closure;
import groovy.lang.GroovyShell;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public class GroovyBackend implements Backend {
private static GroovyBackend instance;
-
private final List<StepDefinition> stepDefinitions = new ArrayList<StepDefinition>();
- private static Closure worldClosure;
- private static Object world;
+ private final GroovyShell shell;
+ private Closure worldClosure;
+ private Object groovyWorld;
+ private World world;
- public GroovyBackend(List<String> scriptPaths) {
+ public GroovyBackend() {
instance = this;
- try {
- defineStepDefinitions(scriptPaths);
- } catch (IOException e) {
- throw new CucumberException("Couldn't load stepdefs", e);
- }
+ shell = new GroovyShell(new Binding());
}
- private void defineStepDefinitions(List<String> scriptPaths) throws IOException {
- final GroovyShell shell = new GroovyShell(new Binding());
- for (String scriptPath : scriptPaths) {
- Resources.scan(scriptPath.replace('.', '/'), ".groovy", new Consumer() {
+ @Override
+ public void buildWorld(List<String> codePaths, World world) {
+ this.world = world;
+ for (String codePath : codePaths) {
+ Resources.scan(codePath.replace('.', '/'), ".groovy", new Consumer() {
public void consume(Resource resource) {
shell.evaluate(resource.getString(), resource.getPath());
}
});
}
}
- public List<StepDefinition> getStepDefinitions() {
- return stepDefinitions;
- }
-
- public void newWorld() {
- world = null;
- }
-
+ @Override
public void disposeWorld() {
+ this.groovyWorld = null;
}
+ @Override
public String getSnippet(Step step) {
return new GroovySnippetGenerator(step).getSnippet();
}
@@ -64,19 +55,19 @@ public static void addStepDefinition(Pattern regexp, Closure body) {
}
public static void registerWorld(Closure closure) {
- worldClosure = closure;
+ instance.worldClosure = closure;
}
public void invokeStepDefinition(Closure body, Object[] args) {
- body.setDelegate(getWorld());
+ body.setDelegate(getGroovyWorld());
body.call(args);
}
- private Object getWorld() {
- if (world == null) {
- world = worldClosure == null ? new Object() : worldClosure.call();
+ private Object getGroovyWorld() {
+ if (groovyWorld == null) {
+ groovyWorld = worldClosure == null ? new Object() : worldClosure.call();
}
- return world;
+ return groovyWorld;
}
private static StackTraceElement stepDefLocation() {
@@ -89,14 +80,4 @@ private static StackTraceElement stepDefLocation() {
}
throw new RuntimeException("Couldn't find location for step definition");
}
-
- @Override
- public List<HookDefinition> getBeforeHooks() {
- return new ArrayList<HookDefinition>();
- }
-
- @Override
- public List<HookDefinition> getAfterHooks() {
- return new ArrayList<HookDefinition>();
- }
}
View
1 guice/guice.iml
@@ -29,6 +29,7 @@
<orderEntry type="library" name="Maven: com.google.inject:guice:3.0" level="project" />
<orderEntry type="library" name="Maven: javax.inject:javax.inject:1" level="project" />
<orderEntry type="library" name="Maven: aopalliance:aopalliance:1.0" level="project" />
+ <orderEntry type="module" module-name="junit" scope="TEST" />
</component>
</module>
View
5 guice/pom.xml
@@ -25,6 +25,11 @@
<artifactId>guice</artifactId>
</dependency>
<dependency>
+ <groupId>info.cukes</groupId>
+ <artifactId>cucumber-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
View
62 ioke/src/main/java/cucumber/runtime/ioke/IokeBackend.java
@@ -5,8 +5,7 @@
import cucumber.resources.Resources;
import cucumber.runtime.Backend;
import cucumber.runtime.CucumberException;
-import cucumber.runtime.HookDefinition;
-import cucumber.runtime.StepDefinition;
+import cucumber.runtime.World;
import cucumber.table.Table;
import gherkin.formatter.model.Step;
import ioke.lang.IokeObject;
@@ -22,54 +21,52 @@
private final Runtime ioke;
private final List<Runtime.RescueInfo> failureRescues;
private final List<Runtime.RescueInfo> pendingRescues;
- private final List<StepDefinition> stepDefinitions = new ArrayList<StepDefinition>();
private String currentLocation;
+ private World world;
- public IokeBackend(List<String> scriptPaths) {
+ public IokeBackend() {
try {
ioke = new Runtime();
ioke.init();
ioke.ground.setCell("IokeBackend", this);
ioke.evaluateString("use(\"cucumber/runtime/ioke/dsl\")");
-
failureRescues = createRescues("ISpec", "ExpectationNotMet");
pendingRescues = createRescues("Pending");
-
- for (String scriptPath : scriptPaths) {
- Resources.scan(scriptPath.replace('.', '/'), ".ik", new Consumer() {
- public void consume(Resource resource) {
- try {
- currentLocation = resource.getPath();
- ioke.evaluateString("use(\"" + resource.getPath() + "\")");
- } catch (ControlFlow controlFlow) {
- throw new CucumberException("Failed to load " + resource.getPath(), controlFlow);
- }
- }
- });
- }
} catch (Throwable e) {
throw new CucumberException("Failed to initialize Ioke", e);
}
}
- public void addStepDefinition(Object iokeStepDefObject) throws Throwable {
- stepDefinitions.add(new IokeStepDefinition(this, ioke, (IokeObject) iokeStepDefObject, currentLocation));
- }
-
- public List<StepDefinition> getStepDefinitions() {
- return stepDefinitions;
- }
-
- public void newWorld() {
+ @Override
+ public void buildWorld(List<String> codePaths, World world) {
+ this.world = world;
+ for (String codePath : codePaths) {
+ Resources.scan(codePath.replace('.', '/'), ".ik", new Consumer() {
+ public void consume(Resource resource) {
+ try {
+ currentLocation = resource.getPath();
+ ioke.evaluateString("use(\"" + resource.getPath() + "\")");
+ } catch (ControlFlow controlFlow) {
+ throw new CucumberException("Failed to load " + resource.getPath(), controlFlow);
+ }
+ }
+ });
+ }
}
+ @Override
public void disposeWorld() {
}
+ @Override
public String getSnippet(Step step) {
return new IokeSnippetGenerator(step).getSnippet();
}
+ public void addStepDefinition(Object iokeStepDefObject) throws Throwable {
+ world.addStepDefinition(new IokeStepDefinition(this, ioke, (IokeObject) iokeStepDefObject, currentLocation));
+ }
+
private List<Runtime.RescueInfo> createRescues(String... names) throws ControlFlow {
IokeObject condition = IokeObject.as(IokeObject.getCellChain(ioke.condition,
ioke.message,
@@ -127,15 +124,4 @@ Object invoke(IokeObject iokeStepDefObject, String message, Object... args) thro
Message m = (Message) IokeObject.data(msg);
return m.sendTo(msg, iokeStepDefObject, iokeStepDefObject, Arrays.asList(args));
}
-
- @Override
- public List<HookDefinition> getBeforeHooks() {
- return new ArrayList<HookDefinition>();
- }
-
- @Override
- public List<HookDefinition> getAfterHooks() {
- return new ArrayList<HookDefinition>();
- }
-
}
View
44 java/src/main/java/cucumber/runtime/java/JavaBackend.java
@@ -5,68 +5,54 @@
import cucumber.annotation.Order;
import cucumber.resources.Resources;
import cucumber.runtime.Backend;
-import cucumber.runtime.HookDefinition;
-import cucumber.runtime.StepDefinition;
+import cucumber.runtime.World;
import gherkin.formatter.model.Step;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
-import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
public class JavaBackend implements Backend {
- private final List<StepDefinition> stepDefinitions = new ArrayList<StepDefinition>();
- private final List<HookDefinition> beforeHooks = new ArrayList<HookDefinition>();
- private final List<HookDefinition> afterHooks = new ArrayList<HookDefinition>();
private final Set<Class> stepDefinitionClasses = new HashSet<Class>();
private final ObjectFactory objectFactory;
+ private final ClasspathMethodScanner classpathMethodScanner = new ClasspathMethodScanner();
+ private World world;
- public JavaBackend(List<String> packagePrefixes) {
- this.objectFactory = Resources.instantiateExactlyOneSubclass(ObjectFactory.class, "cucumber.runtime", new Class[0], new Object[0]);
- ClasspathMethodScanner classpathMethodScanner = new ClasspathMethodScanner();
- for (String packagePrefix : packagePrefixes) {
- classpathMethodScanner.scan(this, packagePrefix);
- }
+ public JavaBackend() {
+ this(Resources.instantiateExactlyOneSubclass(ObjectFactory.class, "cucumber.runtime", new Class[0], new Object[0]));
}
public JavaBackend(ObjectFactory objectFactory) {
this.objectFactory = objectFactory;
}
- public List<StepDefinition> getStepDefinitions() {
- return stepDefinitions;
- }
-
- @Override
- public List<HookDefinition> getBeforeHooks() {
- return beforeHooks;
- }
-
@Override
- public List<HookDefinition> getAfterHooks() {
- return afterHooks;
- }
-
- public void newWorld() {
+ public void buildWorld(List<String> codePaths, World world) {
+ this.world = world;
+ for (String codePath : codePaths) {
+ classpathMethodScanner.scan(this, codePath);
+ }
objectFactory.createInstances();
}
+ @Override
public void disposeWorld() {
objectFactory.disposeInstances();
}
+ @Override
public String getSnippet(Step step) {
return new JavaSnippetGenerator(step).getSnippet();
}
void addStepDefinition(Pattern pattern, Method method) {
Class<?> clazz = method.getDeclaringClass();
registerClassInObjectFactory(clazz);
- stepDefinitions.add(new JavaStepDefinition(pattern, method, objectFactory));
+ world.addStepDefinition(new JavaStepDefinition(pattern, method, objectFactory));
}
void registerHook(Annotation annotation, Method method) {
@@ -78,10 +64,10 @@ void registerHook(Annotation annotation, Method method) {
if (annotation.annotationType().equals(Before.class)) {
String[] tagExpressions = ((Before) annotation).value();
- beforeHooks.add(new JavaHookDefinition(method, tagExpressions, hookOrder, objectFactory));
+ world.addBeforeHook(new JavaHookDefinition(method, tagExpressions, hookOrder, objectFactory));
} else {
String[] tagExpressions = ((After) annotation).value();
- afterHooks.add(new JavaHookDefinition(method, tagExpressions, hookOrder, objectFactory));
+ world.addAfterHook(new JavaHookDefinition(method, tagExpressions, hookOrder, objectFactory));
}
}
View
8 java/src/main/java/cucumber/runtime/java/JavaHookDefinition.java
@@ -31,17 +31,19 @@ Method getMethod() {
@Override
public void execute(ScenarioResult scenarioResult) throws Throwable {
Object target = objectFactory.getInstance(method.getDeclaringClass());
+ if (target == null) {
+ throw new IllegalStateException("Bug: No target for " + method);
+ }
Object[] args;
- if(method.getParameterTypes().length == 1) {
+ if (method.getParameterTypes().length == 1) {
args = new Object[]{scenarioResult};
} else {
args = new Object[0];
}
try {
method.invoke(target, args);
} catch (IllegalArgumentException e) {
- throw new CucumberException("Can't invoke "
- + new MethodFormat().format(method));
+ throw new CucumberException("Can't invoke " + new MethodFormat().format(method));
}
}
View
5 java/src/main/java/cucumber/runtime/java/JavaStepDefinition.java
@@ -34,8 +34,9 @@ public JavaStepDefinition(Pattern pattern, Method method, ObjectFactory objectFa
public void execute(Object[] args) throws Throwable {
if (method.isAnnotationPresent(Pending.class)) {
throw new PendingException(method.getAnnotation(Pending.class).value());
- }
- Object target = objectFactory.getInstance(method.getDeclaringClass());
+ }
+ Class<?> clazz = method.getDeclaringClass();
+ Object target = objectFactory.getInstance(clazz);
try {
method.invoke(target, args);
} catch (IllegalArgumentException e) {
View
32 java/src/test/java/cucumber/runtime/java/JavaHookTest.java
@@ -3,13 +3,18 @@
import cucumber.annotation.After;
import cucumber.annotation.Before;
import cucumber.annotation.Order;
+import cucumber.runtime.Backend;
import cucumber.runtime.HookDefinition;
+import cucumber.runtime.World;
import org.junit.Test;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import static java.util.Arrays.asList;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
public class JavaHookTest {
@@ -25,49 +30,56 @@
}
}
- private JavaBackend backend = new JavaBackend(mock(ObjectFactory.class));
+ private final JavaBackend backend = new JavaBackend(mock(ObjectFactory.class));
+ private final World world = new World(new ArrayList<Backend>(), null, new ArrayList<String>());
@Test
public void before_hooks_get_registered() throws Exception {
+ backend.buildWorld(new ArrayList<String>(), world);
backend.registerHook(BEFORE.getAnnotation(Before.class), BEFORE);
- JavaHookDefinition hookDef = (JavaHookDefinition) backend.getBeforeHooks().get(0);
- assertEquals(0, backend.getAfterHooks().size());
+ JavaHookDefinition hookDef = (JavaHookDefinition) world.getBeforeHooks().get(0);
+ assertEquals(0, world.getAfterHooks().size());
assertEquals(BEFORE, hookDef.getMethod());
}
@Test
public void after_hooks_get_registered() throws Exception {
+ backend.buildWorld(new ArrayList<String>(), world);
backend.registerHook(AFTER.getAnnotation(After.class), AFTER);
- JavaHookDefinition hookDef = (JavaHookDefinition) backend.getAfterHooks().get(0);
- assertEquals(0, backend.getBeforeHooks().size());
+ JavaHookDefinition hookDef = (JavaHookDefinition) world.getAfterHooks().get(0);
+ assertEquals(0, world.getBeforeHooks().size());
assertEquals(AFTER, hookDef.getMethod());
}
@Test
public void hook_order_gets_registered() {
+ backend.buildWorld(new ArrayList<String>(), world);
backend.registerHook(AFTER.getAnnotation(After.class), AFTER);
- HookDefinition hookDef = backend.getAfterHooks().get(0);
+ HookDefinition hookDef = world.getAfterHooks().get(0);
assertEquals(1, hookDef.getOrder());
}
@Test
public void hook_with_no_order_is_last() {
+ backend.buildWorld(new ArrayList<String>(), world);
backend.registerHook(BEFORE.getAnnotation(Before.class), BEFORE);
- HookDefinition hookDef = backend.getBeforeHooks().get(0);
+ HookDefinition hookDef = world.getBeforeHooks().get(0);
assertEquals(Integer.MAX_VALUE, hookDef.getOrder());
}
@Test
public void matches_matching_tags() {
+ backend.buildWorld(new ArrayList<String>(), world);
backend.registerHook(BEFORE.getAnnotation(Before.class), BEFORE);
- HookDefinition before = backend.getBeforeHooks().get(0);
+ HookDefinition before = world.getBeforeHooks().get(0);
assertTrue(before.matches(asList("@bar", "@zap")));
}
@Test
public void does_not_match_non_matching_tags() {
+ backend.buildWorld(new ArrayList<String>(), world);
backend.registerHook(BEFORE.getAnnotation(Before.class), BEFORE);
- HookDefinition before = backend.getBeforeHooks().get(0);
+ HookDefinition before = world.getBeforeHooks().get(0);
assertFalse(before.matches(asList("@bar")));
}
View
13 java/src/test/java/cucumber/runtime/java/JavaStepDefinitionDependencyInjectionTest.java
@@ -1,18 +1,15 @@
package cucumber.runtime.java;
import cucumber.annotation.en.Given;
-import cucumber.runtime.StepDefinition;
-import org.junit.Ignore;
+import cucumber.runtime.Backend;
+import cucumber.runtime.World;
import org.junit.Test;
import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collections;
import java.util.regex.Pattern;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.*;
public class JavaStepDefinitionDependencyInjectionTest {
@@ -33,6 +30,7 @@
@Test
public void constructor_arguments_get_registered() {
+ backend.buildWorld(Collections.<String>emptyList(), new World(Collections.<Backend>emptyList(), null, Collections.<String>emptyList()));
backend.addStepDefinition(Pattern.compile("whatever"), GIVEN);
verify(mockObjectFactory).addClass(Steps.class);
verify(mockObjectFactory).addClass(StepContext1.class);
@@ -41,6 +39,7 @@ public void constructor_arguments_get_registered() {
@Test
public void constructor_arguments_get_registered_exactly_once() {
+ backend.buildWorld(Collections.<String>emptyList(), new World(Collections.<Backend>emptyList(), null, Collections.<String>emptyList()));
backend.addStepDefinition(Pattern.compile("whatever"), OTHER_GIVEN);
verify(mockObjectFactory, times(1)).addClass(OtherSteps.class);
verify(mockObjectFactory, times(1)).addClass(StepContext3.class);
View
1 java/src/test/resources/cucumber-features/cucumber_java_mappings.rb
@@ -42,6 +42,7 @@ def write_pom
<groupId>info.cukes</groupId>
<artifactId>cucumber-picocontainer</artifactId>
<version>1.0.0-SNAPSHOT</version>
+ <scope>test</scope>
</dependency>
</dependencies>
</project>
View
1 jruby/jruby.iml
@@ -27,6 +27,7 @@
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.8.2" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.java-diff-utils:diffutils:1.2.1" level="project" />
<orderEntry type="library" name="Maven: org.jruby:jruby:1.6.3" level="project" />
+ <orderEntry type="module" module-name="junit" scope="TEST" />
</component>
</module>
View
5 jruby/pom.xml
@@ -25,6 +25,11 @@
<artifactId>jruby</artifactId>
</dependency>
<dependency>
+ <groupId>info.cukes</groupId>
+ <artifactId>cucumber-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
View
45 jruby/src/main/java/cucumber/runtime/jruby/JRubyBackend.java
@@ -4,52 +4,38 @@
import cucumber.resources.Resource;
import cucumber.resources.Resources;
import cucumber.runtime.Backend;
-import cucumber.runtime.HookDefinition;
-import cucumber.runtime.StepDefinition;
+import cucumber.runtime.World;
import gherkin.formatter.model.Step;
import org.jruby.RubyObject;
import org.jruby.embed.ScriptingContainer;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
import java.util.List;
public class JRubyBackend implements Backend {
private static final String DSL = "/cucumber/runtime/jruby/dsl.rb";
private final ScriptingContainer jruby = new ScriptingContainer();
- private final List<StepDefinition> stepDefinitions = new ArrayList<StepDefinition>();
+ private World world;
- public JRubyBackend(List<String> scriptPaths) throws UnsupportedEncodingException {
- defineStepDefinitions(scriptPaths);
- }
-
- private void defineStepDefinitions(List<String> scriptPaths) throws UnsupportedEncodingException {
+ public JRubyBackend() throws UnsupportedEncodingException {
jruby.put("$backend", this);
jruby.runScriptlet(new InputStreamReader(getClass().getResourceAsStream(DSL), "UTF-8"), DSL);
- for (String scriptPath : scriptPaths) {
- Resources.scan(scriptPath.replace('.', '/'), ".rb", new Consumer() {
+ }
+
+ @Override
+ public void buildWorld(List<String> codePaths, World world) {
+ this.world = world;
+ jruby.put("$world", new Object());
+ for (String codePath : codePaths) {
+ Resources.scan(codePath.replace('.', '/'), ".rb", new Consumer() {
public void consume(Resource resource) {
jruby.runScriptlet(resource.getReader(), resource.getPath());
}
});
}
}
- public void registerStepdef(RubyObject stepdef) {
- stepDefinitions.add(new JRubyStepDefinition(stepdef));
- }
-
- @Override
- public List<StepDefinition> getStepDefinitions() {
- return stepDefinitions;
- }
-
- @Override
- public void newWorld() {
- jruby.put("$world", new Object());
- }
-
@Override
public void disposeWorld() {
}
@@ -59,13 +45,8 @@ public String getSnippet(Step step) {
return new JRubySnippetGenerator(step).getSnippet();
}
- @Override
- public List<HookDefinition> getBeforeHooks() {
- return new ArrayList<HookDefinition>();
+ public void registerStepdef(RubyObject stepdef) {
+ world.addStepDefinition(new JRubyStepDefinition(stepdef));
}
- @Override
- public List<HookDefinition> getAfterHooks() {
- return new ArrayList<HookDefinition>();
- }
}
View
28 junit/pom.xml
@@ -0,0 +1,28 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>info.cukes</groupId>
+ <artifactId>cucumber-jvm</artifactId>
+ <relativePath>../pom.xml</relativePath>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>cucumber-junit</artifactId>
+ <packaging>jar</packaging>
+ <name>Cucumber-JVM: JUnit</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>info.cukes</groupId>
+ <artifactId>cucumber-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+</project>
View
74 ...rc/main/java/cucumber/junit/Cucumber.java → ...rc/main/java/cucumber/junit/Cucumber.java
@@ -8,69 +8,52 @@
import cucumber.runtime.SnippetPrinter;
import cucumber.runtime.model.CucumberFeature;
import cucumber.runtime.model.CucumberScenario;
-import gherkin.formatter.PrettyFormatter;
import gherkin.formatter.model.Feature;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.InitializationError;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static java.util.Arrays.asList;
+/**
+ * Classes annotated with {@code @RunWith(Cucumber.class)} will run a Cucumber Feature.
+ * The class should be empty without any fields or methods.
+ *
+ * Cucumber will look for a {@code .feature} file on the classpath, using the same resource
+ * path as the annotated class ({@code .class} substituted by {@code .feature}).
+ *
+ * Additional hints can be given to Cucumber by annotating the class with {@link cucumber.junit.Feature}.
+ *
+ * @see cucumber.junit.Feature
+ */
public class Cucumber extends ParentRunner<ScenarioRunner> {
- private final Runtime runtime;
- private final List<ScenarioRunner> scenarioRunners = new ArrayList<ScenarioRunner>();
- private final JUnitReporter jUnitReporter;
- private final Feature feature;
- private final String featurePath;
+ private static final Runtime runtime = new Runtime();
+ private static JUnitReporter jUnitReporter;
- private static Runtime runtime(Class featureClass) {
- // TODO: This creates a Runtime for each test class. That's expensive.
- // We should only have a single Runtime per JVM. This means package/script paths
- // must not be passed to the constructor. Instead, each stepdef must know where it was
- // loaded from so we can apply the correct ones.
- final Runtime runtime = new Runtime(packageNamesOrScriptPaths(featureClass));
+ static {
+ jUnitReporter = JUnitReporterFactory.create(System.getProperty("cucumber.reporter"));
java.lang.Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
new SnippetPrinter(System.out).printSnippets(runtime);
}
});
- return runtime;
}
- private static List<String> packageNamesOrScriptPaths(Class featureClass) {
- List<String> packageNamesOrScriptPaths = new ArrayList<String>();
- String featurePackageName = featureClass.getName().substring(0, featureClass.getName().lastIndexOf("."));
- packageNamesOrScriptPaths.add(featurePackageName);
-
- // Add additional ones
- cucumber.junit.Feature featureAnnotation = (cucumber.junit.Feature) featureClass.getAnnotation(cucumber.junit.Feature.class);
- if (featureAnnotation != null) {
- packageNamesOrScriptPaths.addAll(asList(featureAnnotation.packages()));
- }
- return packageNamesOrScriptPaths;
- }
+ private final List<ScenarioRunner> scenarioRunners = new ArrayList<ScenarioRunner>();
+ private final Feature feature;
+ private final String featurePath;
- private static JUnitReporter jUnitReporter() {
- PrettyFormatter pf = new PrettyFormatter(System.out, false, true);
- return new JUnitReporter(pf, pf);
- }
-
/**
* Constructor called by JUnit.
*/
- public Cucumber(Class featureClass) throws InitializationError {
- this(featureClass, runtime(featureClass), jUnitReporter());
- }
-
- public Cucumber(Class featureClass, final Runtime runtime, JUnitReporter jUnitReporter) throws InitializationError {
+ public Cucumber(Class featureClass) throws InitializationError, IOException {
super(featureClass);
- this.runtime = runtime;
- this.jUnitReporter = jUnitReporter;
featurePath = featurePath(featureClass);
this.feature = parseFeature(featurePath, filters(featureClass));
@@ -150,12 +133,12 @@ public void consume(Resource resource) {
private void buildScenarioRunners(CucumberFeature cucumberFeature) {
for (CucumberScenario cucumberScenario : cucumberFeature.getCucumberScenarios()) {
try {
- scenarioRunners.add(new ScenarioRunner(runtime, cucumberScenario, jUnitReporter));
+ List<String> extraCodePaths = extraCodePaths(super.getTestClass().getJavaClass());
+ scenarioRunners.add(new ScenarioRunner(runtime, extraCodePaths, cucumberScenario, jUnitReporter));
} catch (InitializationError e) {
throw new RuntimeException("Failed to create scenario runner", e);
}
}
-
}
private Long[] toLong(long[] plongs) {
@@ -165,4 +148,17 @@ private void buildScenarioRunners(CucumberFeature cucumberFeature) {
}
return longs;
}
+
+ private List<String> extraCodePaths(Class featureClass) {
+ List<String> packageNamesOrScriptPaths = new ArrayList<String>();
+ String featurePackageName = featureClass.getName().substring(0, featureClass.getName().lastIndexOf("."));
+ packageNamesOrScriptPaths.add(featurePackageName);
+
+ // Add additional ones
+ cucumber.junit.Feature featureAnnotation = (cucumber.junit.Feature) featureClass.getAnnotation(cucumber.junit.Feature.class);
+ if (featureAnnotation != null) {
+ packageNamesOrScriptPaths.addAll(asList(featureAnnotation.packages()));
+ }
+ return packageNamesOrScriptPaths;
+ }
}
View
4 ...src/main/java/cucumber/junit/Feature.java → ...src/main/java/cucumber/junit/Feature.java
@@ -5,6 +5,10 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+/**
+ * This annotation can be used to give additional hints to the {@link Cucumber} runner
+ * about what to run.
+ */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Feature {
View
0 ...in/java/cucumber/junit/JUnitReporter.java → ...in/java/cucumber/junit/JUnitReporter.java
File renamed without changes.
View
65 junit/src/main/java/cucumber/junit/JUnitReporterFactory.java
@@ -0,0 +1,65 @@
+package cucumber.junit;
+
+import cucumber.runtime.NullReporter;
+import gherkin.formatter.Formatter;
+import gherkin.formatter.PrettyFormatter;
+import gherkin.formatter.Reporter;
+
+import java.io.FileWriter;
+import java.io.IOException;
+
+class JUnitReporterFactory {
+ /**
+ * Creates a JUnitReporter with an underlying gherkin reporter/formatter.
+ *
+ * @param reporterString what underlying reporter/formatter to create
+ * @return a reporter
+ */
+ static JUnitReporter create(String reporterString) {
+ Reporter reporter = null;
+ Formatter formatter;
+
+ if(reporterString != null) {
+ String[] nameAndOut = reporterString.split("=");
+ String name = nameAndOut[0];
+ Appendable appendable;
+ try {
+ if (nameAndOut.length < 2) {
+ appendable = System.out;
+ }
+ else {
+ final FileWriter fw = new FileWriter(nameAndOut[1]);
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ try {
+ fw.flush();
+ fw.close();
+ } catch (IOException ignore) {
+ }
+ }
+ });
+
+ appendable = fw;
+ }
+ } catch (IOException e) {
+ System.err.println("ERROR: Failed to create file " + nameAndOut[0] + ". Using STDOUT instead.");
+ appendable = System.out;
+ }
+ if(name.equals("pretty")) {
+ reporter = new PrettyFormatter(appendable, false, true);
+ } else {
+ // TODO: instantiate it with reflection. name is classname. expect one arg (Appendable).
+ }
+ }
+ if(reporter == null) {
+ reporter = new NullReporter();
+ }
+ if(reporter instanceof Formatter) {
+ formatter = (Formatter) reporter;
+ } else {
+ formatter = new NullReporter();
+ }
+ return new JUnitReporter(reporter, formatter);
+ }
+}
View
12 ...n/java/cucumber/junit/ScenarioRunner.java → ...n/java/cucumber/junit/ScenarioRunner.java
@@ -13,14 +13,16 @@
import java.util.List;
-public class ScenarioRunner extends ParentRunner<Step> {
+class ScenarioRunner extends ParentRunner<Step> {
private final Runtime runtime;
+ private final List<String> extraCodePaths;
private final CucumberScenario cucumberScenario;
private final JUnitReporter jUnitReporter;
- public ScenarioRunner(Runtime runtime, CucumberScenario cucumberScenario, JUnitReporter jUnitReporter) throws InitializationError {
+ public ScenarioRunner(Runtime runtime, List<String> extraCodePaths, CucumberScenario cucumberScenario, JUnitReporter jUnitReporter) throws InitializationError {
super(null);
this.runtime = runtime;
+ this.extraCodePaths = extraCodePaths;
this.cucumberScenario = cucumberScenario;
this.jUnitReporter = jUnitReporter;
}
@@ -49,20 +51,20 @@ protected Description describeChild(Step step) {
public void run(RunNotifier notifier) {
jUnitReporter.setRunner(this, notifier);
try {
- cucumberScenario.prepareAndFormat(runtime, jUnitReporter);
+ runtime.prepareAndFormat(cucumberScenario, jUnitReporter, extraCodePaths);
} catch (CucumberException e) {
notifier.fireTestFailure(new Failure(getDescription(), e));
}
super.run(notifier);
try {
- cucumberScenario.dispose();
+ runtime.dispose();
} catch (CucumberException e) {
notifier.fireTestFailure(new Failure(getDescription(), e));
}
}
@Override
protected void runChild(Step step, RunNotifier notifier) {
- cucumberScenario.runStep(step, jUnitReporter);
+ runtime.runStep(cucumberScenario.getUri(), step, jUnitReporter, cucumberScenario.getLocale());
}
}
View
57 junit/src/main/java/cucumber/runtime/NullReporter.java
@@ -0,0 +1,57 @@
+package cucumber.runtime;
+
+import gherkin.formatter.Formatter;
+import gherkin.formatter.Reporter;
+import gherkin.formatter.model.*;
+
+import java.util.List;
+
+public class NullReporter implements Reporter, Formatter {
+ @Override
+ public void uri(String s) {
+ }
+
+ @Override
+ public void feature(Feature feature) {
+ }
+
+ @Override
+ public void background(Background background) {
+ }
+
+ @Override
+ public void scenario(Scenario scenario) {
+ }
+
+ @Override
+ public void scenarioOutline(ScenarioOutline scenarioOutline) {
+ }
+
+ @Override
+ public void examples(Examples examples) {
+ }
+
+ @Override
+ public void step(Step step) {
+ }
+
+ @Override
+ public void eof() {
+ }
+
+ @Override
+ public void syntaxError(String s, String s1, List<String> strings, String s2, int i) {
+ }
+
+ @Override
+ public void result(Result result) {
+ }
+
+ @Override
+ public void match(Match match) {
+ }
+
+ @Override
+ public void embedding(String s, byte[] bytes) {
+ }
+}
View
5 jython/pom.xml
@@ -25,6 +25,11 @@
<artifactId>jython-standalone</artifactId>
</dependency>
<dependency>
+ <groupId>info.cukes</groupId>
+ <artifactId>cucumber-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
View
53 jython/src/main/java/cucumber/runtime/jython/JythonBackend.java
@@ -4,56 +4,39 @@
import cucumber.resources.Resource;
import cucumber.resources.Resources;
import cucumber.runtime.Backend;
-import cucumber.runtime.HookDefinition;
-import cucumber.runtime.StepDefinition;
+import cucumber.runtime.World;
import gherkin.formatter.model.Step;
import org.python.core.PyInstance;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.util.PythonInterpreter;
-import java.util.ArrayList;
import java.util.List;
-import static java.util.Collections.emptyList;
-
public class JythonBackend implements Backend {
private static final String DSL = "/cucumber/runtime/jython/dsl.py";
private final PythonInterpreter jython = new PythonInterpreter();
- private final List<StepDefinition> stepDefinitions = new ArrayList<StepDefinition>();
- private PyObject world;
-
- public JythonBackend(List<String> scriptPaths) {
- defineStepDefinitions(scriptPaths);
- }
+ private PyObject pyWorld;
+ private World world;
- private void defineStepDefinitions(List<String> scriptPaths) {
+ public JythonBackend() {
jython.set("backend", this);
jython.execfile(getClass().getResourceAsStream(DSL), DSL);
+ }
- for (String scriptPath : scriptPaths) {
- Resources.scan(scriptPath.replace('.', '/'), ".py", new Consumer() {
+ @Override
+ public void buildWorld(List<String> codePaths, World world) {
+ this.pyWorld = jython.eval("World()");
+ this.world = world;
+ for (String codePath : codePaths) {
+ Resources.scan(codePath.replace('.', '/'), ".py", new Consumer() {
public void consume(Resource resource) {
jython.execfile(resource.getInputStream(), resource.getPath());
}
});
}
}
- public void registerStepdef(PyInstance stepdef, int arity) {
- stepDefinitions.add(new JythonStepDefinition(this, stepdef, arity));
- }
-
- @Override
- public List<StepDefinition> getStepDefinitions() {
- return stepDefinitions;
- }
-
- @Override
- public void newWorld() {
- world = jython.eval("World()");
- }
-
@Override
public void disposeWorld() {
}
@@ -63,21 +46,15 @@ public String getSnippet(Step step) {
return new JythonSnippetGenerator(step).getSnippet();
}
- @Override
- public List<HookDefinition> getBeforeHooks() {
- return emptyList();
- }
-
- @Override
- public List<HookDefinition> getAfterHooks() {
- return emptyList();
+ public void registerStepdef(PyInstance stepdef, int arity) {
+ world.addStepDefinition(new JythonStepDefinition(this, stepdef, arity));
}
public void execute(PyInstance stepdef, Object[] args) {
PyObject[] pyArgs = new PyObject[args.length + 1];
- pyArgs[0] = world;
+ pyArgs[0] = pyWorld;
for (int i = 0; i < args.length; i++) {
- pyArgs[i+1] = new PyString((String) args[i]);
+ pyArgs[i + 1] = new PyString((String) args[i]);
}
stepdef.invoke("execute", pyArgs);
}
View
7 jython/src/main/java/cucumber/runtime/jython/JythonStepDefinition.java
@@ -4,7 +4,10 @@
import cucumber.runtime.Utils;
import gherkin.formatter.Argument;
import gherkin.formatter.model.Step;
-import org.python.core.*;
+import org.python.core.PyInstance;
+import org.python.core.PyList;
+import org.python.core.PyObject;
+import org.python.core.PyString;
import java.util.List;
@@ -23,7 +26,7 @@ public JythonStepDefinition(JythonBackend jythonBackend, PyInstance stepdef, int
public List<Argument> matchedArguments(Step step) {
PyObject stepName = new PyString(step.getName());
PyObject matched_arguments = stepdef.invoke("matched_arguments", stepName);
- if(matched_arguments instanceof PyList) {
+ if (matched_arguments instanceof PyList) {
return (PyList) matched_arguments;
} else {
return null;
View
1 picocontainer/picocontainer.iml
@@ -27,6 +27,7 @@
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.8.2" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.java-diff-utils:diffutils:1.2.1" level="project" />
<orderEntry type="library" name="Maven: org.picocontainer:picocontainer:2.13.6" level="project" />
+ <orderEntry type="module" module-name="junit" scope="TEST" />
</component>
</module>
View
5 picocontainer/pom.xml
@@ -25,6 +25,11 @@
<artifactId>picocontainer</artifactId>
</dependency>
<dependency>
+ <groupId>info.cukes</groupId>
+ <artifactId>cucumber-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
View
19 pom.xml
@@ -50,6 +50,16 @@
</dependency>
<dependency>
<groupId>info.cukes</groupId>
+ <artifactId>cucumber-junit</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>info.cukes</groupId>
+ <artifactId>cucumber-picocontainer</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>info.cukes</groupId>
<artifactId>gherkin</artifactId>
<version>2.4.18</version>
</dependency>
@@ -203,19 +213,20 @@
</repositories>
<modules>
- <module>jython</module>
<module>core</module>
+ <module>junit</module>
+ <module>picocontainer</module>
<module>spring</module>
<module>guice</module>
- <module>picocontainer</module>
<module>weld</module>
<module>groovy</module>
<module>ioke</module>
<module>rhino</module>
<module>java</module>
<module>clojure</module>
<module>jruby</module>
- <module>scala</module>
+ <module>jython</module>
+ <!--module>scala</module-->
</modules>
<profiles>
@@ -230,7 +241,7 @@
<id>examples</id>
<modules>
<module>examples/java-calculator</module>
- <module>examples/scala-calculator</module>
+ <!--module>examples/scala-calculator</module-->
<module>examples/java-webbit-websockets-selenium</module>
</modules>
</profile>
View
58 rhino/src/main/java/cucumber/runtime/rhino/RhinoBackend.java
@@ -5,8 +5,7 @@
import cucumber.resources.Resources;
import cucumber.runtime.Backend;
import cucumber.runtime.CucumberException;
-import cucumber.runtime.HookDefinition;
-import cucumber.runtime.StepDefinition;
+import cucumber.runtime.World;
import cucumber.runtime.javascript.JavascriptSnippetGenerator;
import gherkin.formatter.model.Step;
import org.mozilla.javascript.Context;
@@ -17,34 +16,32 @@
import java.io.IOException;
import java.io.InputStreamReader;
-import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
public class RhinoBackend implements Backend {
private static final String JS_DSL = "/cucumber/runtime/rhino/dsl.js";
- private final List<StepDefinition> stepDefinitions = new ArrayList<StepDefinition>();
- private Context cx;
- private Scriptable scope;
- private final