diff --git a/android/pom.xml b/android/pom.xml
index b3535b1aef..024244d906 100644
--- a/android/pom.xml
+++ b/android/pom.xml
@@ -2,10 +2,10 @@
4.0.0
- info.cukes
+ io.cucumber
cucumber-jvm
../pom.xml
- 1.2.6-SNAPSHOT
+ 2.0.0-SNAPSHOT
cucumber-android
@@ -30,7 +30,7 @@
provided
- info.cukes
+ io.cucumber
cucumber-java
diff --git a/android/src/main/java/cucumber/runtime/android/AndroidInstrumentationReporter.java b/android/src/main/java/cucumber/runtime/android/AndroidInstrumentationReporter.java
index f6341daa95..01a8ccf676 100644
--- a/android/src/main/java/cucumber/runtime/android/AndroidInstrumentationReporter.java
+++ b/android/src/main/java/cucumber/runtime/android/AndroidInstrumentationReporter.java
@@ -2,11 +2,15 @@
import android.app.Instrumentation;
import android.os.Bundle;
+import cucumber.api.Result;
+import cucumber.api.TestCase;
+import cucumber.api.event.EventHandler;
+import cucumber.api.event.EventPublisher;
+import cucumber.api.event.TestCaseFinished;
+import cucumber.api.event.TestCaseStarted;
+import cucumber.api.event.TestStepFinished;
+import cucumber.api.formatter.Formatter;
import cucumber.runtime.Runtime;
-import gherkin.formatter.model.Feature;
-import gherkin.formatter.model.Match;
-import gherkin.formatter.model.Result;
-import gherkin.formatter.model.Scenario;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -30,7 +34,7 @@
* hook threw an exception other than an {@link AssertionError}
*
*/
-public class AndroidInstrumentationReporter extends NoOpFormattingReporter {
+public class AndroidInstrumentationReporter implements Formatter {
/**
* Tests status keys.
@@ -74,9 +78,44 @@ public static class StatusCodes {
private Result severestResult;
/**
- * The feature of the current test execution.
+ * The location in the feature file of the current test case.
*/
- private Feature currentFeature;
+ private String currentPath;
+
+ /**
+ * The name of the current test case.
+ */
+ private String currentTestCaseName;
+
+ /**
+ * The event handler for the {@link TestCaseStarted} events.
+ */
+ private final EventHandler testCaseStartedHandler = new EventHandler() {
+ @Override
+ public void receive(TestCaseStarted event) {
+ startTestCase(event.testCase);
+ }
+ };
+
+ /**
+ * The event handler for the {@link TestStepFinished} events.
+ */
+ private final EventHandler testStepFinishedHandler = new EventHandler() {
+ @Override
+ public void receive(TestStepFinished event) {
+ finishTestStep(event.result);
+ }
+ };
+
+ /**
+ * The event handler for the {@link TestCaseFinished} events.
+ */
+ private final EventHandler testCaseFinishedHandler = new EventHandler() {
+ @Override
+ public void receive(TestCaseFinished event) {
+ finishTestCase();
+ }
+ };
/**
* Creates a new instance for the given parameters
@@ -96,36 +135,26 @@ public AndroidInstrumentationReporter(
}
@Override
- public void feature(final Feature feature) {
- currentFeature = feature;
+ public void setEventPublisher(final EventPublisher publisher) {
+ publisher.registerHandlerFor(TestCaseStarted.class, testCaseStartedHandler);
+ publisher.registerHandlerFor(TestCaseFinished.class, testCaseFinishedHandler);
+ publisher.registerHandlerFor(TestStepFinished.class, testStepFinishedHandler);
}
- @Override
- public void startOfScenarioLifeCycle(final Scenario scenario) {
+ void startTestCase(final TestCase testCase) {
+ currentPath = testCase.getPath();
+ currentTestCaseName = testCase.getName();
resetSeverestResult();
- final Bundle testStart = createBundle(currentFeature, scenario);
+ final Bundle testStart = createBundle(currentPath, currentTestCaseName);
instrumentation.sendStatus(StatusCodes.START, testStart);
}
- @Override
- public void before(final Match match, final Result result) {
- checkAndSetSeverestStepResult(result);
- }
-
- @Override
- public void result(final Result result) {
+ void finishTestStep(final Result result) {
checkAndSetSeverestStepResult(result);
}
- @Override
- public void after(final Match match, final Result result) {
- checkAndSetSeverestStepResult(result);
- }
-
- @Override
- public void endOfScenarioLifeCycle(final Scenario scenario) {
-
- final Bundle testResult = createBundle(currentFeature, scenario);
+ void finishTestCase() {
+ final Bundle testResult = createBundle(currentPath, currentTestCaseName);
if (severestResult.getStatus().equals(Result.FAILED)) {
@@ -149,7 +178,7 @@ public void endOfScenarioLifeCycle(final Scenario scenario) {
return;
}
- if (severestResult.getStatus().equals(Result.UNDEFINED.getStatus())) {
+ if (severestResult.getStatus().equals(Result.UNDEFINED)) {
testResult.putString(StatusKeys.STACK, getStackTrace(new MissingStepDefinitionError(getLastSnippet())));
instrumentation.sendStatus(StatusCodes.ERROR, testResult);
return;
@@ -161,15 +190,15 @@ public void endOfScenarioLifeCycle(final Scenario scenario) {
/**
* Creates a template bundle for reporting the start and end of a test.
*
- * @param feature the {@link Feature} of the current execution
- * @param scenario the {@link Scenario} of the current execution
+ * @param path of the feature file of the current execution
+ * @param name of the test case of the current execution
* @return the new {@link Bundle}
*/
- private Bundle createBundle(final Feature feature, final Scenario scenario) {
+ private Bundle createBundle(final String path, final String testCaseName) {
final Bundle bundle = new Bundle();
bundle.putInt(StatusKeys.NUMTESTS, numberOfTests);
- bundle.putString(StatusKeys.CLASS, String.format("%s %s", feature.getKeyword(), feature.getName()));
- bundle.putString(StatusKeys.TEST, String.format("%s %s", scenario.getKeyword(), scenario.getName()));
+ bundle.putString(StatusKeys.CLASS, String.format("%s", path));
+ bundle.putString(StatusKeys.TEST, String.format("%s", testCaseName));
return bundle;
}
diff --git a/android/src/main/java/cucumber/runtime/android/AndroidLogcatReporter.java b/android/src/main/java/cucumber/runtime/android/AndroidLogcatReporter.java
index 9a6dd64a38..0febe8fe08 100644
--- a/android/src/main/java/cucumber/runtime/android/AndroidLogcatReporter.java
+++ b/android/src/main/java/cucumber/runtime/android/AndroidLogcatReporter.java
@@ -1,19 +1,18 @@
package cucumber.runtime.android;
import android.util.Log;
+import cucumber.api.event.EventHandler;
+import cucumber.api.event.EventPublisher;
+import cucumber.api.event.TestCaseStarted;
+import cucumber.api.event.TestRunFinished;
+import cucumber.api.event.TestStepStarted;
+import cucumber.api.formatter.Formatter;
import cucumber.runtime.Runtime;
-import gherkin.formatter.model.Background;
-import gherkin.formatter.model.Examples;
-import gherkin.formatter.model.Feature;
-import gherkin.formatter.model.Scenario;
-import gherkin.formatter.model.ScenarioOutline;
-import gherkin.formatter.model.Step;
-import java.util.List;
/**
* Logs information about the currently executed statements to androids logcat.
*/
-public class AndroidLogcatReporter extends NoOpFormattingReporter {
+public class AndroidLogcatReporter implements Formatter {
/**
* The {@link cucumber.runtime.Runtime} to get the errors and snippets from for writing them to the logcat at the end of the execution.
@@ -26,9 +25,43 @@ public class AndroidLogcatReporter extends NoOpFormattingReporter {
private final String logTag;
/**
- * Holds the feature's uri.
+ * The event handler that logs the {@link TestCaseStarted} events.
*/
- private String uri;
+ private final EventHandler testCaseStartedHandler = new EventHandler() {
+ @Override
+ public void receive(TestCaseStarted event) {
+ Log.d(logTag, String.format("%s", event.testCase.getName()));
+ }
+ };
+
+ /**
+ * The event handler that logs the {@link TestStepStarted} events.
+ */
+ private final EventHandler testStepStartedHandler = new EventHandler() {
+ @Override
+ public void receive(TestStepStarted event) {
+ if (!event.testStep.isHook()) {
+ Log.d(logTag, String.format("%s", event.testStep.getStepText()));
+ }
+ }
+ };
+
+ /**
+ * The event handler that logs the {@link TestRunFinished} events.
+ */
+ private EventHandler runFinishHandler = new EventHandler() {
+
+ @Override
+ public void receive(TestRunFinished event) {
+ for (final Throwable throwable : runtime.getErrors()) {
+ Log.e(logTag, throwable.toString());
+ }
+
+ for (final String snippet : runtime.getSnippets()) {
+ Log.w(logTag, snippet);
+ }
+ }
+ };
/**
* Creates a new instance for the given parameters.
@@ -42,53 +75,9 @@ public AndroidLogcatReporter(final Runtime runtime, final String logTag) {
}
@Override
- public void uri(final String uri) {
- this.uri = uri;
- }
-
- @Override
- public void feature(final Feature feature) {
- Log.d(logTag, String.format("%s: %s (%s)%n%s", feature.getKeyword(), feature.getName(), uri, feature.getDescription()));
- }
-
- @Override
- public void background(final Background background) {
- Log.d(logTag, background.getName());
- }
-
- @Override
- public void scenario(final Scenario scenario) {
- Log.d(logTag, String.format("%s: %s", scenario.getKeyword(), scenario.getName()));
- }
-
- @Override
- public void scenarioOutline(final ScenarioOutline scenarioOutline) {
- Log.d(logTag, String.format("%s: %s", scenarioOutline.getKeyword(), scenarioOutline.getName()));
- }
-
- @Override
- public void examples(final Examples examples) {
- Log.d(logTag, String.format("%s: %s", examples.getKeyword(), examples.getName()));
- }
-
- @Override
- public void step(final Step step) {
- Log.d(logTag, String.format("%s%s", step.getKeyword(), step.getName()));
- }
-
- @Override
- public void syntaxError(final String state, final String event, final List legalEvents, final String uri, final Integer line) {
- Log.e(logTag, String.format("syntax error '%s' %s:%d", event, uri, line));
- }
-
- @Override
- public void done() {
- for (final Throwable throwable : runtime.getErrors()) {
- Log.e(logTag, throwable.toString());
- }
-
- for (final String snippet : runtime.getSnippets()) {
- Log.w(logTag, snippet);
- }
+ public void setEventPublisher(final EventPublisher publisher) {
+ publisher.registerHandlerFor(TestCaseStarted.class, testCaseStartedHandler);
+ publisher.registerHandlerFor(TestStepStarted.class, testStepStartedHandler);
+ publisher.registerHandlerFor(TestRunFinished.class, runFinishHandler);
}
}
diff --git a/android/src/main/java/cucumber/runtime/android/CucumberExecutor.java b/android/src/main/java/cucumber/runtime/android/CucumberExecutor.java
index 345e9dd2ba..580b4acea9 100644
--- a/android/src/main/java/cucumber/runtime/android/CucumberExecutor.java
+++ b/android/src/main/java/cucumber/runtime/android/CucumberExecutor.java
@@ -5,6 +5,9 @@
import android.util.Log;
import cucumber.api.CucumberOptions;
import cucumber.api.StepDefinitionReporter;
+import cucumber.api.event.TestRunFinished;
+import cucumber.api.formatter.Formatter;
+import cucumber.api.java.ObjectFactory;
import cucumber.runtime.Backend;
import cucumber.runtime.ClassFinder;
import cucumber.runtime.CucumberException;
@@ -14,12 +17,10 @@
import cucumber.runtime.RuntimeOptionsFactory;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.java.JavaBackend;
-import cucumber.api.java.ObjectFactory;
import cucumber.runtime.java.ObjectFactoryLoader;
import cucumber.runtime.model.CucumberFeature;
import dalvik.system.DexFile;
-import gherkin.formatter.Formatter;
-import gherkin.formatter.Reporter;
+import gherkin.events.PickleEvent;
import java.io.IOException;
import java.util.ArrayList;
@@ -67,9 +68,9 @@ public class CucumberExecutor {
private final Runtime runtime;
/**
- * The actual {@link CucumberFeature}s to run.
+ * The actual {@link PickleEvent}s to run stored in {@link PickleStruct}s.
*/
- private final List cucumberFeatures;
+ private final List pickleEvents;
/**
* Creates a new instance for the given parameters.
@@ -89,7 +90,8 @@ public CucumberExecutor(final Arguments arguments, final Instrumentation instrum
ResourceLoader resourceLoader = new AndroidResourceLoader(context);
this.runtime = new Runtime(resourceLoader, classLoader, createBackends(), runtimeOptions);
- this.cucumberFeatures = runtimeOptions.cucumberFeatures(resourceLoader);
+ List cucumberFeatures = runtimeOptions.cucumberFeatures(resourceLoader, runtime.getEventBus());
+ this.pickleEvents = FeatureCompiler.compile(cucumberFeatures, this.runtime);
}
/**
@@ -102,25 +104,23 @@ public void execute() {
// TODO: This is duplicated in info.cucumber.Runtime.
- final Reporter reporter = runtimeOptions.reporter(classLoader);
final Formatter formatter = runtimeOptions.formatter(classLoader);
final StepDefinitionReporter stepDefinitionReporter = runtimeOptions.stepDefinitionReporter(classLoader);
- runtime.getGlue().reportStepDefinitions(stepDefinitionReporter);
+ runtime.reportStepDefinitions(stepDefinitionReporter);
- for (final CucumberFeature cucumberFeature : cucumberFeatures) {
- cucumberFeature.run(formatter, reporter, runtime);
+ for (final PickleEvent pickleEvent : pickleEvents) {
+ runtime.getRunner().runPickle(pickleEvent);
}
- formatter.done();
- formatter.close();
+ runtime.getEventBus().send(new TestRunFinished(runtime.getEventBus().getTime()));
}
/**
* @return the number of actual scenarios, including outlined
*/
public int getNumberOfConcreteScenarios() {
- return ScenarioCounter.countScenarios(cucumberFeatures);
+ return pickleEvents.size();
}
private void trySetCucumberOptionsToSystemProperties(final Arguments arguments) {
diff --git a/android/src/main/java/cucumber/runtime/android/FeatureCompiler.java b/android/src/main/java/cucumber/runtime/android/FeatureCompiler.java
new file mode 100644
index 0000000000..1782e5e9da
--- /dev/null
+++ b/android/src/main/java/cucumber/runtime/android/FeatureCompiler.java
@@ -0,0 +1,37 @@
+package cucumber.runtime.android;
+
+import cucumber.runtime.Runtime;
+import cucumber.runtime.model.CucumberFeature;
+import gherkin.events.PickleEvent;
+import gherkin.pickles.Compiler;
+import gherkin.pickles.Pickle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class to count scenarios, including outlined.
+ */
+public class FeatureCompiler {
+
+ /**
+ * Compilers the given {@code cucumberFeatures} to {@link Pickle}s.
+ *
+ * @param cucumberFeatures the list of {@link CucumberFeature} to compile
+ * @return the compiled pickles in {@link PickleStruct}s
+ */
+ public static List compile(final List cucumberFeatures, final Runtime runtime) {
+ List pickles = new ArrayList();
+ Compiler compiler = new Compiler();
+ for (final CucumberFeature feature : cucumberFeatures) {
+ for (final Pickle pickle : compiler.compile(feature.getGherkinFeature())) {
+ final PickleEvent pickleEvent = new PickleEvent(feature.getPath(), pickle);
+ if (runtime.matchesFilters(pickleEvent)) {
+ pickles.add(pickleEvent);
+ }
+ }
+ }
+ return pickles;
+ }
+
+}
diff --git a/android/src/main/java/cucumber/runtime/android/NoOpFormattingReporter.java b/android/src/main/java/cucumber/runtime/android/NoOpFormattingReporter.java
deleted file mode 100644
index 13e761d8da..0000000000
--- a/android/src/main/java/cucumber/runtime/android/NoOpFormattingReporter.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package cucumber.runtime.android;
-
-import gherkin.formatter.Formatter;
-import gherkin.formatter.Reporter;
-import gherkin.formatter.model.Background;
-import gherkin.formatter.model.Examples;
-import gherkin.formatter.model.Feature;
-import gherkin.formatter.model.Match;
-import gherkin.formatter.model.Result;
-import gherkin.formatter.model.Scenario;
-import gherkin.formatter.model.ScenarioOutline;
-import gherkin.formatter.model.Step;
-
-import java.util.List;
-
-/**
- * A "no operation" abstract implementation of the {@link Formatter} and {@link Reporter}
- * interface to ease overriding only specific methods.
- */
-abstract class NoOpFormattingReporter implements Formatter, Reporter {
-
- @Override
- public void uri(String uri) {
- // NoOp
- }
-
- @Override
- public void feature(Feature feature) {
- // NoOp
- }
-
- @Override
- public void background(Background background) {
- // NoOp
- }
-
- @Override
- public void scenario(Scenario scenario) {
- // NoOp
- }
-
- @Override
- public void scenarioOutline(ScenarioOutline scenarioOutline) {
- // NoOp
- }
-
- @Override
- public void examples(Examples examples) {
- // NoOp
- }
-
- @Override
- public void step(Step step) {
- // NoOp
- }
-
- @Override
- public void eof() {
- // NoOp
- }
-
- @Override
- public void syntaxError(String state, String event, List legalEvents, String uri, Integer line) {
- // NoOp
- }
-
- @Override
- public void done() {
- // NoOp
- }
-
- @Override
- public void close() {
- // NoOp
- }
-
- @Override
- public void startOfScenarioLifeCycle(Scenario scenario) {
- // NoOp
- }
-
- @Override
- public void endOfScenarioLifeCycle(Scenario scenario) {
- // NoOp
- }
-
- @Override
- public void before(Match match, Result result) {
- // NoOp
- }
-
- @Override
- public void result(Result result) {
- // NoOp
- }
-
- @Override
- public void after(Match match, Result result) {
- // NoOp
- }
-
- @Override
- public void match(Match match) {
- // NoOp
- }
-
- @Override
- public void embedding(String mimeType, byte[] data) {
- // NoOp
- }
-
- @Override
- public void write(String text) {
- // NoOp
- }
-}
diff --git a/android/src/main/java/cucumber/runtime/android/ScenarioCounter.java b/android/src/main/java/cucumber/runtime/android/ScenarioCounter.java
deleted file mode 100644
index 0c00deabd4..0000000000
--- a/android/src/main/java/cucumber/runtime/android/ScenarioCounter.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package cucumber.runtime.android;
-
-import cucumber.runtime.model.CucumberExamples;
-import cucumber.runtime.model.CucumberFeature;
-import cucumber.runtime.model.CucumberScenario;
-import cucumber.runtime.model.CucumberScenarioOutline;
-import cucumber.runtime.model.CucumberTagStatement;
-
-import java.util.List;
-
-/**
- * Utility class to count scenarios, including outlined.
- */
-public final class ScenarioCounter {
-
- private ScenarioCounter() {
- // disallow public instantiation
- }
-
- /**
- * Counts the number of test cases for the given {@code cucumberFeatures}.
- *
- * @param cucumberFeatures the list of {@link CucumberFeature} to count the test cases for
- * @return the number of test cases
- */
- public static int countScenarios(final List cucumberFeatures) {
- int numberOfTestCases = 0;
- for (final CucumberFeature cucumberFeature : cucumberFeatures) {
- for (final CucumberTagStatement cucumberTagStatement : cucumberFeature.getFeatureElements()) {
- if (cucumberTagStatement instanceof CucumberScenario) {
- numberOfTestCases++;
- } else if (cucumberTagStatement instanceof CucumberScenarioOutline) {
- for (final CucumberExamples cucumberExamples : ((CucumberScenarioOutline) cucumberTagStatement).getCucumberExamplesList()) {
- final int numberOfRows = cucumberExamples.getExamples().getRows().size();
- final int numberOfRowsExcludingHeader = numberOfRows - 1;
- numberOfTestCases += numberOfRowsExcludingHeader;
- }
- }
- }
- }
- return numberOfTestCases;
- }
-}
diff --git a/android/src/test/java/cucumber/runtime/android/AndroidInstrumentationReporterTest.java b/android/src/test/java/cucumber/runtime/android/AndroidInstrumentationReporterTest.java
index ac7247cbce..9362e5990b 100644
--- a/android/src/test/java/cucumber/runtime/android/AndroidInstrumentationReporterTest.java
+++ b/android/src/test/java/cucumber/runtime/android/AndroidInstrumentationReporterTest.java
@@ -2,12 +2,10 @@
import android.app.Instrumentation;
import android.os.Bundle;
+import cucumber.api.Result;
+import cucumber.api.TestCase;
import cucumber.runtime.Runtime;
import edu.emory.mathcs.backport.java.util.Collections;
-import gherkin.formatter.model.Feature;
-import gherkin.formatter.model.Match;
-import gherkin.formatter.model.Result;
-import gherkin.formatter.model.Scenario;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -37,19 +35,15 @@ public class AndroidInstrumentationReporterTest {
private final Runtime runtime = mock(Runtime.class);
private final Instrumentation instrumentation = mock(Instrumentation.class);
- private final Feature feature = mock(Feature.class);
- private final Scenario scenario = mock(Scenario.class);
- private final Match match = mock(Match.class);
+ private final TestCase testCase = mock(TestCase.class);
private final Result firstResult = mock(Result.class);
private final Result secondResult = mock(Result.class);
@Before
public void beforeEachTest() {
- when(feature.getKeyword()).thenReturn("Feature");
- when(feature.getName()).thenReturn("Some important feature");
- when(scenario.getKeyword()).thenReturn("Scenario");
- when(scenario.getName()).thenReturn("Some important scenario");
+ when(testCase.getPath()).thenReturn("path/file.feature");
+ when(testCase.getName()).thenReturn("Some important scenario");
}
@Test
@@ -59,8 +53,7 @@ public void feature_name_and_keyword_is_contained_in_start_signal() {
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
// when
- formatter.feature(feature);
- formatter.startOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -69,8 +62,7 @@ public void feature_name_and_keyword_is_contained_in_start_signal() {
final Bundle actualBundle = captor.getValue();
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.CLASS), containsString(feature.getKeyword()));
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.CLASS), containsString(feature.getName()));
+ assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.CLASS), containsString(testCase.getPath()));
}
@Test
@@ -81,9 +73,9 @@ public void feature_name_and_keyword_is_contained_in_end_signal() {
when(firstResult.getStatus()).thenReturn(Result.PASSED);
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestCase();
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -92,19 +84,17 @@ public void feature_name_and_keyword_is_contained_in_end_signal() {
final Bundle actualBundle = captor.getValue();
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.CLASS), containsString(feature.getKeyword()));
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.CLASS), containsString(feature.getName()));
+ assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.CLASS), containsString(testCase.getPath()));
}
@Test
- public void scenario_name_and_keyword_is_contained_in_start_signal() {
+ public void scenario_name_is_contained_in_start_signal() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
// when
- formatter.feature(feature);
- formatter.startOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -113,21 +103,20 @@ public void scenario_name_and_keyword_is_contained_in_start_signal() {
final Bundle actualBundle = captor.getValue();
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.TEST), containsString(scenario.getKeyword()));
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.TEST), containsString(scenario.getName()));
+ assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.TEST), containsString(testCase.getName()));
}
@Test
- public void scenario_name_and_keyword_is_contained_in_end_signal() {
+ public void scenario_name_is_contained_in_end_signal() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
when(firstResult.getStatus()).thenReturn(Result.PASSED);
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestCase();
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -136,29 +125,7 @@ public void scenario_name_and_keyword_is_contained_in_end_signal() {
final Bundle actualBundle = captor.getValue();
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.TEST), containsString(scenario.getKeyword()));
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.TEST), containsString(scenario.getName()));
- }
-
- @Test
- public void any_before_hook_exception_causes_test_error() {
-
- // given
- final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
- when(firstResult.getStatus()).thenReturn(Result.FAILED);
- when(firstResult.getError()).thenReturn(new RuntimeException("some random runtime exception"));
-
- // when
- formatter.feature(feature);
- formatter.before(match, firstResult);
- formatter.endOfScenarioLifeCycle(scenario);
-
- // then
- final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
- verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
-
- final Bundle actualBundle = captor.getValue();
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some random runtime exception"));
+ assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.TEST), containsString(testCase.getName()));
}
@Test
@@ -170,9 +137,9 @@ public void any_step_exception_causes_test_error() {
when(firstResult.getError()).thenReturn(new RuntimeException("some random runtime exception"));
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestCase();
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -183,27 +150,6 @@ public void any_step_exception_causes_test_error() {
}
- @Test
- public void any_after_hook_exception_causes_test_error() {
-
- // given
- final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
- when(firstResult.getStatus()).thenReturn(Result.FAILED);
- when(firstResult.getError()).thenReturn(new RuntimeException("some random runtime exception"));
-
- // when
- formatter.feature(feature);
- formatter.after(match, firstResult);
- formatter.endOfScenarioLifeCycle(scenario);
-
- // then
- final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
- verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
-
- final Bundle actualBundle = captor.getValue();
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some random runtime exception"));
- }
-
@Test
public void any_failing_step_causes_test_failure() {
@@ -214,9 +160,9 @@ public void any_failing_step_causes_test_failure() {
when(firstResult.getErrorMessage()).thenReturn("some test assertion went wrong");
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestCase();
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -231,13 +177,13 @@ public void any_undefined_step_causes_test_error() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
- when(firstResult.getStatus()).thenReturn(Result.UNDEFINED.getStatus());
+ when(firstResult.getStatus()).thenReturn(Result.UNDEFINED);
when(runtime.getSnippets()).thenReturn(Collections.singletonList("some snippet"));
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestCase();
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -255,9 +201,9 @@ public void passing_step_causes_test_success() {
when(firstResult.getStatus()).thenReturn(Result.PASSED);
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestCase();
// then
verify(instrumentation).sendStatus(eq(StatusCodes.OK), any(Bundle.class));
@@ -272,41 +218,16 @@ public void skipped_step_causes_test_success() {
when(secondResult.getStatus()).thenReturn(Result.SKIPPED.getStatus());
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.result(secondResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestStep(secondResult);
+ formatter.finishTestCase();
// then
verify(instrumentation).sendStatus(eq(StatusCodes.OK), any(Bundle.class));
}
- @Test
- public void first_before_exception_is_reported() {
-
- // given
- final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
- when(firstResult.getStatus()).thenReturn(Result.FAILED);
- when(firstResult.getError()).thenReturn(new RuntimeException("first exception"));
-
- when(secondResult.getStatus()).thenReturn(Result.FAILED);
- when(secondResult.getError()).thenReturn(new RuntimeException("second exception"));
-
- // when
- formatter.feature(feature);
- formatter.before(match, firstResult);
- formatter.before(match, secondResult);
- formatter.endOfScenarioLifeCycle(scenario);
-
- // then
- final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
- verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
-
- final Bundle actualBundle = captor.getValue();
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("first exception"));
- }
-
@Test
public void first_step_result_exception_is_reported() {
@@ -319,35 +240,10 @@ public void first_step_result_exception_is_reported() {
when(secondResult.getError()).thenReturn(new RuntimeException("second exception"));
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.result(secondResult);
- formatter.endOfScenarioLifeCycle(scenario);
-
- // then
- final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
- verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
-
- final Bundle actualBundle = captor.getValue();
- assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("first exception"));
- }
-
- @Test
- public void first_after_exception_is_reported() {
-
- // given
- final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
- when(firstResult.getStatus()).thenReturn(Result.FAILED);
- when(firstResult.getError()).thenReturn(new RuntimeException("first exception"));
-
- when(secondResult.getStatus()).thenReturn(Result.FAILED);
- when(secondResult.getError()).thenReturn(new RuntimeException("second exception"));
-
- // when
- formatter.feature(feature);
- formatter.after(match, firstResult);
- formatter.after(match, secondResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestStep(secondResult);
+ formatter.finishTestCase();
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -364,14 +260,14 @@ public void undefined_step_overrides_preceding_passed_step() {
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn(Result.PASSED);
- when(secondResult.getStatus()).thenReturn(Result.UNDEFINED.getStatus());
+ when(secondResult.getStatus()).thenReturn(Result.UNDEFINED);
when(runtime.getSnippets()).thenReturn(Collections.singletonList("some snippet"));
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.result(secondResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestStep(secondResult);
+ formatter.finishTestCase();
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -393,10 +289,10 @@ public void failed_step_overrides_preceding_passed_step() {
when(secondResult.getErrorMessage()).thenReturn("some assertion went wrong");
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.result(secondResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestStep(secondResult);
+ formatter.finishTestCase();
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -417,10 +313,10 @@ public void error_step_overrides_preceding_passed_step() {
when(secondResult.getError()).thenReturn(new RuntimeException("some exception"));
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.result(secondResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestStep(secondResult);
+ formatter.finishTestCase();
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -435,7 +331,7 @@ public void failed_step_does_not_overrides_preceding_undefined_step() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
- when(firstResult.getStatus()).thenReturn(Result.UNDEFINED.getStatus());
+ when(firstResult.getStatus()).thenReturn(Result.UNDEFINED);
when(runtime.getSnippets()).thenReturn(Collections.singletonList("some snippet"));
when(secondResult.getStatus()).thenReturn(Result.FAILED);
@@ -443,10 +339,10 @@ public void failed_step_does_not_overrides_preceding_undefined_step() {
when(secondResult.getErrorMessage()).thenReturn("some assertion went wrong");
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.result(secondResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestStep(secondResult);
+ formatter.finishTestCase();
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -469,10 +365,10 @@ public void error_step_does_not_override_preceding_failed_step() {
when(secondResult.getError()).thenReturn(new RuntimeException("some exception"));
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.result(secondResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestStep(secondResult);
+ formatter.finishTestCase();
// then
final ArgumentCaptor captor = ArgumentCaptor.forClass(Bundle.class);
@@ -493,9 +389,9 @@ public void unexpected_status_code_causes_IllegalStateException() {
expectedException.expectMessage(containsString("foobar"));
// when
- formatter.feature(feature);
- formatter.result(firstResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestCase();
}
@Test
@@ -509,14 +405,13 @@ public void step_result_contains_only_the_current_scenarios_severest_result() {
when(secondResult.getStatus()).thenReturn(Result.PASSED);
// when
- formatter.feature(feature);
- formatter.startOfScenarioLifeCycle(scenario);
- formatter.result(firstResult);
- formatter.endOfScenarioLifeCycle(scenario);
-
- formatter.startOfScenarioLifeCycle(scenario);
- formatter.result(secondResult);
- formatter.endOfScenarioLifeCycle(scenario);
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(firstResult);
+ formatter.finishTestCase();
+
+ formatter.startTestCase(testCase);
+ formatter.finishTestStep(secondResult);
+ formatter.finishTestCase();
// then
diff --git a/android/src/test/java/cucumber/runtime/android/ScenarioCounterTest.java b/android/src/test/java/cucumber/runtime/android/ScenarioCounterTest.java
deleted file mode 100644
index a7c880fbca..0000000000
--- a/android/src/test/java/cucumber/runtime/android/ScenarioCounterTest.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package cucumber.runtime.android;
-
-import cucumber.runtime.model.CucumberExamples;
-import cucumber.runtime.model.CucumberFeature;
-import cucumber.runtime.model.CucumberScenario;
-import cucumber.runtime.model.CucumberScenarioOutline;
-import cucumber.runtime.model.CucumberTagStatement;
-import gherkin.formatter.model.Examples;
-import gherkin.formatter.model.ExamplesTableRow;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class ScenarioCounterTest {
-
- @Test
- public void calculates_number_of_tests_for_regular_scenarios() {
-
- // given
- final List cucumberFeatures = createCucumberFeaturesWithScenarios(1, 2);
-
- // when
- final int result = ScenarioCounter.countScenarios(cucumberFeatures);
-
- // then
- assertThat(result, is(2));
- }
-
- @Test
- public void calculates_number_of_tests_for_scenarios_with_examples() {
-
- // given 2 scenario outlines with 2 examples each and 2 rows (excluding the header row) each
- final List cucumberFeatures = createCucumberFeaturesWithScenarioOutlines(1, 2, 2, 2);
-
- // when
- final int result = ScenarioCounter.countScenarios(cucumberFeatures);
-
- // then
- assertThat(result, is(8));
- }
-
- private List createCucumberFeaturesWithScenarios(
- final int numberOfCucumberFeatures,
- final int numberOfCucumberScenarios) {
-
- final List cucumberFeatures = new ArrayList();
-
- for (int f = 0; f < numberOfCucumberFeatures; f++) {
-
- final CucumberFeature cucumberFeature = mock(CucumberFeature.class);
- cucumberFeatures.add(cucumberFeature);
-
- final List cucumberTagStatements = new ArrayList();
- for (int s = 0; s < numberOfCucumberScenarios; s++) {
- cucumberTagStatements.add(mock(CucumberScenario.class));
- }
-
- when(cucumberFeature.getFeatureElements()).thenReturn(cucumberTagStatements);
- }
- return cucumberFeatures;
- }
-
- private List createCucumberFeaturesWithScenarioOutlines(
- final int numberOfCucumberFeatures,
- final int numberOfScenarioOutlines,
- final int numberOfCucumberExamples,
- final int numberOfExampleRows) {
-
- final int numberOfExampleRowsIncludingHeaderRow = numberOfExampleRows + 1;
- final List cucumberFeatures = new ArrayList();
-
- for (int f = 0; f < numberOfCucumberFeatures; f++) {
-
- final CucumberFeature cucumberFeature = mock(CucumberFeature.class);
- cucumberFeatures.add(cucumberFeature);
-
- // set up 2 scenarios outlines
- final List cucumberTagStatements = new ArrayList();
-
- for (int o = 0; o < numberOfScenarioOutlines; o++) {
- cucumberTagStatements.add(mock(CucumberScenarioOutline.class));
- }
- when(cucumberFeature.getFeatureElements()).thenReturn(cucumberTagStatements);
-
- // with 2 examples for each scenario outline
- for (final CucumberTagStatement cucumberTagStatement : cucumberTagStatements) {
- final CucumberScenarioOutline cucumberScenarioOutline = (CucumberScenarioOutline) cucumberTagStatement;
- final List cucumberExamplesList = createMockList(CucumberExamples.class, numberOfCucumberExamples);
- when(cucumberScenarioOutline.getCucumberExamplesList()).thenReturn(cucumberExamplesList);
-
- // each example should have two rows (excluding the header row)
- for (final CucumberExamples cucumberExamples : cucumberExamplesList) {
-
- final Examples examples = mock(Examples.class);
- when(examples.getRows()).thenReturn(createMockList(ExamplesTableRow.class, numberOfExampleRowsIncludingHeaderRow));
- when(cucumberExamples.getExamples()).thenReturn(examples);
-
- }
- }
-
- }
-
- return cucumberFeatures;
- }
-
- private static List createMockList(final Class type, final int numberOfMocks) {
- final List list = new ArrayList();
-
- for (int i = 0; i < numberOfMocks; i++) {
- list.add(mock(type));
- }
- return list;
- }
-}
diff --git a/clojure/pom.xml b/clojure/pom.xml
index 1cf0c26f20..c8cacebc7a 100644
--- a/clojure/pom.xml
+++ b/clojure/pom.xml
@@ -2,10 +2,10 @@
4.0.0
- info.cukes
+ io.cucumber
cucumber-jvm
../pom.xml
- 1.2.6-SNAPSHOT
+ 2.0.0-SNAPSHOT
cucumber-clojure
@@ -14,7 +14,7 @@
- info.cukes
+ io.cucumber
cucumber-core
@@ -23,7 +23,7 @@
provided
- info.cukes
+ io.cucumber
gherkin
provided
@@ -39,7 +39,7 @@
test
- info.cukes
+ io.cucumber
cucumber-junit
test
diff --git a/clojure/src/main/clj/cucumber/runtime/clj.clj b/clojure/src/main/clj/cucumber/runtime/clj.clj
index 1966e78006..c51a35d25f 100644
--- a/clojure/src/main/clj/cucumber/runtime/clj.clj
+++ b/clojure/src/main/clj/cucumber/runtime/clj.clj
@@ -3,10 +3,10 @@
(:import (cucumber.runtime CucumberException
JdkPatternArgumentMatcher
StepDefinition
- HookDefinition)
+ HookDefinition
+ TagPredicate)
(cucumber.runtime.snippets Snippet
SnippetGenerator)
- (gherkin TagExpression)
(clojure.lang RT))
(:gen-class :name cucumber.runtime.clj.Backend
:implements [cucumber.runtime.Backend]
@@ -56,8 +56,8 @@
(defn- -disposeWorld [cljb])
-(defn- -getSnippet [cljb step _]
- (.getSnippet snippet-generator step nil))
+(defn- -getSnippet [cljb step keyword _]
+ (.getSnippet snippet-generator step keyword nil))
(defn- -setUnreportedStepExecutor [cljb executor]
"executor")
@@ -72,7 +72,7 @@
StepDefinition
(matchedArguments [_ step]
(.argumentsFrom (JdkPatternArgumentMatcher. pattern)
- (.getName step)))
+ (.getText step)))
(getLocation [_ detail]
(location-str location))
(getParameterCount [_]
@@ -92,7 +92,7 @@
(defmulti add-hook-definition (fn [t & _] t))
(defmethod add-hook-definition :before [_ tag-expression hook-fun location]
- (let [te (TagExpression. tag-expression)]
+ (let [tp (TagPredicate. tag-expression)]
(.addBeforeHook
@glue
(reify
@@ -102,12 +102,12 @@
(execute [hd scenario-result]
(hook-fun))
(matches [hd tags]
- (.evaluate te tags))
+ (.apply tp tags))
(getOrder [hd] 0)
(isScenarioScoped [hd] false)))))
(defmethod add-hook-definition :after [_ tag-expression hook-fun location]
- (let [te (TagExpression. tag-expression)
+ (let [tp (TagPredicate. tag-expression)
max-parameter-count (->> hook-fun class .getDeclaredMethods
(filter #(= "invoke" (.getName %)))
(map #(count (.getParameterTypes %)))
@@ -123,7 +123,7 @@
(hook-fun)
(hook-fun scenario-result)))
(matches [hd tags]
- (.evaluate te tags))
+ (.apply tp tags))
(getOrder [hd] 0)
(isScenarioScoped [hd] false)))))
diff --git a/clojure/src/test/java/cucumber/runtime/clojure/ClojureSnippetTest.java b/clojure/src/test/java/cucumber/runtime/clojure/ClojureSnippetTest.java
index e4b8c00f25..d666266ff2 100644
--- a/clojure/src/test/java/cucumber/runtime/clojure/ClojureSnippetTest.java
+++ b/clojure/src/test/java/cucumber/runtime/clojure/ClojureSnippetTest.java
@@ -2,9 +2,12 @@
import cucumber.runtime.Backend;
import cucumber.runtime.io.ResourceLoader;
-import gherkin.formatter.model.Comment;
-import gherkin.formatter.model.DataTableRow;
-import gherkin.formatter.model.Step;
+import gherkin.pickles.Argument;
+import gherkin.pickles.PickleCell;
+import gherkin.pickles.PickleLocation;
+import gherkin.pickles.PickleRow;
+import gherkin.pickles.PickleStep;
+import gherkin.pickles.PickleTable;
import org.junit.Test;
import java.util.Collections;
@@ -14,12 +17,13 @@
import static org.junit.Assert.assertEquals;
public class ClojureSnippetTest {
- private static final List NO_COMMENTS = Collections.emptyList();
+ private static final List NO_ARGUMENTS = Collections.emptyList();
+ private static final List NO_LOCATIONS = Collections.emptyList();
@Test
public void generatesPlainSnippet() throws Exception {
- Step step = new Step(NO_COMMENTS, "Given ", "I have 4 cukes in my \"big\" belly", 0, null, null);
- String snippet = newBackend().getSnippet(step, null);
+ PickleStep step = new PickleStep("I have 4 cukes in my \"big\" belly", NO_ARGUMENTS, NO_LOCATIONS);
+ String snippet = newBackend().getSnippet(step, "Given", null);
String expected = "" +
"(Given #\"^I have (\\d+) cukes in my \\\"([^\\\"]*)\\\" belly$\" [arg1 arg2]\n" +
" (comment Write code here that turns the phrase above into concrete actions )\n" +
@@ -29,9 +33,9 @@ public void generatesPlainSnippet() throws Exception {
@Test
public void generatesSnippetWithDataTable() throws Exception {
- List dataTable = asList(new DataTableRow(NO_COMMENTS, asList("col1"), 1));
- Step step = new Step(NO_COMMENTS, "Given ", "I have:", 0, dataTable, null);
- String snippet = (newBackend()).getSnippet(step, null);
+ PickleTable dataTable = new PickleTable(asList(new PickleRow(asList(new PickleCell(null, "col1")))));
+ PickleStep step = new PickleStep("I have:", asList((Argument)dataTable), NO_LOCATIONS);
+ String snippet = (newBackend()).getSnippet(step, "Given", null);
String expected = "" +
"(Given #\"^I have:$\" [arg1]\n" +
" (comment Write code here that turns the phrase above into concrete actions )\n" +
diff --git a/clojure/tmp.txt b/clojure/tmp.txt
new file mode 100644
index 0000000000..10f3e44b1a
--- /dev/null
+++ b/clojure/tmp.txt
@@ -0,0 +1,2 @@
+ a
+ b
diff --git a/core/pom.xml b/core/pom.xml
index fd5242dfa4..9075a1f64c 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -2,10 +2,10 @@
4.0.0
- info.cukes
+ io.cucumber
cucumber-jvm
../pom.xml
- 1.2.6-SNAPSHOT
+ 2.0.0-SNAPSHOT
cucumber-core
@@ -22,9 +22,13 @@
cucumber-jvm-deps
- info.cukes
+ io.cucumber
gherkin
+
+ io.cucumber
+ tag-expressions
+
junit
diff --git a/core/src/main/java/cucumber/api/DataTable.java b/core/src/main/java/cucumber/api/DataTable.java
index c387b02b9f..59df9595bb 100644
--- a/core/src/main/java/cucumber/api/DataTable.java
+++ b/core/src/main/java/cucumber/api/DataTable.java
@@ -6,10 +6,11 @@
import cucumber.runtime.table.TableConverter;
import cucumber.runtime.table.TableDiffException;
import cucumber.runtime.table.TableDiffer;
+import cucumber.runtime.table.TablePrinter;
import cucumber.runtime.xstream.LocalizedXStreams;
-import gherkin.formatter.PrettyFormatter;
-import gherkin.formatter.model.DataTableRow;
-import gherkin.formatter.model.Row;
+import gherkin.pickles.PickleCell;
+import gherkin.pickles.PickleRow;
+import gherkin.pickles.PickleTable;
import java.util.ArrayList;
import java.util.Collections;
@@ -24,7 +25,7 @@
public class DataTable {
private final List> raw;
- private final List gherkinRows;
+ private final PickleTable pickleTable;
private final TableConverter tableConverter;
public static DataTable create(List> raw) {
@@ -48,17 +49,19 @@ private static DataTable create(List> raw, Locale locale, String format, Strin
/**
* Creates a new DataTable. This constructor should not be called by Cucumber users - it's used internally only.
*
- * @param gherkinRows the underlying rows.
+ * @param pickleTable the underlying table.
* @param tableConverter how to convert the rows.
*/
- public DataTable(List gherkinRows, TableConverter tableConverter) {
- this.gherkinRows = gherkinRows;
+ public DataTable(PickleTable pickleTable, TableConverter tableConverter) {
+ this.pickleTable = pickleTable;
this.tableConverter = tableConverter;
- int columns = gherkinRows.isEmpty() ? 0 : gherkinRows.get(0).getCells().size();
+ int columns = pickleTable.getRows().isEmpty() ? 0 : pickleTable.getRows().get(0).getCells().size();
List> raw = new ArrayList>();
- for (Row row : gherkinRows) {
+ for (PickleRow row : pickleTable.getRows()) {
List list = new ArrayList();
- list.addAll(row.getCells());
+ for (PickleCell cell : row.getCells()) {
+ list.add(cell.getValue());
+ }
if (columns != row.getCells().size()) {
throw new CucumberException(String.format("Table is unbalanced: expected %s column(s) but found %s.", columns, row.getCells().size()));
}
@@ -67,8 +70,8 @@ public DataTable(List gherkinRows, TableConverter tableConverter)
this.raw = Collections.unmodifiableList(raw);
}
- private DataTable(List gherkinRows, List> raw, TableConverter tableConverter) {
- this.gherkinRows = gherkinRows;
+ private DataTable(PickleTable pickleTable, List> raw, TableConverter tableConverter) {
+ this.pickleTable = pickleTable;
this.tableConverter = tableConverter;
this.raw = Collections.unmodifiableList(raw);
}
@@ -206,16 +209,15 @@ public void unorderedDiff(List> other) throws TableDiffException {
*
* @return a list of raw rows.
*/
- public List getGherkinRows() {
- return Collections.unmodifiableList(gherkinRows);
+ public List getPickleRows() {
+ return Collections.unmodifiableList(pickleTable.getRows());
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
- PrettyFormatter pf = new PrettyFormatter(result, true, false);
- pf.table(getGherkinRows());
- pf.eof();
+ TablePrinter printer = createTablePrinter();
+ printer.printTable(raw, result);
return result.toString();
}
@@ -223,7 +225,7 @@ public List diffableRows() {
List result = new ArrayList();
List> convertedRows = raw();
for (int i = 0; i < convertedRows.size(); i++) {
- result.add(new DiffableRow(getGherkinRows().get(i), convertedRows.get(i)));
+ result.add(new DiffableRow(getPickleRows().get(i), convertedRows.get(i)));
}
return result;
}
@@ -234,9 +236,9 @@ public TableConverter getTableConverter() {
public DataTable transpose() {
List> transposed = new ArrayList>();
- for (int i = 0; i < gherkinRows.size(); i++) {
- Row gherkinRow = gherkinRows.get(i);
- for (int j = 0; j < gherkinRow.getCells().size(); j++) {
+ for (int i = 0; i < pickleTable.getRows().size(); i++) {
+ PickleRow pickleRow = pickleTable.getRows().get(i);
+ for (int j = 0; j < pickleRow.getCells().size(); j++) {
List row = null;
if (j < transposed.size()) {
row = transposed.get(j);
@@ -245,10 +247,10 @@ public DataTable transpose() {
row = new ArrayList();
transposed.add(row);
}
- row.add(gherkinRow.getCells().get(j));
+ row.add(pickleRow.getCells().get(j).getValue());
}
}
- return new DataTable(this.gherkinRows, transposed, this.tableConverter);
+ return new DataTable(this.pickleTable, transposed, this.tableConverter);
}
@Override
@@ -267,4 +269,8 @@ public boolean equals(Object o) {
public int hashCode() {
return raw.hashCode();
}
+
+ protected TablePrinter createTablePrinter() {
+ return new TablePrinter();
+ }
}
diff --git a/core/src/main/java/cucumber/api/HookType.java b/core/src/main/java/cucumber/api/HookType.java
new file mode 100644
index 0000000000..423c29421d
--- /dev/null
+++ b/core/src/main/java/cucumber/api/HookType.java
@@ -0,0 +1,10 @@
+package cucumber.api;
+
+public enum HookType {
+ Before, After;
+
+ @Override
+ public String toString() {
+ return super.toString().toLowerCase();
+ }
+}
diff --git a/core/src/main/java/cucumber/api/Result.java b/core/src/main/java/cucumber/api/Result.java
new file mode 100644
index 0000000000..e5e2a2a96d
--- /dev/null
+++ b/core/src/main/java/cucumber/api/Result.java
@@ -0,0 +1,85 @@
+package cucumber.api;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.List;
+
+public class Result {
+ private static final long serialVersionUID = 1L;
+
+ private final String status;
+ private final Long duration;
+ private final Throwable error;
+ private final List snippets;
+ public static final Result SKIPPED = new Result("skipped", null, null);
+ public static final String UNDEFINED = "undefined";
+ public static final String PASSED = "passed";
+ public static final String PENDING = "pending";
+ public static final String FAILED = "failed";
+
+ /**
+ * Used at runtime
+ *
+ * @param status
+ * @param duration
+ * @param error
+ */
+ public Result(String status, Long duration, Throwable error) {
+ this(status, duration, error, Collections.emptyList());
+ }
+
+ /**
+ * Used at runtime
+ *
+ * @param status
+ * @param duration
+ * @param error
+ * @param snippets
+ */
+ public Result(String status, Long duration, Throwable error, List snippets) {
+ this.status = status;
+ this.duration = duration;
+ this.error = error;
+ this.snippets = snippets;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public Long getDuration() {
+ return duration;
+ }
+
+ public String getErrorMessage() {
+ return error != null ? getErrorMessage(error) : null;
+ }
+
+ public Throwable getError() {
+ return error;
+ }
+
+ public List getSnippets() {
+ return snippets;
+ }
+
+ public boolean isOk(boolean isStrict) {
+ return hasAlwaysOkStatus() || !isStrict && hasOkWhenNotStrictStatus();
+ }
+
+ private boolean hasAlwaysOkStatus() {
+ return Result.PASSED.equals(status) || Result.SKIPPED.getStatus().equals(status);
+ }
+
+ private boolean hasOkWhenNotStrictStatus() {
+ return Result.UNDEFINED.equals(status) || Result.PENDING.equals(status);
+ }
+
+ private String getErrorMessage(Throwable error) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ error.printStackTrace(printWriter);
+ return stringWriter.getBuffer().toString();
+ }
+}
diff --git a/core/src/main/java/cucumber/api/Scenario.java b/core/src/main/java/cucumber/api/Scenario.java
index fa90f726db..9394eca871 100644
--- a/core/src/main/java/cucumber/api/Scenario.java
+++ b/core/src/main/java/cucumber/api/Scenario.java
@@ -51,9 +51,4 @@ public interface Scenario {
* @return the name of the Scenario
*/
String getName();
-
- /**
- * @return the id of the Scenario.
- */
- String getId();
}
diff --git a/core/src/main/java/cucumber/api/TestCase.java b/core/src/main/java/cucumber/api/TestCase.java
new file mode 100644
index 0000000000..62d28dd7fc
--- /dev/null
+++ b/core/src/main/java/cucumber/api/TestCase.java
@@ -0,0 +1,63 @@
+package cucumber.api;
+
+import cucumber.api.event.TestCaseFinished;
+import cucumber.api.event.TestCaseStarted;
+import cucumber.runner.EventBus;
+import cucumber.runtime.ScenarioImpl;
+import gherkin.events.PickleEvent;
+import gherkin.pickles.PickleLocation;
+import gherkin.pickles.PickleTag;
+
+import java.util.List;
+
+public class TestCase {
+ private final PickleEvent pickleEvent;
+ private final List testSteps;
+
+ public TestCase(List testSteps, PickleEvent pickleEvent) {
+ this.testSteps = testSteps;
+ this.pickleEvent = pickleEvent;
+ }
+
+ public void run(EventBus bus) {
+ boolean skipNextStep = false;
+ bus.send(new TestCaseStarted(bus.getTime(), this));
+ ScenarioImpl scenarioResult = new ScenarioImpl(bus, pickleEvent.pickle);
+ for (TestStep step : testSteps) {
+ Result stepResult = step.run(bus, pickleEvent.pickle.getLanguage(), scenarioResult, skipNextStep);
+ if (stepResult.getStatus() != Result.PASSED) {
+ skipNextStep = true;
+ }
+ scenarioResult.add(stepResult);
+ }
+ bus.send(new TestCaseFinished(bus.getTime(), this, new Result(scenarioResult.getStatus(), null, null)));
+ }
+
+ public List getTestSteps() {
+ return testSteps;
+ }
+
+ public String getName() {
+ return pickleEvent.pickle.getName();
+ }
+
+ public String getScenarioDesignation() {
+ return fileColonLine(pickleEvent.pickle.getLocations().get(0)) + " # " + getName();
+ }
+
+ public String getPath() {
+ return pickleEvent.uri;
+ }
+
+ public int getLine() {
+ return pickleEvent.pickle.getLocations().get(0).getLine();
+ }
+
+ private String fileColonLine(PickleLocation location) {
+ return pickleEvent.uri + ":" + Integer.toString(location.getLine());
+ }
+
+ public List getTags() {
+ return pickleEvent.pickle.getTags();
+ }
+}
diff --git a/core/src/main/java/cucumber/api/TestStep.java b/core/src/main/java/cucumber/api/TestStep.java
new file mode 100644
index 0000000000..84bb9db6ed
--- /dev/null
+++ b/core/src/main/java/cucumber/api/TestStep.java
@@ -0,0 +1,106 @@
+package cucumber.api;
+
+import cucumber.api.event.TestStepFinished;
+import cucumber.api.event.TestStepStarted;
+import cucumber.runner.EventBus;
+import cucumber.runtime.DefinitionMatch;
+import cucumber.runtime.UndefinedStepDefinitionException;
+import gherkin.pickles.Argument;
+import gherkin.pickles.PickleStep;
+
+import java.util.Arrays;
+import java.util.List;
+
+public abstract class TestStep {
+ private static final String[] PENDING_EXCEPTIONS = {
+ "org.junit.AssumptionViolatedException",
+ "org.junit.internal.AssumptionViolatedException"
+ };
+ static {
+ Arrays.sort(PENDING_EXCEPTIONS);
+ }
+ protected final DefinitionMatch definitionMatch;
+
+ public TestStep(DefinitionMatch definitionMatch) {
+ this.definitionMatch = definitionMatch;
+ }
+
+ public String getPattern() {
+ return definitionMatch.getPattern();
+ }
+
+ public String getCodeLocation() {
+ return definitionMatch.getCodeLocation();
+ }
+
+ public List getDefinitionArgument() {
+ return definitionMatch.getArguments();
+ }
+
+ public abstract boolean isHook();
+
+ public abstract PickleStep getPickleStep();
+
+ public abstract String getStepText();
+
+ public abstract String getStepLocation();
+
+ public abstract int getStepLine();
+
+ public abstract List getStepArgument();
+
+ public abstract HookType getHookType();
+
+ public Result run(EventBus bus, String language, Scenario scenario, boolean skipSteps) {
+ Long startTime = bus.getTime();
+ bus.send(new TestStepStarted(startTime, this));
+ String status;
+ Throwable error = null;
+ try {
+ status = executeStep(language, scenario, skipSteps);
+ } catch (Throwable t) {
+ error = t;
+ status = mapThrowableToStatus(t);
+ }
+ Long stopTime = bus.getTime();
+ Result result = mapStatusToResult(status, error, stopTime - startTime);
+ bus.send(new TestStepFinished(stopTime, this, result));
+ return result;
+ }
+
+ protected String nonExceptionStatus(boolean skipSteps) {
+ return skipSteps ? Result.SKIPPED.getStatus() : Result.PASSED;
+ }
+
+ protected String executeStep(String language, Scenario scenario, boolean skipSteps) throws Throwable {
+ if (!skipSteps) {
+ definitionMatch.runStep(language, scenario);
+ return Result.PASSED;
+ } else {
+ definitionMatch.dryRunStep(language, scenario);
+ return Result.SKIPPED.getStatus();
+ }
+ }
+
+ private String mapThrowableToStatus(Throwable t) {
+ if (t.getClass().isAnnotationPresent(Pending.class) || Arrays.binarySearch(PENDING_EXCEPTIONS, t.getClass().getName()) >= 0) {
+ return Result.PENDING;
+ }
+ if (t.getClass() == UndefinedStepDefinitionException.class) {
+ return Result.UNDEFINED;
+ }
+ return Result.FAILED;
+ }
+
+ private Result mapStatusToResult(String status, Throwable error, long duration) {
+ Long resultDuration = duration;
+ Throwable resultError = error;
+ if (status == Result.SKIPPED.getStatus()) {
+ return Result.SKIPPED;
+ }
+ if (status == Result.UNDEFINED) {
+ return new Result(status, 0l, null, definitionMatch.getSnippets());
+ }
+ return new Result(status, resultDuration, resultError);
+ }
+}
diff --git a/core/src/main/java/cucumber/api/event/EmbedEvent.java b/core/src/main/java/cucumber/api/event/EmbedEvent.java
new file mode 100644
index 0000000000..aab9b6bf49
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/EmbedEvent.java
@@ -0,0 +1,13 @@
+package cucumber.api.event;
+
+public class EmbedEvent extends TimeStampedEvent {
+ public final byte[] data;
+ public final String mimeType;
+
+ public EmbedEvent(Long timeStamp, byte[] data, String mimeType) {
+ super(timeStamp);
+ this.data = data;
+ this.mimeType = mimeType;
+ }
+
+}
diff --git a/core/src/main/java/cucumber/api/event/Event.java b/core/src/main/java/cucumber/api/event/Event.java
new file mode 100644
index 0000000000..5c6cb431a9
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/Event.java
@@ -0,0 +1,7 @@
+package cucumber.api.event;
+
+public interface Event {
+
+ Long getTimeStamp();
+
+}
diff --git a/core/src/main/java/cucumber/api/event/EventHandler.java b/core/src/main/java/cucumber/api/event/EventHandler.java
new file mode 100644
index 0000000000..650725089b
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/EventHandler.java
@@ -0,0 +1,7 @@
+package cucumber.api.event;
+
+public interface EventHandler {
+
+ public void receive(T event);
+
+}
diff --git a/core/src/main/java/cucumber/api/event/EventListener.java b/core/src/main/java/cucumber/api/event/EventListener.java
new file mode 100644
index 0000000000..9bc8d72240
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/EventListener.java
@@ -0,0 +1,16 @@
+package cucumber.api.event;
+
+import cucumber.api.event.EventPublisher;
+
+/**
+ * This is the interface you should implement if you want your own custom
+ * formatter.
+ */
+public interface EventListener {
+
+ /**
+ * Set the event bus that the formatter can register event listeners in.
+ */
+ void setEventPublisher(EventPublisher publisher);
+
+}
diff --git a/core/src/main/java/cucumber/api/event/EventPublisher.java b/core/src/main/java/cucumber/api/event/EventPublisher.java
new file mode 100644
index 0000000000..e438865c40
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/EventPublisher.java
@@ -0,0 +1,5 @@
+package cucumber.api.event;
+
+public interface EventPublisher {
+ void registerHandlerFor(Class extends Event> eventType, EventHandler extends Event> handler);
+}
diff --git a/core/src/main/java/cucumber/api/event/TestCaseFinished.java b/core/src/main/java/cucumber/api/event/TestCaseFinished.java
new file mode 100644
index 0000000000..f8091ec18a
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/TestCaseFinished.java
@@ -0,0 +1,16 @@
+package cucumber.api.event;
+
+import cucumber.api.Result;
+import cucumber.api.TestCase;
+
+public class TestCaseFinished extends TimeStampedEvent {
+ public final Result result;
+ public final TestCase testCase;
+
+ public TestCaseFinished(Long timeStamp, TestCase testCase, Result result) {
+ super(timeStamp);
+ this.testCase = testCase;
+ this.result = result;
+ }
+
+}
diff --git a/core/src/main/java/cucumber/api/event/TestCaseStarted.java b/core/src/main/java/cucumber/api/event/TestCaseStarted.java
new file mode 100644
index 0000000000..3d5c0649aa
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/TestCaseStarted.java
@@ -0,0 +1,13 @@
+package cucumber.api.event;
+
+import cucumber.api.TestCase;
+
+public class TestCaseStarted extends TimeStampedEvent {
+ public final TestCase testCase;
+
+ public TestCaseStarted(Long timeStamp, TestCase testCase) {
+ super(timeStamp);
+ this.testCase = testCase;
+ }
+
+}
diff --git a/core/src/main/java/cucumber/api/event/TestRunFinished.java b/core/src/main/java/cucumber/api/event/TestRunFinished.java
new file mode 100644
index 0000000000..68654e24c9
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/TestRunFinished.java
@@ -0,0 +1,8 @@
+package cucumber.api.event;
+
+public class TestRunFinished extends TimeStampedEvent {
+
+ public TestRunFinished(Long timeStamp) {
+ super(timeStamp);
+ }
+}
diff --git a/core/src/main/java/cucumber/api/event/TestSourceRead.java b/core/src/main/java/cucumber/api/event/TestSourceRead.java
new file mode 100644
index 0000000000..ff80ec9bc6
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/TestSourceRead.java
@@ -0,0 +1,15 @@
+package cucumber.api.event;
+
+public class TestSourceRead extends TimeStampedEvent {
+ public final String path;
+ public final String language;
+ public final String source;
+
+ public TestSourceRead(Long timeStamp, String path, String language, String source) {
+ super(timeStamp);
+ this.path = path;
+ this.language = language;
+ this.source = source;
+ }
+
+}
diff --git a/core/src/main/java/cucumber/api/event/TestStepFinished.java b/core/src/main/java/cucumber/api/event/TestStepFinished.java
new file mode 100644
index 0000000000..287c52d6a2
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/TestStepFinished.java
@@ -0,0 +1,16 @@
+package cucumber.api.event;
+
+import cucumber.api.Result;
+import cucumber.api.TestStep;
+
+public class TestStepFinished extends TimeStampedEvent {
+ public final TestStep testStep;
+ public final Result result;
+
+ public TestStepFinished(Long timeStamp, TestStep testStep, Result result) {
+ super(timeStamp);
+ this.testStep = testStep;
+ this.result = result;
+ }
+
+}
diff --git a/core/src/main/java/cucumber/api/event/TestStepStarted.java b/core/src/main/java/cucumber/api/event/TestStepStarted.java
new file mode 100644
index 0000000000..1d4879396a
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/TestStepStarted.java
@@ -0,0 +1,13 @@
+package cucumber.api.event;
+
+import cucumber.api.TestStep;
+
+public class TestStepStarted extends TimeStampedEvent {
+ public final TestStep testStep;
+
+ public TestStepStarted(Long timeStamp, TestStep testStep) {
+ super(timeStamp);
+ this.testStep = testStep;
+ }
+
+}
diff --git a/core/src/main/java/cucumber/api/event/TimeStampedEvent.java b/core/src/main/java/cucumber/api/event/TimeStampedEvent.java
new file mode 100644
index 0000000000..71c771884d
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/TimeStampedEvent.java
@@ -0,0 +1,14 @@
+package cucumber.api.event;
+
+abstract class TimeStampedEvent implements Event {
+ private Long timeStamp;
+
+ public TimeStampedEvent(Long timeStamp) {
+ this.timeStamp = timeStamp;
+ }
+
+ @Override
+ public Long getTimeStamp() {
+ return timeStamp;
+ }
+}
diff --git a/core/src/main/java/cucumber/api/event/WriteEvent.java b/core/src/main/java/cucumber/api/event/WriteEvent.java
new file mode 100644
index 0000000000..8f16211820
--- /dev/null
+++ b/core/src/main/java/cucumber/api/event/WriteEvent.java
@@ -0,0 +1,10 @@
+package cucumber.api.event;
+
+public class WriteEvent extends TimeStampedEvent {
+ public final String text;
+
+ public WriteEvent(Long timeStamp, String text) {
+ super(timeStamp);
+ this.text = text;
+ }
+}
diff --git a/core/src/main/java/cucumber/api/formatter/AnsiEscapes.java b/core/src/main/java/cucumber/api/formatter/AnsiEscapes.java
new file mode 100644
index 0000000000..389488a4e3
--- /dev/null
+++ b/core/src/main/java/cucumber/api/formatter/AnsiEscapes.java
@@ -0,0 +1,48 @@
+package cucumber.api.formatter;
+
+public class AnsiEscapes {
+ private static final char ESC = 27;
+ private static final char BRACKET = '[';
+
+ public static AnsiEscapes RESET = color(0);
+ public static AnsiEscapes BLACK = color(30);
+ public static AnsiEscapes RED = color(31);
+ public static AnsiEscapes GREEN = color(32);
+ public static AnsiEscapes YELLOW = color(33);
+ public static AnsiEscapes BLUE = color(34);
+ public static AnsiEscapes MAGENTA = color(35);
+ public static AnsiEscapes CYAN = color(36);
+ public static AnsiEscapes WHITE = color(37);
+ public static AnsiEscapes DEFAULT = color(9);
+ public static AnsiEscapes GREY = color(90);
+ public static AnsiEscapes INTENSITY_BOLD = color(1);
+
+ private static AnsiEscapes color(int code) {
+ return new AnsiEscapes(String.valueOf(code) + "m");
+ }
+
+ public static AnsiEscapes up(int count) {
+ return new AnsiEscapes(String.valueOf(count) + "A");
+ }
+
+ private final String value;
+
+ private AnsiEscapes(String value) {
+ this.value = value;
+ }
+
+ public void appendTo(NiceAppendable a) {
+ a.append(ESC).append(BRACKET).append(value);
+ }
+
+ public void appendTo(StringBuilder a) {
+ a.append(ESC).append(BRACKET).append(value);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ appendTo(sb);
+ return sb.toString();
+ }
+}
diff --git a/core/src/main/java/cucumber/runtime/formatter/ColorAware.java b/core/src/main/java/cucumber/api/formatter/ColorAware.java
similarity index 68%
rename from core/src/main/java/cucumber/runtime/formatter/ColorAware.java
rename to core/src/main/java/cucumber/api/formatter/ColorAware.java
index f82b0925f5..ecc8fcbbbf 100644
--- a/core/src/main/java/cucumber/runtime/formatter/ColorAware.java
+++ b/core/src/main/java/cucumber/api/formatter/ColorAware.java
@@ -1,4 +1,4 @@
-package cucumber.runtime.formatter;
+package cucumber.api.formatter;
public interface ColorAware {
void setMonochrome(boolean monochrome);
diff --git a/core/src/main/java/cucumber/api/formatter/Formatter.java b/core/src/main/java/cucumber/api/formatter/Formatter.java
new file mode 100644
index 0000000000..8858f1b27b
--- /dev/null
+++ b/core/src/main/java/cucumber/api/formatter/Formatter.java
@@ -0,0 +1,10 @@
+package cucumber.api.formatter;
+
+import cucumber.api.event.EventListener;
+
+/**
+ * This is the interface you should implement if you want your own custom
+ * formatter.
+ */
+public interface Formatter extends EventListener {
+}
diff --git a/core/src/main/java/cucumber/api/formatter/NiceAppendable.java b/core/src/main/java/cucumber/api/formatter/NiceAppendable.java
new file mode 100644
index 0000000000..cf31ea5e13
--- /dev/null
+++ b/core/src/main/java/cucumber/api/formatter/NiceAppendable.java
@@ -0,0 +1,85 @@
+package cucumber.api.formatter;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+
+/**
+ * A nice appendable that doesn't throw checked exceptions
+ */
+public class NiceAppendable {
+ private static final CharSequence NL = "\n";
+ private final Appendable out;
+
+ public NiceAppendable(Appendable out) {
+ this.out = out;
+ }
+
+ public NiceAppendable append(CharSequence csq) {
+ try {
+ out.append(csq);
+ tryFlush();
+ return this;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public NiceAppendable append(CharSequence csq, int start, int end) {
+ try {
+ out.append(csq, start, end);
+ tryFlush();
+ return this;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public NiceAppendable append(char c) {
+ try {
+ out.append(c);
+ tryFlush();
+ return this;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public NiceAppendable println() {
+ return append(NL);
+ }
+
+ public NiceAppendable println(CharSequence csq) {
+ try {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(csq);
+ buffer.append(NL);
+ out.append(buffer.toString());
+ tryFlush();
+ return this;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void close() {
+ try {
+ tryFlush();
+ if (out instanceof Closeable) {
+ ((Closeable) out).close();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void tryFlush() {
+ if (!(out instanceof Flushable))
+ return;
+ try {
+ ((Flushable) out).flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/core/src/main/java/cucumber/runtime/formatter/StrictAware.java b/core/src/main/java/cucumber/api/formatter/StrictAware.java
similarity index 68%
rename from core/src/main/java/cucumber/runtime/formatter/StrictAware.java
rename to core/src/main/java/cucumber/api/formatter/StrictAware.java
index 4037a87fbc..c11be19f75 100755
--- a/core/src/main/java/cucumber/runtime/formatter/StrictAware.java
+++ b/core/src/main/java/cucumber/api/formatter/StrictAware.java
@@ -1,4 +1,4 @@
-package cucumber.runtime.formatter;
+package cucumber.api.formatter;
public interface StrictAware {
public void setStrict(boolean strict);
diff --git a/core/src/main/java/cucumber/runner/EventBus.java b/core/src/main/java/cucumber/runner/EventBus.java
new file mode 100644
index 0000000000..999c1b7503
--- /dev/null
+++ b/core/src/main/java/cucumber/runner/EventBus.java
@@ -0,0 +1,43 @@
+package cucumber.runner;
+
+import cucumber.api.event.Event;
+import cucumber.api.event.EventHandler;
+import cucumber.api.event.EventPublisher;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class EventBus implements EventPublisher {
+ private final TimeService stopWatch;
+ private Map, List> handlers = new HashMap, List>();
+
+ public EventBus(TimeService stopWatch) {
+ this.stopWatch = stopWatch;
+ }
+
+ public Long getTime() {
+ return stopWatch.time();
+ }
+
+ public void send(Event event) {
+ if (handlers.containsKey(event.getClass())) {
+ for (EventHandler handler : handlers.get(event.getClass())) {
+ handler.receive(event);
+ }
+ }
+ }
+
+ @Override
+ public void registerHandlerFor(Class extends Event> eventType, EventHandler extends Event> handler) {
+ if (handlers.containsKey(eventType)) {
+ handlers.get(eventType).add(handler);
+ } else {
+ List list = new ArrayList();
+ list.add(handler);
+ handlers.put(eventType, list);
+ }
+ }
+
+}
diff --git a/core/src/main/java/cucumber/runner/PickleTestStep.java b/core/src/main/java/cucumber/runner/PickleTestStep.java
new file mode 100644
index 0000000000..5d8a2cac65
--- /dev/null
+++ b/core/src/main/java/cucumber/runner/PickleTestStep.java
@@ -0,0 +1,56 @@
+package cucumber.runner;
+
+import cucumber.api.HookType;
+import cucumber.api.TestStep;
+import cucumber.runtime.DefinitionMatch;
+import cucumber.runtime.StepDefinitionMatch;
+import gherkin.pickles.Argument;
+import gherkin.pickles.PickleStep;
+
+import java.util.List;
+
+public class PickleTestStep extends TestStep {
+ private String uri;
+ private PickleStep step;
+
+ public PickleTestStep(String uri, PickleStep step, DefinitionMatch definitionMatch) {
+ super(definitionMatch);
+ this.uri = uri;
+ this.step = step;
+ }
+
+ @Override
+ public boolean isHook() {
+ return false;
+ }
+
+ @Override
+ public PickleStep getPickleStep() {
+ return step;
+ }
+
+ @Override
+ public String getStepLocation() {
+ return uri + ":" + Integer.toString(getStepLine());
+ }
+
+ @Override
+ public int getStepLine() {
+ return StepDefinitionMatch.getStepLine(step);
+ }
+
+ @Override
+ public String getStepText() {
+ return step.getText();
+ }
+
+ @Override
+ public List getStepArgument() {
+ return step.getArgument();
+ }
+
+ @Override
+ public HookType getHookType() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/core/src/main/java/cucumber/runner/Runner.java b/core/src/main/java/cucumber/runner/Runner.java
new file mode 100644
index 0000000000..1dab975fa5
--- /dev/null
+++ b/core/src/main/java/cucumber/runner/Runner.java
@@ -0,0 +1,159 @@
+package cucumber.runner;
+
+import cucumber.api.HookType;
+import cucumber.api.StepDefinitionReporter;
+import cucumber.api.TestCase;
+import cucumber.api.TestStep;
+import cucumber.runtime.AmbiguousStepDefinitionsMatch;
+import cucumber.runtime.AmbiguousStepDefinitionsException;
+import cucumber.runtime.Backend;
+import cucumber.runtime.FailedStepInstantiationMatch;
+import cucumber.runtime.Glue;
+import cucumber.runtime.HookDefinition;
+import cucumber.runtime.HookDefinitionMatch;
+import cucumber.runtime.RuntimeOptions;
+import cucumber.runtime.StepDefinitionMatch;
+import cucumber.runtime.UndefinedStepDefinitionMatch;
+import cucumber.runtime.UnreportedStepExecutor;
+import gherkin.events.PickleEvent;
+import gherkin.pickles.Argument;
+import gherkin.pickles.PickleLocation;
+import gherkin.pickles.PickleRow;
+import gherkin.pickles.PickleStep;
+import gherkin.pickles.PickleString;
+import gherkin.pickles.PickleTable;
+import gherkin.pickles.PickleTag;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class Runner implements UnreportedStepExecutor {
+ private final Glue glue;
+ private final EventBus bus;
+ private final Collection extends Backend> backends;
+ private final RuntimeOptions runtimeOptions;
+
+ public Runner(Glue glue, EventBus bus, Collection extends Backend> backends, RuntimeOptions runtimeOptions) {
+ this.glue = glue;
+ this.bus = bus;
+ this.runtimeOptions = runtimeOptions;
+ this.backends = backends;
+ for (Backend backend : backends) {
+ backend.loadGlue(glue, runtimeOptions.getGlue());
+ backend.setUnreportedStepExecutor(this);
+ }
+
+ }
+
+ //TODO: Maybe this should go into the cucumber step execution model and it should return the result of that execution!
+ @Override
+ public void runUnreportedStep(String featurePath, String language, String stepName, int line, List dataTableRows, PickleString docString) throws Throwable {
+ List arguments = new ArrayList();
+ if (dataTableRows != null && !dataTableRows.isEmpty()) {
+ arguments.add((Argument) new PickleTable(dataTableRows));
+ } else if (docString != null) {
+ arguments.add(docString);
+ }
+ PickleStep step = new PickleStep(stepName, arguments, Collections.emptyList());
+
+ StepDefinitionMatch match = glue.stepDefinitionMatch(featurePath, step);
+ if (match == null) {
+ UndefinedStepException error = new UndefinedStepException(step);
+
+ StackTraceElement[] originalTrace = error.getStackTrace();
+ StackTraceElement[] newTrace = new StackTraceElement[originalTrace.length + 1];
+ newTrace[0] = new StackTraceElement("✽", "StepDefinition", featurePath, line);
+ System.arraycopy(originalTrace, 0, newTrace, 1, originalTrace.length);
+ error.setStackTrace(newTrace);
+
+ throw error;
+ }
+ match.runStep(language, null);
+ }
+
+ public void runPickle(PickleEvent pickle) {
+ buildBackendWorlds(); // Java8 step definitions will be added to the glue here
+ TestCase testCase = createTestCaseForPickle(pickle);
+ testCase.run(bus);
+ disposeBackendWorlds();
+ }
+
+ public Glue getGlue() {
+ return glue;
+ }
+
+
+ public void reportStepDefinitions(StepDefinitionReporter stepDefinitionReporter) {
+ glue.reportStepDefinitions(stepDefinitionReporter);
+ }
+
+ private TestCase createTestCaseForPickle(PickleEvent pickleEvent) {
+ List testSteps = new ArrayList();
+ if (!runtimeOptions.isDryRun()) {
+ addTestStepsForBeforeHooks(testSteps, pickleEvent.pickle.getTags());
+ }
+ addTestStepsForPickleSteps(testSteps, pickleEvent);
+ if (!runtimeOptions.isDryRun()) {
+ addTestStepsForAfterHooks(testSteps, pickleEvent.pickle.getTags());
+ }
+ TestCase testCase = new TestCase(testSteps, pickleEvent);
+ return testCase;
+ }
+
+ private void addTestStepsForPickleSteps(List testSteps, PickleEvent pickleEvent) {
+ for (PickleStep step : pickleEvent.pickle.getSteps()) {
+ StepDefinitionMatch match;
+ try {
+ match = glue.stepDefinitionMatch(pickleEvent.uri, step);
+ if (match == null) {
+ List snippets = new ArrayList();
+ for (Backend backend : backends) {
+ String snippet = backend.getSnippet(step, "**KEYWORD**", runtimeOptions.getSnippetType().getFunctionNameGenerator());
+ if (snippet != null) {
+ snippets.add(snippet);
+ }
+ }
+ match = new UndefinedStepDefinitionMatch(step, snippets);
+ }
+ } catch (AmbiguousStepDefinitionsException e) {
+ match = new AmbiguousStepDefinitionsMatch(step, e);
+ } catch (Throwable t) {
+ match = new FailedStepInstantiationMatch(pickleEvent.uri, step, t);
+ }
+ testSteps.add(new PickleTestStep(pickleEvent.uri, step, match));
+ }
+ }
+
+ private void addTestStepsForBeforeHooks(List testSteps, List tags) {
+ addTestStepsForHooks(testSteps, tags, glue.getBeforeHooks(), HookType.Before);
+ }
+
+ private void addTestStepsForAfterHooks(List testSteps, List tags) {
+ addTestStepsForHooks(testSteps, tags, glue.getAfterHooks(), HookType.After);
+ }
+
+ private void addTestStepsForHooks(List testSteps, List tags, List hooks, HookType hookType) {
+ for (HookDefinition hook : hooks) {
+ if (hook.matches(tags)) {
+ TestStep testStep = new UnskipableStep(hookType, new HookDefinitionMatch(hook));
+ testSteps.add(testStep);
+ }
+ }
+ }
+
+ private void buildBackendWorlds() {
+ runtimeOptions.getPlugins(); // To make sure that the plugins are instantiated after
+ // the features have been parsed but before the pickles starts to execute.
+ for (Backend backend : backends) {
+ backend.buildWorld();
+ }
+ }
+
+ private void disposeBackendWorlds() {
+ for (Backend backend : backends) {
+ backend.disposeWorld();
+ }
+ }
+}
diff --git a/core/src/main/java/cucumber/runner/TimeService.java b/core/src/main/java/cucumber/runner/TimeService.java
new file mode 100644
index 0000000000..f40ee8bda0
--- /dev/null
+++ b/core/src/main/java/cucumber/runner/TimeService.java
@@ -0,0 +1,29 @@
+package cucumber.runner;
+
+public interface TimeService {
+ long time();
+
+ TimeService SYSTEM = new TimeService() {
+ @Override
+ public long time() {
+ return System.nanoTime();
+ }
+ };
+
+ public static class Stub implements TimeService {
+ private final long duration;
+ private final ThreadLocal currentTime = new ThreadLocal();
+
+ public Stub(long duration) {
+ this.duration = duration;
+ }
+
+ @Override
+ public long time() {
+ Long result = currentTime.get();
+ result = result != null ? result : 0l;
+ currentTime.set(result + duration);
+ return result;
+ }
+ }
+}
diff --git a/core/src/main/java/cucumber/runner/UndefinedStepException.java b/core/src/main/java/cucumber/runner/UndefinedStepException.java
new file mode 100644
index 0000000000..13fd63900d
--- /dev/null
+++ b/core/src/main/java/cucumber/runner/UndefinedStepException.java
@@ -0,0 +1,9 @@
+package cucumber.runner;
+
+import gherkin.pickles.PickleStep;
+
+class UndefinedStepException extends Throwable {
+ public UndefinedStepException(PickleStep step) {
+ super(String.format("Undefined Step: %s", step.getText()));
+ }
+}
diff --git a/core/src/main/java/cucumber/runner/UnskipableStep.java b/core/src/main/java/cucumber/runner/UnskipableStep.java
new file mode 100644
index 0000000000..972bf095d7
--- /dev/null
+++ b/core/src/main/java/cucumber/runner/UnskipableStep.java
@@ -0,0 +1,60 @@
+package cucumber.runner;
+
+import cucumber.api.HookType;
+import cucumber.api.Result;
+import cucumber.api.Scenario;
+import cucumber.api.TestStep;
+import cucumber.runtime.DefinitionMatch;
+import gherkin.pickles.Argument;
+import gherkin.pickles.PickleStep;
+
+import java.util.List;
+
+public class UnskipableStep extends TestStep {
+ private final HookType hookType;
+
+ public UnskipableStep(HookType hookType, DefinitionMatch definitionMatch) {
+ super(definitionMatch);
+ this.hookType = hookType;
+ }
+
+ protected String executeStep(String language, Scenario scenario, boolean skipSteps) throws Throwable {
+ definitionMatch.runStep(language, scenario);
+ return Result.PASSED;
+ }
+
+ @Override
+ public boolean isHook() {
+ return true;
+ }
+
+ @Override
+ public PickleStep getPickleStep() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getStepLocation() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getStepLine() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getStepText() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List getStepArgument() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public HookType getHookType() {
+ return hookType;
+ }
+}
diff --git a/core/src/main/java/cucumber/runtime/AmbiguousStepDefinitionsException.java b/core/src/main/java/cucumber/runtime/AmbiguousStepDefinitionsException.java
index 339ba3afe6..4d6c7d4e53 100644
--- a/core/src/main/java/cucumber/runtime/AmbiguousStepDefinitionsException.java
+++ b/core/src/main/java/cucumber/runtime/AmbiguousStepDefinitionsException.java
@@ -1,24 +1,30 @@
package cucumber.runtime;
+import gherkin.pickles.PickleStep;
+
import java.util.List;
public class AmbiguousStepDefinitionsException extends CucumberException {
private final List matches;
- public AmbiguousStepDefinitionsException(List matches) {
- super(createMessage(matches));
+ public AmbiguousStepDefinitionsException(PickleStep step, List matches) {
+ super(createMessage(step, matches));
this.matches = matches;
}
- private static String createMessage(List matches) {
+ private static String createMessage(PickleStep step, List matches) {
StringBuilder msg = new StringBuilder();
- msg.append(matches.get(0).getStepLocation()).append(" matches more than one step definition:\n");
+ msg.append(quoteText(step.getText())).append(" matches more than one step definition:\n");
for (StepDefinitionMatch match : matches) {
- msg.append(" ").append(match.getPattern()).append(" in ").append(match.getLocation()).append("\n");
+ msg.append(" ").append(quoteText(match.getPattern())).append(" in ").append(match.getLocation()).append("\n");
}
return msg.toString();
}
+ private static String quoteText(String text) {
+ return "\"" + text + "\"";
+ }
+
public List getMatches() {
return matches;
}
diff --git a/core/src/main/java/cucumber/runtime/AmbiguousStepDefinitionsMatch.java b/core/src/main/java/cucumber/runtime/AmbiguousStepDefinitionsMatch.java
new file mode 100644
index 0000000000..e6bf082165
--- /dev/null
+++ b/core/src/main/java/cucumber/runtime/AmbiguousStepDefinitionsMatch.java
@@ -0,0 +1,28 @@
+package cucumber.runtime;
+
+import cucumber.api.Scenario;
+import gherkin.pickles.PickleStep;
+
+public class AmbiguousStepDefinitionsMatch extends StepDefinitionMatch {
+ private AmbiguousStepDefinitionsException exception;
+
+ public AmbiguousStepDefinitionsMatch(PickleStep step, AmbiguousStepDefinitionsException e) {
+ super(null, new NoStepDefinition(), null, step, null);
+ this.exception = e;
+ }
+
+ @Override
+ public void runStep(String language, Scenario scenario) throws Throwable {
+ throw exception;
+ }
+
+ @Override
+ public void dryRunStep(String language, Scenario scenario) throws Throwable {
+ runStep(language, scenario);
+ }
+
+ @Override
+ public Match getMatch() {
+ return exception.getMatches().get(0);
+ }
+}
diff --git a/core/src/main/java/cucumber/runtime/Argument.java b/core/src/main/java/cucumber/runtime/Argument.java
new file mode 100644
index 0000000000..f4ce2c3910
--- /dev/null
+++ b/core/src/main/java/cucumber/runtime/Argument.java
@@ -0,0 +1,25 @@
+package cucumber.runtime;
+
+public class Argument {
+ private static final long serialVersionUID = 1L;
+
+ private final Integer offset;
+ private final String val;
+
+ public Argument(Integer offset, String val) {
+ this.offset = offset;
+ this.val = val;
+ }
+
+ public String getVal() {
+ return val;
+ }
+
+ public Integer getOffset() {
+ return offset;
+ }
+
+ public String toString() {
+ return getVal();
+ }
+}
diff --git a/core/src/main/java/cucumber/runtime/Backend.java b/core/src/main/java/cucumber/runtime/Backend.java
index ebfa09040b..6b7edbe50e 100644
--- a/core/src/main/java/cucumber/runtime/Backend.java
+++ b/core/src/main/java/cucumber/runtime/Backend.java
@@ -1,7 +1,7 @@
package cucumber.runtime;
import cucumber.runtime.snippets.FunctionNameGenerator;
-import gherkin.formatter.model.Step;
+import gherkin.pickles.PickleStep;
import java.util.List;
@@ -28,5 +28,5 @@ public interface Backend {
*/
void disposeWorld();
- String getSnippet(Step step, FunctionNameGenerator functionNameGenerator);
+ String getSnippet(PickleStep step, String keyword, FunctionNameGenerator functionNameGenerator);
}
diff --git a/core/src/main/java/cucumber/runtime/DefinitionMatch.java b/core/src/main/java/cucumber/runtime/DefinitionMatch.java
new file mode 100644
index 0000000000..8509e1a0de
--- /dev/null
+++ b/core/src/main/java/cucumber/runtime/DefinitionMatch.java
@@ -0,0 +1,21 @@
+package cucumber.runtime;
+
+import cucumber.api.Scenario;
+
+import java.util.List;
+
+public interface DefinitionMatch {
+ void runStep(String language, Scenario scenario) throws Throwable;
+
+ void dryRunStep(String language, Scenario scenario) throws Throwable;
+
+ Match getMatch();
+
+ String getPattern();
+
+ String getCodeLocation();
+
+ List getArguments();
+
+ List getSnippets();
+}
diff --git a/core/src/main/java/cucumber/runtime/FailedStepInstantiationMatch.java b/core/src/main/java/cucumber/runtime/FailedStepInstantiationMatch.java
new file mode 100644
index 0000000000..9cac56a844
--- /dev/null
+++ b/core/src/main/java/cucumber/runtime/FailedStepInstantiationMatch.java
@@ -0,0 +1,30 @@
+package cucumber.runtime;
+
+import cucumber.api.Scenario;
+import gherkin.pickles.PickleStep;
+
+import java.util.Collections;
+
+public class FailedStepInstantiationMatch extends StepDefinitionMatch {
+ private final Throwable throwable;
+
+ public FailedStepInstantiationMatch(String uri, PickleStep step, Throwable throwable) {
+ super(Collections.emptyList(), new NoStepDefinition(), uri, step, null);
+ this.throwable = removeFrameworkFramesAndAppendStepLocation(throwable, getStepLocation());
+ }
+
+ @Override
+ public void runStep(String language, Scenario scenario) throws Throwable {
+ throw throwable;
+ }
+
+ @Override
+ public void dryRunStep(String language, Scenario scenario) throws Throwable {
+ runStep(language, scenario);
+ }
+
+ @Override
+ public Match getMatch() {
+ return Match.UNDEFINED;
+ }
+}
diff --git a/core/src/main/java/cucumber/runtime/FeatureBuilder.java b/core/src/main/java/cucumber/runtime/FeatureBuilder.java
index 4e500aeaef..ed64ba05e3 100644
--- a/core/src/main/java/cucumber/runtime/FeatureBuilder.java
+++ b/core/src/main/java/cucumber/runtime/FeatureBuilder.java
@@ -2,22 +2,15 @@
import cucumber.runtime.io.Resource;
import cucumber.runtime.model.CucumberFeature;
-import gherkin.I18n;
-import gherkin.formatter.FilterFormatter;
-import gherkin.formatter.Formatter;
-import gherkin.formatter.model.Background;
-import gherkin.formatter.model.Examples;
-import gherkin.formatter.model.Feature;
-import gherkin.formatter.model.Scenario;
-import gherkin.formatter.model.ScenarioOutline;
-import gherkin.formatter.model.Step;
-import gherkin.lexer.Encoding;
-import gherkin.parser.Parser;
-import gherkin.util.FixJava;
+import cucumber.util.Encoding;
+import gherkin.AstBuilder;
+import gherkin.Parser;
+import gherkin.ParserException;
+import gherkin.TokenMatcher;
+import gherkin.ast.GherkinDocument;
import java.io.File;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.MessageDigest;
@@ -26,14 +19,12 @@
import java.util.List;
import java.util.Map;
-public class FeatureBuilder implements Formatter {
+public class FeatureBuilder {
private static final Charset UTF8 = Charset.forName("UTF-8");
private final List cucumberFeatures;
private final char fileSeparatorChar;
private final MessageDigest md5;
private final Map pathsByChecksum = new HashMap();
- private CucumberFeature currentCucumberFeature;
- private String featurePath;
public FeatureBuilder(List cucumberFeatures) {
this(cucumberFeatures, File.separatorChar);
@@ -49,69 +40,7 @@ public FeatureBuilder(List cucumberFeatures) {
}
}
- @Override
- public void uri(String uri) {
- this.featurePath = uri;
- }
-
- @Override
- public void feature(Feature feature) {
- currentCucumberFeature = new CucumberFeature(feature, featurePath);
- cucumberFeatures.add(currentCucumberFeature);
- }
-
- @Override
- public void background(Background background) {
- currentCucumberFeature.background(background);
- }
-
- @Override
- public void scenario(Scenario scenario) {
- currentCucumberFeature.scenario(scenario);
- }
-
- @Override
- public void scenarioOutline(ScenarioOutline scenarioOutline) {
- currentCucumberFeature.scenarioOutline(scenarioOutline);
- }
-
- @Override
- public void examples(Examples examples) {
- currentCucumberFeature.examples(examples);
- }
-
- @Override
- public void step(Step step) {
- currentCucumberFeature.step(step);
- }
-
- @Override
- public void eof() {
- }
-
- @Override
- public void syntaxError(String state, String event, List legalEvents, String uri, Integer line) {
- }
-
- @Override
- public void done() {
- }
-
- @Override
- public void close() {
- }
-
- @Override
- public void startOfScenarioLifeCycle(Scenario scenario) {
- // NoOp
- }
-
- @Override
- public void endOfScenarioLifeCycle(Scenario scenario) {
- // NoOp
- }
-
- public void parse(Resource resource, List