From 2f4e9da7e29e36929ded9e06a2fd210842ed9f1e Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Sun, 26 Jan 2020 17:17:57 +0100 Subject: [PATCH 01/28] [Java] Add BeforeAll and AfterAll hooks Work in progress to fix #515. TODO: - [ ] How to deal with failure? - [ ] Test with JUnit4 - [ ] Test with JUnit5 - [ ] Test with TestNG - [ ] Test with CLI - [ ] Invoke around semantics? - [ ] How to report execution results? - [ ] TeamCity Plugin - [ ] Pretty formatter - [ ] Messages/Events --- .../java/io/cucumber/core/backend/Glue.java | 4 ++ .../core/backend/StaticHookDefinition.java | 11 ++++ .../io/cucumber/core/runner/CachingGlue.java | 22 ++++++++ .../java/io/cucumber/core/runner/Runner.java | 9 ++++ .../io/cucumber/core/runtime/Runtime.java | 3 +- .../examples/java/RpnCalculatorSteps.java | 12 +++++ .../src/main/java/io/cucumber/java/After.java | 4 +- .../main/java/io/cucumber/java/AfterAll.java | 25 +++++++++ .../main/java/io/cucumber/java/Before.java | 4 +- .../main/java/io/cucumber/java/BeforeAll.java | 25 +++++++++ .../java/io/cucumber/java/GlueAdaptor.java | 8 ++- .../java/JavaStaticHookDefinition.java | 52 +++++++++++++++++++ .../java/io/cucumber/java/MethodScanner.java | 11 ++-- .../io/cucumber/java/GlueAdaptorTest.java | 11 ++++ .../CucumberEngineExecutionContext.java | 6 ++- .../main/java/io/cucumber/junit/Cucumber.java | 5 +- .../cucumber/testng/TestNGCucumberRunner.java | 2 + 17 files changed, 205 insertions(+), 9 deletions(-) create mode 100644 core/src/main/java/io/cucumber/core/backend/StaticHookDefinition.java create mode 100644 java/src/main/java/io/cucumber/java/AfterAll.java create mode 100644 java/src/main/java/io/cucumber/java/BeforeAll.java create mode 100644 java/src/main/java/io/cucumber/java/JavaStaticHookDefinition.java diff --git a/core/src/main/java/io/cucumber/core/backend/Glue.java b/core/src/main/java/io/cucumber/core/backend/Glue.java index 33abdd2671..8ac042ef85 100644 --- a/core/src/main/java/io/cucumber/core/backend/Glue.java +++ b/core/src/main/java/io/cucumber/core/backend/Glue.java @@ -5,6 +5,10 @@ @API(status = API.Status.STABLE) public interface Glue { + void addBeforeAllHook(StaticHookDefinition beforeAllHook); + + void addAfterAllHook(StaticHookDefinition afterAllHook); + void addStepDefinition(StepDefinition stepDefinition); void addBeforeHook(HookDefinition beforeHook); diff --git a/core/src/main/java/io/cucumber/core/backend/StaticHookDefinition.java b/core/src/main/java/io/cucumber/core/backend/StaticHookDefinition.java new file mode 100644 index 0000000000..7032418d62 --- /dev/null +++ b/core/src/main/java/io/cucumber/core/backend/StaticHookDefinition.java @@ -0,0 +1,11 @@ +package io.cucumber.core.backend; + +import org.apiguardian.api.API; + +@API(status = API.Status.STABLE) +public interface StaticHookDefinition extends Located { + + void execute(); + + int getOrder(); +} diff --git a/core/src/main/java/io/cucumber/core/runner/CachingGlue.java b/core/src/main/java/io/cucumber/core/runner/CachingGlue.java index 9244b3a93a..85e212d21a 100644 --- a/core/src/main/java/io/cucumber/core/runner/CachingGlue.java +++ b/core/src/main/java/io/cucumber/core/runner/CachingGlue.java @@ -9,6 +9,7 @@ import io.cucumber.core.backend.HookDefinition; import io.cucumber.core.backend.ParameterTypeDefinition; import io.cucumber.core.backend.ScenarioScoped; +import io.cucumber.core.backend.StaticHookDefinition; import io.cucumber.core.backend.StepDefinition; import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.gherkin.Step; @@ -45,11 +46,13 @@ final class CachingGlue implements Glue { private final List defaultDataTableCellTransformers = new ArrayList<>(); private final List docStringTypeDefinitions = new ArrayList<>(); + private final List beforeAllHooks = new ArrayList<>(); private final List beforeHooks = new ArrayList<>(); private final List beforeStepHooks = new ArrayList<>(); private final List stepDefinitions = new ArrayList<>(); private final List afterStepHooks = new ArrayList<>(); private final List afterHooks = new ArrayList<>(); + private final List afterAllHooks = new ArrayList<>(); /* * Storing the pattern that matches the step text allows us to cache the rather slow @@ -67,6 +70,17 @@ final class CachingGlue implements Glue { this.bus = bus; } + @Override + public void addBeforeAllHook(StaticHookDefinition beforeAllHook) { + beforeAllHooks.add(beforeAllHook); + + } + + @Override + public void addAfterAllHook(StaticHookDefinition afterAllHook) { + afterAllHooks.add(afterAllHook); + } + @Override public void addStepDefinition(StepDefinition stepDefinition) { stepDefinitions.add(stepDefinition); @@ -126,6 +140,10 @@ public void addDocStringType(DocStringTypeDefinition docStringType) { docStringTypeDefinitions.add(docStringType); } + List getBeforeAllHooks() { + return new ArrayList<>(beforeAllHooks); + } + Collection getBeforeHooks() { return new ArrayList<>(beforeHooks); } @@ -146,6 +164,10 @@ Collection getAfterStepHooks() { return hooks; } + List getAfterAllHooks() { + return new ArrayList<>(afterAllHooks); + } + Collection getParameterTypeDefinitions() { return parameterTypeDefinitions; } diff --git a/core/src/main/java/io/cucumber/core/runner/Runner.java b/core/src/main/java/io/cucumber/core/runner/Runner.java index cfb996a431..8f6f50d989 100644 --- a/core/src/main/java/io/cucumber/core/runner/Runner.java +++ b/core/src/main/java/io/cucumber/core/runner/Runner.java @@ -3,6 +3,7 @@ import io.cucumber.core.api.TypeRegistryConfigurer; import io.cucumber.core.backend.Backend; import io.cucumber.core.backend.ObjectFactory; +import io.cucumber.core.backend.StaticHookDefinition; import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.gherkin.Step; @@ -70,6 +71,14 @@ public void runPickle(Pickle pickle) { } } + public void runBeforeAllHooks(){ + glue.getBeforeAllHooks().forEach(StaticHookDefinition::execute); + } + + public void runAfterAllHooks(){ + glue.getAfterAllHooks().forEach(StaticHookDefinition::execute); + } + private List createSnippetGeneratorsForPickle(StepTypeRegistry stepTypeRegistry) { return backends.stream() .map(Backend::getSnippet) diff --git a/core/src/main/java/io/cucumber/core/runtime/Runtime.java b/core/src/main/java/io/cucumber/core/runtime/Runtime.java index 4c3266d6eb..8d07e32502 100644 --- a/core/src/main/java/io/cucumber/core/runtime/Runtime.java +++ b/core/src/main/java/io/cucumber/core/runtime/Runtime.java @@ -90,6 +90,7 @@ public void run() { for (Feature feature : features) { bus.send(new TestSourceRead(bus.getInstant(), feature.getUri(), feature.getSource())); } + runnerSupplier.get().runBeforeAllHooks(); final List> executingPickles = features.stream() .flatMap(feature -> feature.getPickles().stream()) @@ -119,7 +120,7 @@ public void run() { } else if (thrown.size() > 1) { throw new CompositeCucumberException(thrown); } - + runnerSupplier.get().runAfterAllHooks(); bus.send(new TestRunFinished(bus.getInstant())); } diff --git a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java b/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java index 005a08e0b4..ea222cb0c9 100644 --- a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java +++ b/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java @@ -1,7 +1,9 @@ package io.cucumber.examples.java; import io.cucumber.java.After; +import io.cucumber.java.AfterAll; import io.cucumber.java.Before; +import io.cucumber.java.BeforeAll; import io.cucumber.java.Scenario; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; @@ -14,6 +16,16 @@ public class RpnCalculatorSteps { private RpnCalculator calc; + @BeforeAll + public static void enable_super_math_engine(){ + // System.enableSuperMaths() + } + + @AfterAll + public static void disable_super_math_engine(){ + // System.disableSuperMaths() + } + @Given("a calculator I just turned on") public void a_calculator_I_just_turned_on() { calc = new RpnCalculator(); diff --git a/java/src/main/java/io/cucumber/java/After.java b/java/src/main/java/io/cucumber/java/After.java index eb3e922d1a..0826da0af1 100644 --- a/java/src/main/java/io/cucumber/java/After.java +++ b/java/src/main/java/io/cucumber/java/After.java @@ -23,8 +23,10 @@ String value() default ""; /** - * @return the order in which this hook should run. Higher numbers are run first. + * The order in which this hook should run. Higher numbers are run first. * The default order is 10000. + * + * @return the order in which this hook should run. */ int order() default 10000; } diff --git a/java/src/main/java/io/cucumber/java/AfterAll.java b/java/src/main/java/io/cucumber/java/AfterAll.java new file mode 100644 index 0000000000..66d6e66166 --- /dev/null +++ b/java/src/main/java/io/cucumber/java/AfterAll.java @@ -0,0 +1,25 @@ +package io.cucumber.java; + +import org.apiguardian.api.API; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Executes a method before all scenarios + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@API(status = API.Status.STABLE) +public @interface AfterAll { + + /** + * The order in which this hook should run. Higher numbers are run first. + * The default order is 10000. + * + * @return the order in which this hook should run. + */ + int order() default 10000; +} diff --git a/java/src/main/java/io/cucumber/java/Before.java b/java/src/main/java/io/cucumber/java/Before.java index 2a62590fd6..3f3d09e471 100644 --- a/java/src/main/java/io/cucumber/java/Before.java +++ b/java/src/main/java/io/cucumber/java/Before.java @@ -23,8 +23,10 @@ String value() default ""; /** - * @return the order in which this hook should run. Lower numbers are run first. + * The order in which this hook should run. Lower numbers are run first. * The default order is 10000. + * + * @return the order in which this hook should run. */ int order() default 10000; } diff --git a/java/src/main/java/io/cucumber/java/BeforeAll.java b/java/src/main/java/io/cucumber/java/BeforeAll.java new file mode 100644 index 0000000000..92d9ed7806 --- /dev/null +++ b/java/src/main/java/io/cucumber/java/BeforeAll.java @@ -0,0 +1,25 @@ +package io.cucumber.java; + +import org.apiguardian.api.API; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Executes a method after all scenarios + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@API(status = API.Status.STABLE) +public @interface BeforeAll { + + /** + * The order in which this hook should run. Lower numbers are run first. + * The default order is 10000. + * + * @return the order in which this hook should run. + */ + int order() default 10000; +} diff --git a/java/src/main/java/io/cucumber/java/GlueAdaptor.java b/java/src/main/java/io/cucumber/java/GlueAdaptor.java index 49a970ae80..79d4be492d 100644 --- a/java/src/main/java/io/cucumber/java/GlueAdaptor.java +++ b/java/src/main/java/io/cucumber/java/GlueAdaptor.java @@ -25,10 +25,16 @@ void addDefinition(Method method, Annotation annotation) { Before before = (Before) annotation; String tagExpression = before.value(); glue.addBeforeHook(new JavaHookDefinition(method, tagExpression, before.order(), lookup)); + } else if (annotationType.equals(BeforeAll.class)) { + BeforeAll beforeAll = (BeforeAll) annotation; + glue.addBeforeAllHook(new JavaStaticHookDefinition(method, beforeAll.order(), lookup)); } else if (annotationType.equals(After.class)) { After after = (After) annotation; String tagExpression = after.value(); glue.addAfterHook(new JavaHookDefinition(method, tagExpression, after.order(), lookup)); + } else if (annotationType.equals(AfterAll.class)) { + AfterAll afterAll = (AfterAll) annotation; + glue.addAfterAllHook(new JavaStaticHookDefinition(method, afterAll.order(), lookup)); } else if (annotationType.equals(BeforeStep.class)) { BeforeStep beforeStep = (BeforeStep) annotation; String tagExpression = beforeStep.value(); @@ -58,7 +64,7 @@ void addDefinition(Method method, Annotation annotation) { DefaultDataTableCellTransformer cellTransformer = (DefaultDataTableCellTransformer) annotation; String[] emptyPatterns = cellTransformer.replaceWithEmptyString(); glue.addDefaultDataTableCellTransformer(new JavaDefaultDataTableCellTransformerDefinition(method, lookup, emptyPatterns)); - } else if (annotationType.equals(DocStringType.class)){ + } else if (annotationType.equals(DocStringType.class)) { DocStringType docStringType = (DocStringType) annotation; String contentType = docStringType.contentType(); glue.addDocStringType(new JavaDocStringTypeDefinition(contentType, method, lookup)); diff --git a/java/src/main/java/io/cucumber/java/JavaStaticHookDefinition.java b/java/src/main/java/io/cucumber/java/JavaStaticHookDefinition.java new file mode 100644 index 0000000000..051d96f306 --- /dev/null +++ b/java/src/main/java/io/cucumber/java/JavaStaticHookDefinition.java @@ -0,0 +1,52 @@ +package io.cucumber.java; + +import io.cucumber.core.backend.Lookup; +import io.cucumber.core.backend.StaticHookDefinition; + +import java.lang.reflect.Method; + +import static io.cucumber.java.InvalidMethodSignatureException.builder; +import static java.lang.reflect.Modifier.isStatic; + +final class JavaStaticHookDefinition extends AbstractGlueDefinition implements StaticHookDefinition { + + private final int order; + private final Lookup lookup; + + JavaStaticHookDefinition(Method method, int order, Lookup lookup) { + super(requireValidMethod(method), lookup); + this.order = order; + this.lookup = lookup; + } + + private static Method requireValidMethod(Method method) { + Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length != 0) { + throw createInvalidSignatureException(method); + } + + if (!isStatic(method.getModifiers())) { + throw createInvalidSignatureException(method); + } + + return method; + } + + private static InvalidMethodSignatureException createInvalidSignatureException(Method method) { + return builder(method) + .addAnnotation(BeforeAll.class) + .addAnnotation(AfterAll.class) + .addSignature("public static void before_or_after_all()") + .build(); + } + + @Override + public void execute() { + Invoker.invoke(this, lookup.getInstance(method.getDeclaringClass()), method); + } + + @Override + public int getOrder() { + return order; + } +} diff --git a/java/src/main/java/io/cucumber/java/MethodScanner.java b/java/src/main/java/io/cucumber/java/MethodScanner.java index abbe7c558c..ddac29af96 100644 --- a/java/src/main/java/io/cucumber/java/MethodScanner.java +++ b/java/src/main/java/io/cucumber/java/MethodScanner.java @@ -2,10 +2,12 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.function.BiConsumer; import static io.cucumber.java.InvalidMethodException.createInvalidMethodException; +import static java.lang.reflect.Modifier.isAbstract; +import static java.lang.reflect.Modifier.isPublic; +import static java.lang.reflect.Modifier.isStatic; final class MethodScanner { @@ -27,8 +29,9 @@ static void scan(Class aClass, BiConsumer consumer) { } private static boolean isInstantiable(Class clazz) { - boolean isNonStaticInnerClass = !Modifier.isStatic(clazz.getModifiers()) && clazz.getEnclosingClass() != null; - return Modifier.isPublic(clazz.getModifiers()) && !Modifier.isAbstract(clazz.getModifiers()) && !isNonStaticInnerClass; + return isPublic(clazz.getModifiers()) + && !isAbstract(clazz.getModifiers()) + && (isStatic(clazz.getModifiers()) || clazz.getEnclosingClass() == null); } private static void scan(BiConsumer consumer, Class aClass, Method method) { @@ -59,7 +62,9 @@ private static void validateMethod(Class glueCodeClass, Method method) { private static boolean isHookAnnotation(Annotation annotation) { Class annotationClass = annotation.annotationType(); return annotationClass.equals(Before.class) + || annotationClass.equals(BeforeAll.class) || annotationClass.equals(After.class) + || annotationClass.equals(AfterAll.class) || annotationClass.equals(BeforeStep.class) || annotationClass.equals(AfterStep.class) || annotationClass.equals(ParameterType.class) diff --git a/java/src/test/java/io/cucumber/java/GlueAdaptorTest.java b/java/src/test/java/io/cucumber/java/GlueAdaptorTest.java index 9ed3617408..55e30c42ce 100644 --- a/java/src/test/java/io/cucumber/java/GlueAdaptorTest.java +++ b/java/src/test/java/io/cucumber/java/GlueAdaptorTest.java @@ -9,6 +9,7 @@ import io.cucumber.core.backend.HookDefinition; import io.cucumber.core.backend.Lookup; import io.cucumber.core.backend.ParameterTypeDefinition; +import io.cucumber.core.backend.StaticHookDefinition; import io.cucumber.core.backend.StepDefinition; import io.cucumber.java.en.Given; import org.hamcrest.CustomTypeSafeMatcher; @@ -61,6 +62,16 @@ protected boolean matchesSafely(StepDefinition item) { private HookDefinition beforeHook; private DocStringTypeDefinition docStringTypeDefinition; private final Glue container = new Glue() { + @Override + public void addBeforeAllHook(StaticHookDefinition beforeAllHook) { + //TODO + } + + @Override + public void addAfterAllHook(StaticHookDefinition afterAllHook) { + //TODO + } + @Override public void addStepDefinition(StepDefinition stepDefinition) { GlueAdaptorTest.this.stepDefinitions.add(stepDefinition); diff --git a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java index a5521b267f..19d0ecdaff 100644 --- a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java +++ b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java @@ -62,8 +62,10 @@ public final class CucumberEngineExecutionContext implements EngineExecutionCont } void startTestRun() { - logger.debug(() -> "Sending run test started event"); + logger.debug(() -> "running before all hooks"); bus.send(new TestRunStarted(bus.getInstant())); + logger.debug(() -> "Sending run test started event"); + runnerSupplier.get().runBeforeAllHooks(); } void beforeFeature(Feature feature) { @@ -83,6 +85,8 @@ void runTestCase(Pickle pickle) { } void finishTestRun() { + logger.debug(() -> "running after all hooks"); + runnerSupplier.get().runAfterAllHooks(); logger.debug(() -> "Sending test run finished event"); bus.send(new TestRunFinished(bus.getInstant())); } diff --git a/junit/src/main/java/io/cucumber/junit/Cucumber.java b/junit/src/main/java/io/cucumber/junit/Cucumber.java index 0606ac87ee..48419a2ce6 100644 --- a/junit/src/main/java/io/cucumber/junit/Cucumber.java +++ b/junit/src/main/java/io/cucumber/junit/Cucumber.java @@ -89,6 +89,7 @@ public final class Cucumber extends ParentRunner> { private final EventBus bus; private final List features; private final Plugins plugins; + private final ThreadLocalRunnerSupplier runnerSupplier; private boolean multiThreadingAssumed = false; @@ -162,7 +163,7 @@ public Cucumber(Class clazz) throws InitializationError { ObjectFactorySupplier objectFactorySupplier = new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader); BackendSupplier backendSupplier = new BackendServiceLoader(clazz::getClassLoader, objectFactorySupplier); TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier = new ScanningTypeRegistryConfigurerSupplier(classLoader, runtimeOptions); - ThreadLocalRunnerSupplier runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, bus, backendSupplier, objectFactorySupplier, typeRegistryConfigurerSupplier); + this.runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, bus, backendSupplier, objectFactorySupplier, typeRegistryConfigurerSupplier); Predicate filters = new Filters(runtimeOptions); this.children = features.stream() .map(feature -> FeatureRunner.create(feature, filters, runnerSupplier, junitOptions)) @@ -216,7 +217,9 @@ public void evaluate() throws Throwable { for (Feature feature : features) { bus.send(new TestSourceRead(bus.getInstant(), feature.getUri(), feature.getSource())); } + runnerSupplier.get().runBeforeAllHooks(); runFeatures.evaluate(); + runnerSupplier.get().runAfterAllHooks(); bus.send(new TestRunFinished(bus.getInstant())); } diff --git a/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java b/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java index 17fa7a9a96..f109b2d803 100644 --- a/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java +++ b/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java @@ -128,6 +128,7 @@ public void runScenario(io.cucumber.testng.Pickle pickle) throws Throwable { } public void finish() { + runnerSupplier.get().runAfterAllHooks(); bus.send(new TestRunFinished(bus.getInstant())); } @@ -162,6 +163,7 @@ private List getFeatures() { features = featureSupplier.get(); bus.send(new TestRunStarted(bus.getInstant())); features.forEach(feature -> bus.send(new TestSourceRead(bus.getInstant(), feature.getUri(), feature.getSource()))); + runnerSupplier.get().runBeforeAllHooks(); } return features; } From c8d2a8089224528ff99fbc9290dbb6620d2c906f Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Mon, 8 Mar 2021 18:02:03 +0100 Subject: [PATCH 02/28] Require void return type and invoke without object --- .../io/cucumber/java/JavaHookDefinition.java | 5 ++ .../java/JavaStaticHookDefinition.java | 10 ++- .../cucumber/java/JavaHookDefinitionTest.java | 19 +++++ .../java/JavaStaticHookDefinitionTest.java | 81 +++++++++++++++++++ 4 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 java/src/test/java/io/cucumber/java/JavaStaticHookDefinitionTest.java diff --git a/java/src/main/java/io/cucumber/java/JavaHookDefinition.java b/java/src/main/java/io/cucumber/java/JavaHookDefinition.java index 825daf46c7..3fcc2e1e52 100644 --- a/java/src/main/java/io/cucumber/java/JavaHookDefinition.java +++ b/java/src/main/java/io/cucumber/java/JavaHookDefinition.java @@ -5,6 +5,7 @@ import io.cucumber.core.backend.TestCaseState; import java.lang.reflect.Method; +import java.lang.reflect.Type; import static io.cucumber.java.InvalidMethodSignatureException.builder; import static java.util.Objects.requireNonNull; @@ -33,6 +34,10 @@ private static Method requireValidMethod(Method method) { } } + Type returnType = method.getGenericReturnType(); + if (!Void.class.equals(returnType) && !void.class.equals(returnType)) { + throw createInvalidSignatureException(method); + } return method; } diff --git a/java/src/main/java/io/cucumber/java/JavaStaticHookDefinition.java b/java/src/main/java/io/cucumber/java/JavaStaticHookDefinition.java index 3ff9fb80ed..1fc61c8f02 100644 --- a/java/src/main/java/io/cucumber/java/JavaStaticHookDefinition.java +++ b/java/src/main/java/io/cucumber/java/JavaStaticHookDefinition.java @@ -4,6 +4,7 @@ import io.cucumber.core.backend.StaticHookDefinition; import java.lang.reflect.Method; +import java.lang.reflect.Type; import static io.cucumber.java.InvalidMethodSignatureException.builder; import static java.lang.reflect.Modifier.isStatic; @@ -11,12 +12,10 @@ final class JavaStaticHookDefinition extends AbstractGlueDefinition implements StaticHookDefinition { private final int order; - private final Lookup lookup; JavaStaticHookDefinition(Method method, int order, Lookup lookup) { super(requireValidMethod(method), lookup); this.order = order; - this.lookup = lookup; } private static Method requireValidMethod(Method method) { @@ -29,6 +28,11 @@ private static Method requireValidMethod(Method method) { throw createInvalidSignatureException(method); } + Type returnType = method.getGenericReturnType(); + if (!Void.class.equals(returnType) && !void.class.equals(returnType)) { + throw createInvalidSignatureException(method); + } + return method; } @@ -42,7 +46,7 @@ private static InvalidMethodSignatureException createInvalidSignatureException(M @Override public void execute() { - Invoker.invoke(this, lookup.getInstance(method.getDeclaringClass()), method); + invokeMethod(); } @Override diff --git a/java/src/test/java/io/cucumber/java/JavaHookDefinitionTest.java b/java/src/test/java/io/cucumber/java/JavaHookDefinitionTest.java index 15f7829527..16aaf72ab6 100644 --- a/java/src/test/java/io/cucumber/java/JavaHookDefinitionTest.java +++ b/java/src/test/java/io/cucumber/java/JavaHookDefinitionTest.java @@ -103,4 +103,23 @@ public void too_many_parameters(Scenario arg1, String arg2) { } + @Test + void fails_with_non_void_return_type() throws Throwable { + Method method = JavaHookDefinitionTest.class.getMethod("string_return_type"); + InvalidMethodSignatureException exception = assertThrows( + InvalidMethodSignatureException.class, + () -> new JavaHookDefinition(method, "", 0, lookup)); + assertThat(exception.getMessage(), startsWith("" + + "A method annotated with Before, After, BeforeStep or AfterStep must have one of these signatures:\n" + + " * public void before_or_after(io.cucumber.java.Scenario scenario)\n" + + " * public void before_or_after()\n" + + "at io.cucumber.java.JavaHookDefinitionTest.string_return_type()\n")); + } + + @Before + public String string_return_type() { + invoked = true; + return ""; + } + } diff --git a/java/src/test/java/io/cucumber/java/JavaStaticHookDefinitionTest.java b/java/src/test/java/io/cucumber/java/JavaStaticHookDefinitionTest.java new file mode 100644 index 0000000000..dd3bb0459e --- /dev/null +++ b/java/src/test/java/io/cucumber/java/JavaStaticHookDefinitionTest.java @@ -0,0 +1,81 @@ +package io.cucumber.java; + +import io.cucumber.core.backend.Lookup; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; + +import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SuppressWarnings({ "WeakerAccess" }) +public class JavaStaticHookDefinitionTest { + + private final Lookup lookup = new Lookup() { + + @Override + @SuppressWarnings("unchecked") + public T getInstance(Class glueClass) { + return (T) JavaStaticHookDefinitionTest.this; + } + }; + + private static boolean invoked; + + @BeforeEach + void reset() { + invoked = false; + } + + @Test + void can_create_with_no_argument() throws Throwable { + Method method = JavaStaticHookDefinitionTest.class.getMethod("no_arguments"); + JavaStaticHookDefinition definition = new JavaStaticHookDefinition(method, 0, lookup); + definition.execute(); + assertTrue(invoked); + } + + @BeforeAll + public static void no_arguments() { + invoked = true; + } + + @Test + void fails_with_arguments() throws Throwable { + Method method = JavaStaticHookDefinitionTest.class.getMethod("single_argument", Scenario.class); + InvalidMethodSignatureException exception = assertThrows( + InvalidMethodSignatureException.class, + () -> new JavaStaticHookDefinition(method, 0, lookup)); + assertThat(exception.getMessage(), startsWith("" + + "A method annotated with BeforeAll or AfterAll must have one of these signatures:\n" + + " * public static void before_or_after_all()\n" + + "at io.cucumber.java.JavaStaticHookDefinitionTest.single_argument(io.cucumber.java.Scenario)\n")); + } + + @Before + public void single_argument(Scenario scenario) { + invoked = true; + } + + @Test + void fails_with_non_void_return_type() throws Throwable { + Method method = JavaStaticHookDefinitionTest.class.getMethod("string_return_type"); + InvalidMethodSignatureException exception = assertThrows( + InvalidMethodSignatureException.class, + () -> new JavaStaticHookDefinition(method, 0, lookup)); + assertThat(exception.getMessage(), startsWith("" + + "A method annotated with BeforeAll or AfterAll must have one of these signatures:\n" + + " * public static void before_or_after_all()\n" + + "at io.cucumber.java.JavaStaticHookDefinitionTest.string_return_type()\n")); + } + + @Before + public String string_return_type() { + invoked = true; + return ""; + } + +} From fbaeeac0807e5b38e6cb33be317b7ccd9b0ef3c9 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Mon, 8 Mar 2021 19:30:42 +0100 Subject: [PATCH 03/28] WIP Invoke around semantics --- .../java/io/cucumber/core/runner/Runner.java | 35 ++++++-- .../runtime/CucumberExecutionContext.java | 23 ++++- .../io/cucumber/core/runtime/Runtime.java | 25 ++++-- .../engine/CucumberEngineDescriptor.java | 13 ++- .../CucumberEngineExecutionContext.java | 8 ++ .../main/java/io/cucumber/junit/Cucumber.java | 90 ++++++++++++++----- .../java/io/cucumber/junit/FeatureRunner.java | 15 ++-- .../java/io/cucumber/junit/PickleRunners.java | 45 +++++----- .../io/cucumber/junit/FeatureRunnerTest.java | 15 +++- ...ickleRunnerWithNoStepDescriptionsTest.java | 19 +++- .../PickleRunnerWithStepDescriptionsTest.java | 25 ++++-- .../cucumber/testng/TestNGCucumberRunner.java | 7 +- 12 files changed, 241 insertions(+), 79 deletions(-) diff --git a/core/src/main/java/io/cucumber/core/runner/Runner.java b/core/src/main/java/io/cucumber/core/runner/Runner.java index 3bf18e8d8d..3c4ed4b392 100644 --- a/core/src/main/java/io/cucumber/core/runner/Runner.java +++ b/core/src/main/java/io/cucumber/core/runner/Runner.java @@ -2,9 +2,12 @@ import io.cucumber.core.api.TypeRegistryConfigurer; import io.cucumber.core.backend.Backend; +import io.cucumber.core.backend.CucumberBackendException; +import io.cucumber.core.backend.CucumberInvocationTargetException; import io.cucumber.core.backend.ObjectFactory; import io.cucumber.core.backend.StaticHookDefinition; import io.cucumber.core.eventbus.EventBus; +import io.cucumber.core.exception.CucumberException; import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.gherkin.Step; import io.cucumber.core.logging.Logger; @@ -24,6 +27,8 @@ import java.util.Objects; import java.util.stream.Collectors; +import static io.cucumber.core.exception.ExceptionUtils.throwAsUncheckedException; +import static io.cucumber.core.runner.StackManipulation.removeFrameworkFrames; import static java.util.Collections.emptyList; public final class Runner { @@ -66,7 +71,7 @@ public void runPickle(Pickle pickle) { snippetGenerators = createSnippetGeneratorsForPickle(stepTypeRegistry); buildBackendWorlds(); // Java8 step definitions will be added to the - // glue here + // glue here glue.prepareGlue(stepTypeRegistry); @@ -79,11 +84,29 @@ public void runPickle(Pickle pickle) { } public void runBeforeAllHooks() { - glue.getBeforeAllHooks().forEach(StaticHookDefinition::execute); + glue.getBeforeAllHooks().forEach(this::executeHook); } public void runAfterAllHooks() { - glue.getAfterAllHooks().forEach(StaticHookDefinition::execute); + glue.getAfterAllHooks().forEach(this::executeHook); + } + + private void executeHook(StaticHookDefinition hookDefinition) { + if (runnerOptions.isDryRun()) { + return; + } + try { + hookDefinition.execute(); + } catch (CucumberBackendException e) { + CucumberException exception = new CucumberException(String.format("" + + "Could not invoke hook defined at '%s'.\n" + + "It appears there was a problem with the hook definition.", + hookDefinition.getLocation()), e); + throwAsUncheckedException(exception); + } catch (CucumberInvocationTargetException e) { + Throwable throwable = removeFrameworkFrames(e); + throwAsUncheckedException(throwable); + } } private List createSnippetGeneratorsForPickle(StepTypeRegistry stepTypeRegistry) { @@ -114,7 +137,7 @@ private void buildBackendWorlds() { private TestCase createTestCaseForPickle(Pickle pickle) { if (pickle.getSteps().isEmpty()) { return new TestCase(bus.generateId(), emptyList(), emptyList(), emptyList(), pickle, - runnerOptions.isDryRun()); + runnerOptions.isDryRun()); } List testSteps = createTestStepsForPickleSteps(pickle); @@ -138,7 +161,7 @@ private List createTestStepsForPickleSteps(Pickle pickle) { List afterStepHookSteps = createAfterStepHooks(pickle.getTags()); List beforeStepHookSteps = createBeforeStepHooks(pickle.getTags()); testSteps.add(new PickleStepTestStep(bus.generateId(), pickle.getUri(), step, beforeStepHookSteps, - afterStepHookSteps, match)); + afterStepHookSteps, match)); } return testSteps; @@ -174,7 +197,7 @@ private void emitSnippetSuggestedEvent(Pickle pickle, Step step) { Location scenarioLocation = pickle.getLocation(); Location stepLocation = step.getLocation(); SnippetsSuggestedEvent event = new SnippetsSuggestedEvent(bus.getInstant(), pickle.getUri(), scenarioLocation, - stepLocation, suggestion); + stepLocation, suggestion); bus.send(event); } diff --git a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java index ce67361b39..a09bc3fc6d 100644 --- a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java +++ b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java @@ -50,9 +50,9 @@ public CucumberExecutionContext(EventBus bus, ExitStatus exitStatus, RunnerSuppl public void startTestRun() { emitMeta(); emitTestRunStarted(); - runnerSupplier.get().runBeforeAllHooks(); } + private void emitMeta() { bus.send(Envelope.newBuilder() .setMeta(createMeta("cucumber-jvm", VERSION, System.getenv())) @@ -69,8 +69,27 @@ private void emitTestRunStarted() { .build()); } + public void runBeforeAllHooks() { + try { + runnerSupplier.get().runBeforeAllHooks(); + } catch (Throwable e) { + log.error(e, () -> "Unable to start Cucumber"); + thrown.add(e); + throw e; + } + } + + public void runAfterAllHooks() { + try { + runnerSupplier.get().runAfterAllHooks(); + } catch (Throwable e) { + log.error(e, () -> "Unable to start Cucumber"); + thrown.add(e); + throw e; + } + } + public void finishTestRun() { - runnerSupplier.get().runAfterAllHooks(); log.debug(() -> "Sending test run finished event"); CucumberException cucumberException = getException(); emitTestRunFinished(cucumberException); diff --git a/core/src/main/java/io/cucumber/core/runtime/Runtime.java b/core/src/main/java/io/cucumber/core/runtime/Runtime.java index 2e9630be39..920c2224ba 100644 --- a/core/src/main/java/io/cucumber/core/runtime/Runtime.java +++ b/core/src/main/java/io/cucumber/core/runtime/Runtime.java @@ -75,9 +75,24 @@ public static Builder builder() { public void run() { context.startTestRun(); - final List features = featureSupplier.get(); + try { + context.runBeforeAllHooks(); + runFeatures(); + context.runAfterAllHooks(); + } finally { + context.finishTestRun(); + } + + CucumberException exception = context.getException(); + if (exception != null) { + throw exception; + } + } + + private void runFeatures() { + List features = featureSupplier.get(); features.forEach(context::beforeFeature); - final List> executingPickles = features.stream() + List> executingPickles = features.stream() .flatMap(feature -> feature.getPickles().stream()) .filter(filter) .collect(collectingAndThen(toList(), @@ -98,12 +113,6 @@ public void run() { log.debug(e, () -> "Interrupted while executing pickle"); } } - context.finishTestRun(); - - CucumberException exception = context.getException(); - if (exception != null) { - throw exception; - } } private Runnable execute(Pickle pickle) { diff --git a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineDescriptor.java b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineDescriptor.java index 3a471046f0..f689e0abbd 100644 --- a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineDescriptor.java +++ b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineDescriptor.java @@ -27,13 +27,24 @@ private static void recursivelyMerge(TestDescriptor descriptor, TestDescriptor p } @Override - public CucumberEngineExecutionContext before(CucumberEngineExecutionContext context) { + public CucumberEngineExecutionContext prepare(CucumberEngineExecutionContext context) { context.startTestRun(); return context; } + @Override + public CucumberEngineExecutionContext before(CucumberEngineExecutionContext context) { + context.runBeforeAllHooks(); + return context; + } + @Override public void after(CucumberEngineExecutionContext context) { + context.runAfterAllHooks(); + } + + @Override + public void cleanUp(CucumberEngineExecutionContext context) { context.finishTestRun(); } diff --git a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java index 870f818991..9c144efcaf 100644 --- a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java +++ b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java @@ -79,6 +79,10 @@ void startTestRun() { context.startTestRun(); } + public void runBeforeAllHooks() { + context.runBeforeAllHooks(); + } + public void beforeFeature(Feature feature) { context.beforeFeature(feature); } @@ -94,6 +98,10 @@ void runTestCase(Pickle pickle) { }); } + public void runAfterAllHooks() { + context.runAfterAllHooks(); + } + public void finishTestRun() { context.finishTestRun(); } diff --git a/junit/src/main/java/io/cucumber/junit/Cucumber.java b/junit/src/main/java/io/cucumber/junit/Cucumber.java index aac4c4bd3d..88e10616c4 100644 --- a/junit/src/main/java/io/cucumber/junit/Cucumber.java +++ b/junit/src/main/java/io/cucumber/junit/Cucumber.java @@ -54,14 +54,12 @@ * A class annotated with {@code @RunWith(Cucumber.class)} will run feature * files as junit tests. In general, the runner class should be empty without * any fields or methods. For example:
- * *
  * @RunWith(Cucumber.class)
  * @CucumberOptions(plugin = "pretty")
  * public class RunCucumberTest {
  * }
  * 
- * *
*

* By default Cucumber will look for {@code .feature} and glue files on the @@ -101,9 +99,9 @@ public final class Cucumber extends ParentRunner> { /** * Constructor called by JUnit. * - * @param clazz the class with - * the @RunWith - * annotation. + * @param clazz the class with + * the @RunWith + * annotation. * @throws org.junit.runners.model.InitializationError if there is another * problem */ @@ -154,7 +152,7 @@ public Cucumber(Class clazz) throws InitializationError { FeatureParser parser = new FeatureParser(bus::generateId); Supplier classLoader = ClassLoaders::getDefaultClassLoader; FeaturePathFeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(classLoader, runtimeOptions, - parser); + parser); this.features = featureSupplier.get(); // Create plugins after feature parsing to avoid the creation of empty @@ -164,13 +162,13 @@ public Cucumber(Class clazz) throws InitializationError { this.plugins.addPlugin(exitStatus); ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, - runtimeOptions); + runtimeOptions); ObjectFactorySupplier objectFactorySupplier = new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader); BackendSupplier backendSupplier = new BackendServiceLoader(clazz::getClassLoader, objectFactorySupplier); TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier = new ScanningTypeRegistryConfigurerSupplier( - classLoader, runtimeOptions); + classLoader, runtimeOptions); ThreadLocalRunnerSupplier runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, bus, backendSupplier, - objectFactorySupplier, typeRegistryConfigurerSupplier); + objectFactorySupplier, typeRegistryConfigurerSupplier); this.context = new CucumberExecutionContext(bus, exitStatus, runnerSupplier); Predicate filters = new Filters(runtimeOptions); @@ -179,7 +177,7 @@ public Cucumber(Class clazz) throws InitializationError { this.children = features.stream() .map(feature -> { Integer uniqueSuffix = uniqueSuffix(groupedByName, feature, Feature::getName); - return FeatureRunner.create(feature, uniqueSuffix, filters, runnerSupplier, junitOptions); + return FeatureRunner.create(feature, uniqueSuffix, filters, context, junitOptions); }) .filter(runner -> !runner.isEmpty()) .collect(toList()); @@ -202,8 +200,15 @@ protected void runChild(ParentRunner child, RunNotifier notifier) { @Override protected Statement childrenInvoker(RunNotifier notifier) { - Statement runFeatures = super.childrenInvoker(notifier); - return new RunCucumber(runFeatures); + Statement statement = super.childrenInvoker(notifier); + + statement = new RunBeforeAllHooks(statement); + statement = new RunAfterAllHooks(statement); + + statement = new StartTestRun(statement); + statement = new FinishTestRun(statement); + + return statement; } @Override @@ -212,12 +217,11 @@ public void setScheduler(RunnerScheduler scheduler) { multiThreadingAssumed = true; } - class RunCucumber extends Statement { + private class StartTestRun extends Statement { + private final Statement next; - private final Statement runFeatures; - - RunCucumber(Statement runFeatures) { - this.runFeatures = runFeatures; + public StartTestRun(Statement next) { + this.next = next; } @Override @@ -227,12 +231,24 @@ public void evaluate() throws Throwable { } else { plugins.setEventBusOnEventListenerPlugins(bus); } - context.startTestRun(); - features.forEach(context::beforeFeature); + next.evaluate(); + } + + } + + private class FinishTestRun extends Statement { + private final Statement next; + + public FinishTestRun(Statement next) { + this.next = next; + } + + @Override + public void evaluate() throws Throwable { try { - runFeatures.evaluate(); + next.evaluate(); } finally { context.finishTestRun(); } @@ -240,4 +256,38 @@ public void evaluate() throws Throwable { } + private class RunBeforeAllHooks extends Statement { + private final Statement next; + + public RunBeforeAllHooks(Statement next) { + this.next = next; + } + + @Override + public void evaluate() throws Throwable { + context.runBeforeAllHooks(); + features.forEach(context::beforeFeature); + next.evaluate(); + } + + } + + private class RunAfterAllHooks extends Statement { + private final Statement next; + + public RunAfterAllHooks(Statement next) { + this.next = next; + } + + @Override + public void evaluate() throws Throwable { + try { + next.evaluate(); + } finally { + context.runAfterAllHooks(); + } + } + + } + } diff --git a/junit/src/main/java/io/cucumber/junit/FeatureRunner.java b/junit/src/main/java/io/cucumber/junit/FeatureRunner.java index 0911fe0e20..4debe43579 100644 --- a/junit/src/main/java/io/cucumber/junit/FeatureRunner.java +++ b/junit/src/main/java/io/cucumber/junit/FeatureRunner.java @@ -3,7 +3,7 @@ import io.cucumber.core.exception.CucumberException; import io.cucumber.core.gherkin.Feature; import io.cucumber.core.gherkin.Pickle; -import io.cucumber.core.runtime.RunnerSupplier; +import io.cucumber.core.runtime.CucumberExecutionContext; import io.cucumber.junit.PickleRunners.PickleRunner; import org.junit.runner.Description; import org.junit.runner.notification.Failure; @@ -30,10 +30,11 @@ final class FeatureRunner extends ParentRunner { private final Feature feature; private final JUnitOptions options; private final Integer uniqueSuffix; + private final CucumberExecutionContext context; private Description description; private FeatureRunner( - Feature feature, Integer uniqueSuffix, Predicate filter, RunnerSupplier runners, + Feature feature, Integer uniqueSuffix, Predicate filter, CucumberExecutionContext context, JUnitOptions options ) throws InitializationError { @@ -41,6 +42,7 @@ private FeatureRunner( this.feature = feature; this.uniqueSuffix = uniqueSuffix; this.options = options; + this.context = context; Map> groupedByName = feature.getPickles().stream() .collect(groupingBy(Pickle::getName)); @@ -51,18 +53,18 @@ private FeatureRunner( String featureName = getName(); Integer exampleId = uniqueSuffix(groupedByName, pickle, Pickle::getName); return options.stepNotifications() - ? withStepDescriptions(runners, pickle, exampleId, options) - : withNoStepDescriptions(featureName, runners, pickle, exampleId, options); + ? withStepDescriptions(context, pickle, exampleId, options) + : withNoStepDescriptions(featureName, context, pickle, exampleId, options); }) .collect(toList()); } static FeatureRunner create( - Feature feature, Integer uniqueSuffix, Predicate filter, RunnerSupplier runners, + Feature feature, Integer uniqueSuffix, Predicate filter, CucumberExecutionContext context, JUnitOptions options ) { try { - return new FeatureRunner(feature, uniqueSuffix, filter, runners, options); + return new FeatureRunner(feature, uniqueSuffix, filter, context, options); } catch (InitializationError e) { throw new CucumberException("Failed to create scenario runner", e); } @@ -132,6 +134,7 @@ protected Description describeChild(PickleRunner child) { protected void runChild(PickleRunner child, RunNotifier notifier) { notifier.fireTestStarted(describeChild(child)); try { + context.beforeFeature(feature); child.run(notifier); } catch (Throwable e) { notifier.fireTestFailure(new Failure(describeChild(child), e)); diff --git a/junit/src/main/java/io/cucumber/junit/PickleRunners.java b/junit/src/main/java/io/cucumber/junit/PickleRunners.java index 11074ed685..c3935f59ec 100644 --- a/junit/src/main/java/io/cucumber/junit/PickleRunners.java +++ b/junit/src/main/java/io/cucumber/junit/PickleRunners.java @@ -2,8 +2,7 @@ import io.cucumber.core.exception.CucumberException; import io.cucumber.core.gherkin.Pickle; -import io.cucumber.core.runner.Runner; -import io.cucumber.core.runtime.RunnerSupplier; +import io.cucumber.core.runtime.CucumberExecutionContext; import io.cucumber.plugin.event.Step; import org.junit.runner.Description; import org.junit.runner.notification.RunNotifier; @@ -22,20 +21,20 @@ final class PickleRunners { static PickleRunner withStepDescriptions( - RunnerSupplier runnerSupplier, Pickle pickle, Integer uniqueSuffix, JUnitOptions options + CucumberExecutionContext context, Pickle pickle, Integer uniqueSuffix, JUnitOptions options ) { try { - return new WithStepDescriptions(runnerSupplier, pickle, uniqueSuffix, options); + return new WithStepDescriptions(context, pickle, uniqueSuffix, options); } catch (InitializationError e) { throw new CucumberException("Failed to create scenario runner", e); } } static PickleRunner withNoStepDescriptions( - String featureName, RunnerSupplier runnerSupplier, Pickle pickle, Integer uniqueSuffix, + String featureName, CucumberExecutionContext context, Pickle pickle, Integer uniqueSuffix, JUnitOptions jUnitOptions ) { - return new NoStepDescriptions(featureName, runnerSupplier, pickle, uniqueSuffix, jUnitOptions); + return new NoStepDescriptions(featureName, context, pickle, uniqueSuffix, jUnitOptions); } interface PickleRunner { @@ -50,7 +49,7 @@ interface PickleRunner { static class WithStepDescriptions extends ParentRunner implements PickleRunner { - private final RunnerSupplier runnerSupplier; + private final CucumberExecutionContext context; private final Pickle pickle; private final JUnitOptions jUnitOptions; private final Map stepDescriptions = new HashMap<>(); @@ -58,11 +57,11 @@ static class WithStepDescriptions extends ParentRunner implements PickleRu private Description description; WithStepDescriptions( - RunnerSupplier runnerSupplier, Pickle pickle, Integer uniqueSuffix, JUnitOptions jUnitOptions + CucumberExecutionContext context, Pickle pickle, Integer uniqueSuffix, JUnitOptions jUnitOptions ) throws InitializationError { super((Class) null); - this.runnerSupplier = runnerSupplier; + this.context = context; this.pickle = pickle; this.jUnitOptions = jUnitOptions; this.uniqueSuffix = uniqueSuffix; @@ -104,11 +103,12 @@ public Description describeChild(Step step) { @Override public void run(final RunNotifier notifier) { // Possibly invoked by a thread other then the creating thread - Runner runner = runnerSupplier.get(); - JUnitReporter jUnitReporter = new JUnitReporter(runner.getBus(), jUnitOptions); - jUnitReporter.startExecutionUnit(this, notifier); - runner.runPickle(pickle); - jUnitReporter.finishExecutionUnit(); + context.runTestCase(runner -> { + JUnitReporter jUnitReporter = new JUnitReporter(runner.getBus(), jUnitOptions); + jUnitReporter.startExecutionUnit(this, notifier); + runner.runPickle(pickle); + jUnitReporter.finishExecutionUnit(); + }); } @Override @@ -125,18 +125,18 @@ protected void runChild(Step step, RunNotifier notifier) { static final class NoStepDescriptions implements PickleRunner { private final String featureName; - private final RunnerSupplier runnerSupplier; + private final CucumberExecutionContext context; private final Pickle pickle; private final JUnitOptions jUnitOptions; private final Integer uniqueSuffix; private Description description; NoStepDescriptions( - String featureName, RunnerSupplier runnerSupplier, Pickle pickle, Integer uniqueSuffix, + String featureName, CucumberExecutionContext context, Pickle pickle, Integer uniqueSuffix, JUnitOptions jUnitOptions ) { this.featureName = featureName; - this.runnerSupplier = runnerSupplier; + this.context = context; this.pickle = pickle; this.jUnitOptions = jUnitOptions; this.uniqueSuffix = uniqueSuffix; @@ -145,11 +145,12 @@ static final class NoStepDescriptions implements PickleRunner { @Override public void run(final RunNotifier notifier) { // Possibly invoked by a thread other then the creating thread - Runner runner = runnerSupplier.get(); - JUnitReporter jUnitReporter = new JUnitReporter(runner.getBus(), jUnitOptions); - jUnitReporter.startExecutionUnit(this, notifier); - runner.runPickle(pickle); - jUnitReporter.finishExecutionUnit(); + context.runTestCase(runner -> { + JUnitReporter jUnitReporter = new JUnitReporter(runner.getBus(), jUnitOptions); + jUnitReporter.startExecutionUnit(this, notifier); + runner.runPickle(pickle); + jUnitReporter.finishExecutionUnit(); + }); } @Override diff --git a/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java b/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java index e62a9af605..b7dacb58a3 100644 --- a/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java +++ b/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java @@ -6,6 +6,8 @@ import io.cucumber.core.options.RuntimeOptions; import io.cucumber.core.options.RuntimeOptionsBuilder; import io.cucumber.core.runtime.BackendSupplier; +import io.cucumber.core.runtime.CucumberExecutionContext; +import io.cucumber.core.runtime.ExitStatus; import io.cucumber.core.runtime.ObjectFactoryServiceLoader; import io.cucumber.core.runtime.ObjectFactorySupplier; import io.cucumber.core.runtime.RunnerSupplier; @@ -123,7 +125,8 @@ public Instant instant() { classLoader, runtimeOptions); ThreadLocalRunnerSupplier runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, bus, backendSupplier, objectFactory, typeRegistrySupplier); - return FeatureRunner.create(feature, null, filters, runnerSupplier, junitOption); + CucumberExecutionContext context = new CucumberExecutionContext(bus, new ExitStatus(runtimeOptions), runnerSupplier); + return FeatureRunner.create(feature, null, filters, context, junitOption); } @Test @@ -366,8 +369,10 @@ void should_notify_of_failure_to_create_runners_and_request_test_execution_to_st RunnerSupplier runnerSupplier = () -> { throw illegalStateException; }; - - FeatureRunner featureRunner = FeatureRunner.create(feature, null, filters, runnerSupplier, new JUnitOptions()); + TimeServiceEventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); + RuntimeOptions options = RuntimeOptions.defaultOptions(); + CucumberExecutionContext context = new CucumberExecutionContext(bus, new ExitStatus(options), runnerSupplier); + FeatureRunner featureRunner = FeatureRunner.create(feature, null, filters, context, new JUnitOptions()); RunNotifier notifier = mock(RunNotifier.class); PickleRunners.PickleRunner pickleRunner = featureRunner.getChildren().get(0); @@ -407,7 +412,9 @@ void should_filter_pickles() { throw illegalStateException; }; - FeatureRunner featureRunner = FeatureRunner.create(feature, null, filters, runnerSupplier, new JUnitOptions()); + EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); + CucumberExecutionContext context = new CucumberExecutionContext(bus, new ExitStatus(options), runnerSupplier); + FeatureRunner featureRunner = FeatureRunner.create(feature, null, filters, context, new JUnitOptions()); assertThat(featureRunner.getChildren().size(), is(1)); assertThat(featureRunner.getChildren().get(0).getDescription().getDisplayName(), is("scenario_2 name(feature name)")); diff --git a/junit/src/test/java/io/cucumber/junit/PickleRunnerWithNoStepDescriptionsTest.java b/junit/src/test/java/io/cucumber/junit/PickleRunnerWithNoStepDescriptionsTest.java index 2bcbe63157..cca3622051 100644 --- a/junit/src/test/java/io/cucumber/junit/PickleRunnerWithNoStepDescriptionsTest.java +++ b/junit/src/test/java/io/cucumber/junit/PickleRunnerWithNoStepDescriptionsTest.java @@ -1,11 +1,19 @@ package io.cucumber.junit; +import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.gherkin.Pickle; +import io.cucumber.core.options.RuntimeOptions; +import io.cucumber.core.plugin.Options; +import io.cucumber.core.runtime.CucumberExecutionContext; +import io.cucumber.core.runtime.ExitStatus; import io.cucumber.core.runtime.RunnerSupplier; +import io.cucumber.core.runtime.TimeServiceEventBus; import io.cucumber.junit.PickleRunners.PickleRunner; import org.junit.jupiter.api.Test; +import java.time.Clock; import java.util.List; +import java.util.UUID; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; @@ -14,6 +22,11 @@ class PickleRunnerWithNoStepDescriptionsTest { + final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); + final Options options = RuntimeOptions.defaultOptions(); + final RunnerSupplier runnerSupplier = mock(RunnerSupplier.class); + final CucumberExecutionContext context = new CucumberExecutionContext(bus, new ExitStatus(options), runnerSupplier); + @Test void shouldUseScenarioNameWithFeatureNameAsClassNameForDisplayName() { List pickles = TestPickleBuilder.picklesFromFeature("featurePath", "" + @@ -23,7 +36,7 @@ void shouldUseScenarioNameWithFeatureNameAsClassNameForDisplayName() { PickleRunner runner = PickleRunners.withNoStepDescriptions( "feature name", - mock(RunnerSupplier.class), + context, pickles.get(0), null, createJunitOptions()); @@ -44,7 +57,7 @@ void shouldConvertTextFromFeatureFileForNamesWithFilenameCompatibleNameOption() PickleRunner runner = PickleRunners.withNoStepDescriptions( "feature name", - mock(RunnerSupplier.class), + context, pickles.get(0), null, createFileNameCompatibleJUnitOptions()); @@ -66,7 +79,7 @@ void shouldConvertTextFromFeatureFileWithRussianLanguage() { PickleRunner runner = PickleRunners.withNoStepDescriptions( "имя функции", - mock(RunnerSupplier.class), + context, pickles.get(0), null, createFileNameCompatibleJUnitOptions()); diff --git a/junit/src/test/java/io/cucumber/junit/PickleRunnerWithStepDescriptionsTest.java b/junit/src/test/java/io/cucumber/junit/PickleRunnerWithStepDescriptionsTest.java index 85b71b931a..83f3cd4ff1 100644 --- a/junit/src/test/java/io/cucumber/junit/PickleRunnerWithStepDescriptionsTest.java +++ b/junit/src/test/java/io/cucumber/junit/PickleRunnerWithStepDescriptionsTest.java @@ -1,15 +1,23 @@ package io.cucumber.junit; +import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.gherkin.Feature; import io.cucumber.core.gherkin.Pickle; +import io.cucumber.core.options.RuntimeOptions; +import io.cucumber.core.plugin.Options; +import io.cucumber.core.runtime.CucumberExecutionContext; +import io.cucumber.core.runtime.ExitStatus; import io.cucumber.core.runtime.RunnerSupplier; +import io.cucumber.core.runtime.TimeServiceEventBus; import io.cucumber.junit.PickleRunners.PickleRunner; import io.cucumber.junit.PickleRunners.WithStepDescriptions; import io.cucumber.plugin.event.Step; import org.junit.jupiter.api.Test; import org.junit.runner.Description; +import java.time.Clock; import java.util.List; +import java.util.UUID; import static io.cucumber.junit.TestPickleBuilder.picklesFromFeature; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -18,6 +26,11 @@ class PickleRunnerWithStepDescriptionsTest { + final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); + final Options options = RuntimeOptions.defaultOptions(); + final RunnerSupplier runnerSupplier = mock(RunnerSupplier.class); + final CucumberExecutionContext context = new CucumberExecutionContext(bus, new ExitStatus(options), runnerSupplier); + @Test void shouldAssignUnequalDescriptionsToDifferentOccurrencesOfSameStepInAScenario() { List pickles = picklesFromFeature("path/test.feature", "" + @@ -32,7 +45,7 @@ void shouldAssignUnequalDescriptionsToDifferentOccurrencesOfSameStepInAScenario( " Then baz\n"); WithStepDescriptions runner = (WithStepDescriptions) PickleRunners.withStepDescriptions( - mock(RunnerSupplier.class), + context, pickles.get(0), null, createJunitOptions()); @@ -68,7 +81,7 @@ void shouldAssignUnequalDescriptionsToDifferentStepsInAScenarioOutline() { " | a1 | r1 |\n"); WithStepDescriptions runner = (WithStepDescriptions) PickleRunners.withStepDescriptions( - mock(RunnerSupplier.class), + context, features.getPickles().get(0), null, createJunitOptions()); @@ -93,7 +106,7 @@ void shouldIncludeScenarioNameAsClassNameInStepDescriptions() { " Then another step\n"); PickleRunner runner = PickleRunners.withStepDescriptions( - mock(RunnerSupplier.class), + context, features.getPickles().get(0), null, createJunitOptions()); @@ -116,7 +129,7 @@ void shouldUseScenarioNameForDisplayName() { " Then it works\n"); PickleRunner runner = PickleRunners.withStepDescriptions( - mock(RunnerSupplier.class), + context, pickles.get(0), null, createJunitOptions()); @@ -132,7 +145,7 @@ void shouldUseStepKeyworkAndNameForChildName() { " Then it works\n"); PickleRunner runner = PickleRunners.withStepDescriptions( - mock(RunnerSupplier.class), + context, pickles.get(0), null, createJunitOptions()); @@ -148,7 +161,7 @@ void shouldConvertTextFromFeatureFileForNamesWithFilenameCompatibleNameOption() " Then it works\n"); PickleRunner runner = PickleRunners.withStepDescriptions( - mock(RunnerSupplier.class), + context, pickles.get(0), null, createFileNameCompatibleJunitOptions()); diff --git a/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java b/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java index ca6fd4701f..80c44f7529 100644 --- a/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java +++ b/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java @@ -107,6 +107,7 @@ public TestNGCucumberRunner(Class clazz) { plugins.setSerialEventBusOnEventListenerPlugins(bus); features = featureSupplier.get(); context.startTestRun(); + context.runBeforeAllHooks(); features.forEach(context::beforeFeature); } @@ -124,7 +125,11 @@ public void runScenario(io.cucumber.testng.Pickle pickle) { * Finishes test execution by Cucumber. */ public void finish() { - context.finishTestRun(); + try { + context.runAfterAllHooks(); + } finally { + context.finishTestRun(); + } } /** From c64cc353ad2acbc51ef76c4c5adde49fdd95f63b Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Mon, 8 Mar 2021 21:52:32 +0100 Subject: [PATCH 04/28] JUnit Invoke around semantics --- .../runtime/CucumberExecutionContext.java | 2 +- .../main/java/io/cucumber/junit/Cucumber.java | 1 - .../java/io/cucumber/junit/FeatureRunner.java | 7 +- .../junit/InvokeMethodsAroundEventsTest.java | 56 +++++++++++++++- .../junit/StubBackendProviderService.java | 67 ++++++++++++++++++- 5 files changed, 126 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java index a09bc3fc6d..f1466f08f8 100644 --- a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java +++ b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java @@ -114,7 +114,7 @@ private void emitTestRunFinished(CucumberException cucumberException) { bus.send(new TestRunFinished(instant, result)); Messages.TestRunFinished.Builder testRunFinished = Messages.TestRunFinished.newBuilder() - .setSuccess(exitStatus.isSuccess()) + .setSuccess(cucumberException != null && exitStatus.isSuccess()) .setTimestamp(javaInstantToTimestamp(instant)); if (cucumberException != null) { diff --git a/junit/src/main/java/io/cucumber/junit/Cucumber.java b/junit/src/main/java/io/cucumber/junit/Cucumber.java index 88e10616c4..c9ec9727fb 100644 --- a/junit/src/main/java/io/cucumber/junit/Cucumber.java +++ b/junit/src/main/java/io/cucumber/junit/Cucumber.java @@ -266,7 +266,6 @@ public RunBeforeAllHooks(Statement next) { @Override public void evaluate() throws Throwable { context.runBeforeAllHooks(); - features.forEach(context::beforeFeature); next.evaluate(); } diff --git a/junit/src/main/java/io/cucumber/junit/FeatureRunner.java b/junit/src/main/java/io/cucumber/junit/FeatureRunner.java index 4debe43579..4fd134e573 100644 --- a/junit/src/main/java/io/cucumber/junit/FeatureRunner.java +++ b/junit/src/main/java/io/cucumber/junit/FeatureRunner.java @@ -130,11 +130,16 @@ protected Description describeChild(PickleRunner child) { return child.getDescription(); } + @Override + public void run(RunNotifier notifier) { + context.beforeFeature(feature); + super.run(notifier); + } + @Override protected void runChild(PickleRunner child, RunNotifier notifier) { notifier.fireTestStarted(describeChild(child)); try { - context.beforeFeature(feature); child.run(notifier); } catch (Throwable e) { notifier.fireTestFailure(new Failure(describeChild(child), e)); diff --git a/junit/src/test/java/io/cucumber/junit/InvokeMethodsAroundEventsTest.java b/junit/src/test/java/io/cucumber/junit/InvokeMethodsAroundEventsTest.java index ae619b3e5f..965b78ca77 100644 --- a/junit/src/test/java/io/cucumber/junit/InvokeMethodsAroundEventsTest.java +++ b/junit/src/test/java/io/cucumber/junit/InvokeMethodsAroundEventsTest.java @@ -2,18 +2,24 @@ import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.event.EventPublisher; +import io.cucumber.plugin.event.TestCaseFinished; +import io.cucumber.plugin.event.TestCaseStarted; import io.cucumber.plugin.event.TestRunFinished; import io.cucumber.plugin.event.TestRunStarted; +import io.cucumber.plugin.event.TestSourceRead; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.InitializationError; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; +import static io.cucumber.junit.StubBackendProviderService.callbacks; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; @@ -21,19 +27,60 @@ class InvokeMethodsAroundEventsTest { private static final List events = new ArrayList<>(); + private final Consumer callback = events::add; + + @BeforeEach + void before(){ + callbacks.add(callback); + } + @AfterEach - void afterClass() { + void after() { events.clear(); + callbacks.remove(callback); } @Test void invoke_methods_around_events() throws InitializationError { Cucumber cucumber = new Cucumber(BeforeAfterClass.class); cucumber.run(new RunNotifier()); - assertThat(events, contains("BeforeClass", "TestRunStarted", "TestRunFinished", "AfterClass")); + assertThat(events, contains( + "BeforeClass", + "TestRunStarted", + "BeforeAll", + "TestSourceRead", + "TestCaseStarted", + "Before", + "Step", + "Step", + "Step", + "After", + "TestCaseFinished", + "TestCaseStarted", + "Before", + "Step", + "Step", + "Step", + "After", + "TestCaseFinished", + "TestSourceRead", + "TestCaseStarted", + "Before", + "Step", + "Step", + "Step", + "After", + "TestCaseFinished", + "AfterAll", + "TestRunFinished", + "AfterClass" + )); } - @CucumberOptions(plugin = "io.cucumber.junit.InvokeMethodsAroundEventsTest$TestRunStartedFinishedListener") + @CucumberOptions( + plugin = "io.cucumber.junit.InvokeMethodsAroundEventsTest$TestRunStartedFinishedListener", + features = {"classpath:io/cucumber/junit/rule.feature", "classpath:io/cucumber/junit/single.feature"} + ) public static class BeforeAfterClass { @BeforeClass @@ -55,6 +102,9 @@ public static class TestRunStartedFinishedListener implements ConcurrentEventLis public void setEventPublisher(EventPublisher publisher) { publisher.registerHandlerFor(TestRunStarted.class, event -> events.add("TestRunStarted")); publisher.registerHandlerFor(TestRunFinished.class, event -> events.add("TestRunFinished")); + publisher.registerHandlerFor(TestSourceRead.class, event -> events.add("TestSourceRead")); + publisher.registerHandlerFor(TestCaseStarted.class, event -> events.add("TestCaseStarted")); + publisher.registerHandlerFor(TestCaseFinished.class, event -> events.add("TestCaseFinished")); } } diff --git a/junit/src/test/java/io/cucumber/junit/StubBackendProviderService.java b/junit/src/test/java/io/cucumber/junit/StubBackendProviderService.java index bf250b852f..cb4ad19255 100644 --- a/junit/src/test/java/io/cucumber/junit/StubBackendProviderService.java +++ b/junit/src/test/java/io/cucumber/junit/StubBackendProviderService.java @@ -4,21 +4,28 @@ import io.cucumber.core.backend.BackendProviderService; import io.cucumber.core.backend.Container; import io.cucumber.core.backend.Glue; +import io.cucumber.core.backend.HookDefinition; import io.cucumber.core.backend.Lookup; import io.cucumber.core.backend.ParameterInfo; import io.cucumber.core.backend.Snippet; +import io.cucumber.core.backend.StaticHookDefinition; import io.cucumber.core.backend.StepDefinition; +import io.cucumber.core.backend.TestCaseState; import java.lang.reflect.Type; import java.net.URI; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import java.util.function.Supplier; public class StubBackendProviderService implements BackendProviderService { + static final List> callbacks = new ArrayList<>(); + @Override public Backend create(Lookup lookup, Container container, Supplier classLoader) { return new StubBackend(); @@ -57,6 +64,64 @@ public void loadGlue(Glue glue, List gluePaths) { glue.addStepDefinition(createStepDefinition("C is used")); glue.addStepDefinition(createStepDefinition("D is used")); + glue.addBeforeAllHook(createStaticHook("BeforeAll")); + glue.addAfterAllHook(createStaticHook("AfterAll")); + glue.addBeforeHook(createHook("Before")); + glue.addAfterHook(createHook("After")); + + } + + private HookDefinition createHook(String event) { + return new HookDefinition() { + @Override + public void execute(TestCaseState state) { + callbacks.forEach(consumer -> consumer.accept(event)); + } + + @Override + public String getTagExpression() { + return ""; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public boolean isDefinedAt(StackTraceElement stackTraceElement) { + return false; + } + + @Override + public String getLocation() { + return "stubbed location"; + } + }; + } + + private StaticHookDefinition createStaticHook(String event) { + return new StaticHookDefinition() { + @Override + public void execute() { + callbacks.forEach(consumer -> consumer.accept(event)); + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public boolean isDefinedAt(StackTraceElement stackTraceElement) { + return false; + } + + @Override + public String getLocation() { + return "stubbed location"; + } + }; } private StepDefinition createStepDefinition(final String pattern) { @@ -64,7 +129,7 @@ private StepDefinition createStepDefinition(final String pattern) { @Override public void execute(Object[] args) { - + callbacks.forEach(consumer -> consumer.accept("Step")); } @Override From 950e1585fcda79098e14742d6c76984b914da5b7 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Mon, 8 Mar 2021 22:15:46 +0100 Subject: [PATCH 05/28] JUnit Invoke around semantics --- junit/src/main/java/io/cucumber/junit/Cucumber.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/junit/src/main/java/io/cucumber/junit/Cucumber.java b/junit/src/main/java/io/cucumber/junit/Cucumber.java index c9ec9727fb..f71eb14747 100644 --- a/junit/src/main/java/io/cucumber/junit/Cucumber.java +++ b/junit/src/main/java/io/cucumber/junit/Cucumber.java @@ -90,7 +90,6 @@ public final class Cucumber extends ParentRunner> { private final List> children; private final EventBus bus; - private final List features; private final Plugins plugins; private final CucumberExecutionContext context; @@ -153,7 +152,7 @@ public Cucumber(Class clazz) throws InitializationError { Supplier classLoader = ClassLoaders::getDefaultClassLoader; FeaturePathFeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(classLoader, runtimeOptions, parser); - this.features = featureSupplier.get(); + List features = featureSupplier.get(); // Create plugins after feature parsing to avoid the creation of empty // files on lexer errors. From 91002b4906fb33aad659d7e473546116b4ca69bc Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Mon, 8 Mar 2021 22:24:35 +0100 Subject: [PATCH 06/28] TestNG Invoke around semantics --- .../io/cucumber/core/runtime/CucumberExecutionContext.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java index f1466f08f8..8d0a815197 100644 --- a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java +++ b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java @@ -73,7 +73,6 @@ public void runBeforeAllHooks() { try { runnerSupplier.get().runBeforeAllHooks(); } catch (Throwable e) { - log.error(e, () -> "Unable to start Cucumber"); thrown.add(e); throw e; } @@ -83,7 +82,6 @@ public void runAfterAllHooks() { try { runnerSupplier.get().runAfterAllHooks(); } catch (Throwable e) { - log.error(e, () -> "Unable to start Cucumber"); thrown.add(e); throw e; } @@ -114,7 +112,7 @@ private void emitTestRunFinished(CucumberException cucumberException) { bus.send(new TestRunFinished(instant, result)); Messages.TestRunFinished.Builder testRunFinished = Messages.TestRunFinished.newBuilder() - .setSuccess(cucumberException != null && exitStatus.isSuccess()) + .setSuccess(cucumberException == null && exitStatus.isSuccess()) .setTimestamp(javaInstantToTimestamp(instant)); if (cucumberException != null) { @@ -148,7 +146,6 @@ private Runner getRunner() { try { return runnerSupplier.get(); } catch (Throwable e) { - log.error(e, () -> "Unable to start Cucumber"); thrown.add(e); throw e; } From 32b4d7bdf94ec7811b18cdc810216c43841b9bfe Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Mon, 8 Mar 2021 22:51:32 +0100 Subject: [PATCH 07/28] Sort hooks --- .../io/cucumber/core/runner/CachingGlue.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/io/cucumber/core/runner/CachingGlue.java b/core/src/main/java/io/cucumber/core/runner/CachingGlue.java index 757b932183..dc96439a76 100644 --- a/core/src/main/java/io/cucumber/core/runner/CachingGlue.java +++ b/core/src/main/java/io/cucumber/core/runner/CachingGlue.java @@ -49,9 +49,14 @@ final class CachingGlue implements Glue { - private static final Comparator ASCENDING = Comparator + private static final Comparator HOOK_ORDER_ASCENDING = Comparator .comparingInt(CoreHookDefinition::getOrder) .thenComparing(ScenarioScoped.class::isInstance); + + private static final Comparator STATIC_HOOK_ORDER_ASCENDING = Comparator + .comparingInt(StaticHookDefinition::getOrder); + + private final List parameterTypeDefinitions = new ArrayList<>(); private final List dataTableTypeDefinitions = new ArrayList<>(); private final List defaultParameterTransformers = new ArrayList<>(); @@ -85,12 +90,13 @@ final class CachingGlue implements Glue { @Override public void addBeforeAllHook(StaticHookDefinition beforeAllHook) { beforeAllHooks.add(beforeAllHook); - + afterAllHooks.sort(STATIC_HOOK_ORDER_ASCENDING); } @Override public void addAfterAllHook(StaticHookDefinition afterAllHook) { afterAllHooks.add(afterAllHook); + afterAllHooks.sort(STATIC_HOOK_ORDER_ASCENDING); } @Override @@ -101,25 +107,25 @@ public void addStepDefinition(StepDefinition stepDefinition) { @Override public void addBeforeHook(HookDefinition hookDefinition) { beforeHooks.add(CoreHookDefinition.create(hookDefinition)); - beforeHooks.sort(ASCENDING); + beforeHooks.sort(HOOK_ORDER_ASCENDING); } @Override public void addAfterHook(HookDefinition hookDefinition) { afterHooks.add(CoreHookDefinition.create(hookDefinition)); - afterHooks.sort(ASCENDING); + afterHooks.sort(HOOK_ORDER_ASCENDING); } @Override public void addBeforeStepHook(HookDefinition hookDefinition) { beforeStepHooks.add(CoreHookDefinition.create(hookDefinition)); - beforeStepHooks.sort(ASCENDING); + beforeStepHooks.sort(HOOK_ORDER_ASCENDING); } @Override public void addAfterStepHook(HookDefinition hookDefinition) { afterStepHooks.add(CoreHookDefinition.create(hookDefinition)); - afterStepHooks.sort(ASCENDING); + afterStepHooks.sort(HOOK_ORDER_ASCENDING); } @Override From b49a269e5d220d1828b5edfdd7657285db44a374 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Mon, 8 Mar 2021 23:20:47 +0100 Subject: [PATCH 08/28] Reduce diff size --- .../io/cucumber/core/runner/CachingGlue.java | 1 - .../java/io/cucumber/core/runner/Runner.java | 32 +++++----- .../runtime/CucumberExecutionContext.java | 1 - .../cucumber/java/JavaHookDefinitionTest.java | 4 +- .../java/JavaStaticHookDefinitionTest.java | 8 +-- .../CucumberEngineExecutionContext.java | 10 +-- .../main/java/io/cucumber/junit/Cucumber.java | 16 +++-- .../io/cucumber/junit/FeatureRunnerTest.java | 3 +- .../junit/InvokeMethodsAroundEventsTest.java | 64 +++++++++---------- 9 files changed, 69 insertions(+), 70 deletions(-) diff --git a/core/src/main/java/io/cucumber/core/runner/CachingGlue.java b/core/src/main/java/io/cucumber/core/runner/CachingGlue.java index dc96439a76..12e7e1cc67 100644 --- a/core/src/main/java/io/cucumber/core/runner/CachingGlue.java +++ b/core/src/main/java/io/cucumber/core/runner/CachingGlue.java @@ -56,7 +56,6 @@ final class CachingGlue implements Glue { private static final Comparator STATIC_HOOK_ORDER_ASCENDING = Comparator .comparingInt(StaticHookDefinition::getOrder); - private final List parameterTypeDefinitions = new ArrayList<>(); private final List dataTableTypeDefinitions = new ArrayList<>(); private final List defaultParameterTransformers = new ArrayList<>(); diff --git a/core/src/main/java/io/cucumber/core/runner/Runner.java b/core/src/main/java/io/cucumber/core/runner/Runner.java index 3c4ed4b392..0a022d74bb 100644 --- a/core/src/main/java/io/cucumber/core/runner/Runner.java +++ b/core/src/main/java/io/cucumber/core/runner/Runner.java @@ -83,6 +83,16 @@ public void runPickle(Pickle pickle) { } } + private StepTypeRegistry createTypeRegistryForPickle(Pickle pickle) { + Locale locale = typeRegistryConfigurer.locale(); + if (locale == null) { + locale = new Locale(pickle.getLanguage()); + } + StepTypeRegistry stepTypeRegistry = new StepTypeRegistry(locale); + typeRegistryConfigurer.configureTypeRegistry(stepTypeRegistry); + return stepTypeRegistry; + } + public void runBeforeAllHooks() { glue.getBeforeAllHooks().forEach(this::executeHook); } @@ -99,9 +109,9 @@ private void executeHook(StaticHookDefinition hookDefinition) { hookDefinition.execute(); } catch (CucumberBackendException e) { CucumberException exception = new CucumberException(String.format("" + - "Could not invoke hook defined at '%s'.\n" + - "It appears there was a problem with the hook definition.", - hookDefinition.getLocation()), e); + "Could not invoke hook defined at '%s'.\n" + + "It appears there was a problem with the hook definition.", + hookDefinition.getLocation()), e); throwAsUncheckedException(exception); } catch (CucumberInvocationTargetException e) { Throwable throwable = removeFrameworkFrames(e); @@ -117,16 +127,6 @@ private List createSnippetGeneratorsForPickle(StepTypeRegistry .collect(Collectors.toList()); } - private StepTypeRegistry createTypeRegistryForPickle(Pickle pickle) { - Locale locale = typeRegistryConfigurer.locale(); - if (locale == null) { - locale = new Locale(pickle.getLanguage()); - } - StepTypeRegistry stepTypeRegistry = new StepTypeRegistry(locale); - typeRegistryConfigurer.configureTypeRegistry(stepTypeRegistry); - return stepTypeRegistry; - } - private void buildBackendWorlds() { objectFactory.start(); for (Backend backend : backends) { @@ -137,7 +137,7 @@ private void buildBackendWorlds() { private TestCase createTestCaseForPickle(Pickle pickle) { if (pickle.getSteps().isEmpty()) { return new TestCase(bus.generateId(), emptyList(), emptyList(), emptyList(), pickle, - runnerOptions.isDryRun()); + runnerOptions.isDryRun()); } List testSteps = createTestStepsForPickleSteps(pickle); @@ -161,7 +161,7 @@ private List createTestStepsForPickleSteps(Pickle pickle) { List afterStepHookSteps = createAfterStepHooks(pickle.getTags()); List beforeStepHookSteps = createBeforeStepHooks(pickle.getTags()); testSteps.add(new PickleStepTestStep(bus.generateId(), pickle.getUri(), step, beforeStepHookSteps, - afterStepHookSteps, match)); + afterStepHookSteps, match)); } return testSteps; @@ -197,7 +197,7 @@ private void emitSnippetSuggestedEvent(Pickle pickle, Step step) { Location scenarioLocation = pickle.getLocation(); Location stepLocation = step.getLocation(); SnippetsSuggestedEvent event = new SnippetsSuggestedEvent(bus.getInstant(), pickle.getUri(), scenarioLocation, - stepLocation, suggestion); + stepLocation, suggestion); bus.send(event); } diff --git a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java index 8d0a815197..b32fff6885 100644 --- a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java +++ b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java @@ -52,7 +52,6 @@ public void startTestRun() { emitTestRunStarted(); } - private void emitMeta() { bus.send(Envelope.newBuilder() .setMeta(createMeta("cucumber-jvm", VERSION, System.getenv())) diff --git a/java/src/test/java/io/cucumber/java/JavaHookDefinitionTest.java b/java/src/test/java/io/cucumber/java/JavaHookDefinitionTest.java index 16aaf72ab6..e1616cd003 100644 --- a/java/src/test/java/io/cucumber/java/JavaHookDefinitionTest.java +++ b/java/src/test/java/io/cucumber/java/JavaHookDefinitionTest.java @@ -107,8 +107,8 @@ public void too_many_parameters(Scenario arg1, String arg2) { void fails_with_non_void_return_type() throws Throwable { Method method = JavaHookDefinitionTest.class.getMethod("string_return_type"); InvalidMethodSignatureException exception = assertThrows( - InvalidMethodSignatureException.class, - () -> new JavaHookDefinition(method, "", 0, lookup)); + InvalidMethodSignatureException.class, + () -> new JavaHookDefinition(method, "", 0, lookup)); assertThat(exception.getMessage(), startsWith("" + "A method annotated with Before, After, BeforeStep or AfterStep must have one of these signatures:\n" + " * public void before_or_after(io.cucumber.java.Scenario scenario)\n" + diff --git a/java/src/test/java/io/cucumber/java/JavaStaticHookDefinitionTest.java b/java/src/test/java/io/cucumber/java/JavaStaticHookDefinitionTest.java index dd3bb0459e..a4e4f633a0 100644 --- a/java/src/test/java/io/cucumber/java/JavaStaticHookDefinitionTest.java +++ b/java/src/test/java/io/cucumber/java/JavaStaticHookDefinitionTest.java @@ -47,8 +47,8 @@ public static void no_arguments() { void fails_with_arguments() throws Throwable { Method method = JavaStaticHookDefinitionTest.class.getMethod("single_argument", Scenario.class); InvalidMethodSignatureException exception = assertThrows( - InvalidMethodSignatureException.class, - () -> new JavaStaticHookDefinition(method, 0, lookup)); + InvalidMethodSignatureException.class, + () -> new JavaStaticHookDefinition(method, 0, lookup)); assertThat(exception.getMessage(), startsWith("" + "A method annotated with BeforeAll or AfterAll must have one of these signatures:\n" + " * public static void before_or_after_all()\n" + @@ -64,8 +64,8 @@ public void single_argument(Scenario scenario) { void fails_with_non_void_return_type() throws Throwable { Method method = JavaStaticHookDefinitionTest.class.getMethod("string_return_type"); InvalidMethodSignatureException exception = assertThrows( - InvalidMethodSignatureException.class, - () -> new JavaStaticHookDefinition(method, 0, lookup)); + InvalidMethodSignatureException.class, + () -> new JavaStaticHookDefinition(method, 0, lookup)); assertThat(exception.getMessage(), startsWith("" + "A method annotated with BeforeAll or AfterAll must have one of these signatures:\n" + " * public static void before_or_after_all()\n" + diff --git a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java index 9c144efcaf..3ec51705b0 100644 --- a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java +++ b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java @@ -47,7 +47,7 @@ public final class CucumberEngineExecutionContext implements EngineExecutionCont ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, options); EventBus bus = synchronize(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier = new ScanningTypeRegistryConfigurerSupplier( - classLoader, options); + classLoader, options); Plugins plugins = new Plugins(new PluginFactory(), options); ExitStatus exitStatus = new ExitStatus(options); plugins.addPlugin(exitStatus); @@ -56,17 +56,17 @@ public final class CucumberEngineExecutionContext implements EngineExecutionCont if (options.isParallelExecutionEnabled()) { plugins.setSerialEventBusOnEventListenerPlugins(bus); ObjectFactorySupplier objectFactorySupplier = new ThreadLocalObjectFactorySupplier( - objectFactoryServiceLoader); + objectFactoryServiceLoader); BackendSupplier backendSupplier = new BackendServiceLoader(classLoader, objectFactorySupplier); runnerSupplier = new ThreadLocalRunnerSupplier(options, bus, backendSupplier, objectFactorySupplier, - typeRegistryConfigurerSupplier); + typeRegistryConfigurerSupplier); } else { plugins.setEventBusOnEventListenerPlugins(bus); ObjectFactorySupplier objectFactorySupplier = new SingletonObjectFactorySupplier( - objectFactoryServiceLoader); + objectFactoryServiceLoader); BackendSupplier backendSupplier = new BackendServiceLoader(classLoader, objectFactorySupplier); runnerSupplier = new SingletonRunnerSupplier(options, bus, backendSupplier, objectFactorySupplier, - typeRegistryConfigurerSupplier); + typeRegistryConfigurerSupplier); } this.context = new CucumberExecutionContext(bus, exitStatus, runnerSupplier); } diff --git a/junit/src/main/java/io/cucumber/junit/Cucumber.java b/junit/src/main/java/io/cucumber/junit/Cucumber.java index f71eb14747..956146c190 100644 --- a/junit/src/main/java/io/cucumber/junit/Cucumber.java +++ b/junit/src/main/java/io/cucumber/junit/Cucumber.java @@ -54,12 +54,14 @@ * A class annotated with {@code @RunWith(Cucumber.class)} will run feature * files as junit tests. In general, the runner class should be empty without * any fields or methods. For example:

+ * *
  * @RunWith(Cucumber.class)
  * @CucumberOptions(plugin = "pretty")
  * public class RunCucumberTest {
  * }
  * 
+ * *
*

* By default Cucumber will look for {@code .feature} and glue files on the @@ -98,9 +100,9 @@ public final class Cucumber extends ParentRunner> { /** * Constructor called by JUnit. * - * @param clazz the class with - * the @RunWith - * annotation. + * @param clazz the class with + * the @RunWith + * annotation. * @throws org.junit.runners.model.InitializationError if there is another * problem */ @@ -151,7 +153,7 @@ public Cucumber(Class clazz) throws InitializationError { FeatureParser parser = new FeatureParser(bus::generateId); Supplier classLoader = ClassLoaders::getDefaultClassLoader; FeaturePathFeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(classLoader, runtimeOptions, - parser); + parser); List features = featureSupplier.get(); // Create plugins after feature parsing to avoid the creation of empty @@ -161,13 +163,13 @@ public Cucumber(Class clazz) throws InitializationError { this.plugins.addPlugin(exitStatus); ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, - runtimeOptions); + runtimeOptions); ObjectFactorySupplier objectFactorySupplier = new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader); BackendSupplier backendSupplier = new BackendServiceLoader(clazz::getClassLoader, objectFactorySupplier); TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier = new ScanningTypeRegistryConfigurerSupplier( - classLoader, runtimeOptions); + classLoader, runtimeOptions); ThreadLocalRunnerSupplier runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, bus, backendSupplier, - objectFactorySupplier, typeRegistryConfigurerSupplier); + objectFactorySupplier, typeRegistryConfigurerSupplier); this.context = new CucumberExecutionContext(bus, exitStatus, runnerSupplier); Predicate filters = new Filters(runtimeOptions); diff --git a/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java b/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java index b7dacb58a3..33997263f5 100644 --- a/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java +++ b/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java @@ -125,7 +125,8 @@ public Instant instant() { classLoader, runtimeOptions); ThreadLocalRunnerSupplier runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, bus, backendSupplier, objectFactory, typeRegistrySupplier); - CucumberExecutionContext context = new CucumberExecutionContext(bus, new ExitStatus(runtimeOptions), runnerSupplier); + CucumberExecutionContext context = new CucumberExecutionContext(bus, new ExitStatus(runtimeOptions), + runnerSupplier); return FeatureRunner.create(feature, null, filters, context, junitOption); } diff --git a/junit/src/test/java/io/cucumber/junit/InvokeMethodsAroundEventsTest.java b/junit/src/test/java/io/cucumber/junit/InvokeMethodsAroundEventsTest.java index 965b78ca77..c21ddff72e 100644 --- a/junit/src/test/java/io/cucumber/junit/InvokeMethodsAroundEventsTest.java +++ b/junit/src/test/java/io/cucumber/junit/InvokeMethodsAroundEventsTest.java @@ -30,7 +30,7 @@ class InvokeMethodsAroundEventsTest { private final Consumer callback = events::add; @BeforeEach - void before(){ + void before() { callbacks.add(callback); } @@ -45,42 +45,40 @@ void invoke_methods_around_events() throws InitializationError { Cucumber cucumber = new Cucumber(BeforeAfterClass.class); cucumber.run(new RunNotifier()); assertThat(events, contains( - "BeforeClass", - "TestRunStarted", - "BeforeAll", - "TestSourceRead", - "TestCaseStarted", - "Before", - "Step", - "Step", - "Step", - "After", - "TestCaseFinished", - "TestCaseStarted", - "Before", - "Step", - "Step", - "Step", - "After", - "TestCaseFinished", - "TestSourceRead", - "TestCaseStarted", - "Before", - "Step", - "Step", - "Step", - "After", - "TestCaseFinished", - "AfterAll", - "TestRunFinished", - "AfterClass" - )); + "BeforeClass", + "TestRunStarted", + "BeforeAll", + "TestSourceRead", + "TestCaseStarted", + "Before", + "Step", + "Step", + "Step", + "After", + "TestCaseFinished", + "TestCaseStarted", + "Before", + "Step", + "Step", + "Step", + "After", + "TestCaseFinished", + "TestSourceRead", + "TestCaseStarted", + "Before", + "Step", + "Step", + "Step", + "After", + "TestCaseFinished", + "AfterAll", + "TestRunFinished", + "AfterClass")); } @CucumberOptions( plugin = "io.cucumber.junit.InvokeMethodsAroundEventsTest$TestRunStartedFinishedListener", - features = {"classpath:io/cucumber/junit/rule.feature", "classpath:io/cucumber/junit/single.feature"} - ) + features = { "classpath:io/cucumber/junit/rule.feature", "classpath:io/cucumber/junit/single.feature" }) public static class BeforeAfterClass { @BeforeClass From bf1d23ecb68cb2812c46128c4323399255c094bf Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Mon, 8 Mar 2021 23:22:15 +0100 Subject: [PATCH 09/28] Experimental api --- .../java/io/cucumber/core/backend/StaticHookDefinition.java | 2 +- java/src/main/java/io/cucumber/java/AfterAll.java | 2 +- java/src/main/java/io/cucumber/java/BeforeAll.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/io/cucumber/core/backend/StaticHookDefinition.java b/core/src/main/java/io/cucumber/core/backend/StaticHookDefinition.java index 7032418d62..ca8c7045ed 100644 --- a/core/src/main/java/io/cucumber/core/backend/StaticHookDefinition.java +++ b/core/src/main/java/io/cucumber/core/backend/StaticHookDefinition.java @@ -2,7 +2,7 @@ import org.apiguardian.api.API; -@API(status = API.Status.STABLE) +@API(status = API.Status.EXPERIMENTAL) public interface StaticHookDefinition extends Located { void execute(); diff --git a/java/src/main/java/io/cucumber/java/AfterAll.java b/java/src/main/java/io/cucumber/java/AfterAll.java index 66d6e66166..938bc757dd 100644 --- a/java/src/main/java/io/cucumber/java/AfterAll.java +++ b/java/src/main/java/io/cucumber/java/AfterAll.java @@ -12,7 +12,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) -@API(status = API.Status.STABLE) +@API(status = API.Status.EXPERIMENTAL) public @interface AfterAll { /** diff --git a/java/src/main/java/io/cucumber/java/BeforeAll.java b/java/src/main/java/io/cucumber/java/BeforeAll.java index fdf81e381c..7bc6e679a8 100644 --- a/java/src/main/java/io/cucumber/java/BeforeAll.java +++ b/java/src/main/java/io/cucumber/java/BeforeAll.java @@ -12,7 +12,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) -@API(status = API.Status.STABLE) +@API(status = API.Status.EXPERIMENTAL) public @interface BeforeAll { /** From 7cf19bee64a090a0c82c708b7a0e2b5c0d29d722 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 26 Mar 2021 17:30:42 +0100 Subject: [PATCH 10/28] Reduce logspam --- .../src/test/resources/junit-platform.properties | 1 + .../src/test/resources/cucumber.properties | 1 + examples/java-calculator/src/test/resources/cucumber.properties | 1 + .../java-wicket-test/src/test/resources/cucumber.properties | 1 + examples/java8-calculator/src/test/resources/cucumber.properties | 1 + 5 files changed, 5 insertions(+) create mode 100644 examples/java-calculator-testng/src/test/resources/cucumber.properties create mode 100644 examples/java-calculator/src/test/resources/cucumber.properties create mode 100644 examples/java-wicket/java-wicket-test/src/test/resources/cucumber.properties create mode 100644 examples/java8-calculator/src/test/resources/cucumber.properties diff --git a/examples/java-calculator-junit5/src/test/resources/junit-platform.properties b/examples/java-calculator-junit5/src/test/resources/junit-platform.properties index 7393a39aa7..432abd2fea 100644 --- a/examples/java-calculator-junit5/src/test/resources/junit-platform.properties +++ b/examples/java-calculator-junit5/src/test/resources/junit-platform.properties @@ -1 +1,2 @@ cucumber.execution.parallel.enabled=true +cucumber.publish.quiet=true diff --git a/examples/java-calculator-testng/src/test/resources/cucumber.properties b/examples/java-calculator-testng/src/test/resources/cucumber.properties new file mode 100644 index 0000000000..b48dd63bf1 --- /dev/null +++ b/examples/java-calculator-testng/src/test/resources/cucumber.properties @@ -0,0 +1 @@ +cucumber.publish.quiet=true diff --git a/examples/java-calculator/src/test/resources/cucumber.properties b/examples/java-calculator/src/test/resources/cucumber.properties new file mode 100644 index 0000000000..b48dd63bf1 --- /dev/null +++ b/examples/java-calculator/src/test/resources/cucumber.properties @@ -0,0 +1 @@ +cucumber.publish.quiet=true diff --git a/examples/java-wicket/java-wicket-test/src/test/resources/cucumber.properties b/examples/java-wicket/java-wicket-test/src/test/resources/cucumber.properties new file mode 100644 index 0000000000..b48dd63bf1 --- /dev/null +++ b/examples/java-wicket/java-wicket-test/src/test/resources/cucumber.properties @@ -0,0 +1 @@ +cucumber.publish.quiet=true diff --git a/examples/java8-calculator/src/test/resources/cucumber.properties b/examples/java8-calculator/src/test/resources/cucumber.properties new file mode 100644 index 0000000000..b48dd63bf1 --- /dev/null +++ b/examples/java8-calculator/src/test/resources/cucumber.properties @@ -0,0 +1 @@ +cucumber.publish.quiet=true From b15778d6969cbc95e51690ba6350d63f30e4aa04 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 26 Mar 2021 17:35:31 +0100 Subject: [PATCH 11/28] Reduce logspam --- .../java/io/cucumber/core/resource/ClasspathScannerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/io/cucumber/core/resource/ClasspathScannerTest.java b/core/src/test/java/io/cucumber/core/resource/ClasspathScannerTest.java index dba36306fb..9aa8f9506a 100644 --- a/core/src/test/java/io/cucumber/core/resource/ClasspathScannerTest.java +++ b/core/src/test/java/io/cucumber/core/resource/ClasspathScannerTest.java @@ -56,7 +56,7 @@ void tearDown() { @Test void scanForSubClassesInPackage() { - List> classes = scanner.scanForSubClassesInPackage("io.cucumber", + List> classes = scanner.scanForSubClassesInPackage("io.cucumber.core.resource.test", ExampleInterface.class); assertThat(classes, contains(ExampleClass.class)); From a0913724851edf832bb7aee9d2e3ffd500894b7c Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 26 Mar 2021 17:42:06 +0100 Subject: [PATCH 12/28] Reduce logspam --- .../java/io/cucumber/core/resource/ClasspathScannerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/test/java/io/cucumber/core/resource/ClasspathScannerTest.java b/core/src/test/java/io/cucumber/core/resource/ClasspathScannerTest.java index 9aa8f9506a..1b2ce6ea60 100644 --- a/core/src/test/java/io/cucumber/core/resource/ClasspathScannerTest.java +++ b/core/src/test/java/io/cucumber/core/resource/ClasspathScannerTest.java @@ -56,7 +56,8 @@ void tearDown() { @Test void scanForSubClassesInPackage() { - List> classes = scanner.scanForSubClassesInPackage("io.cucumber.core.resource.test", + List> classes = scanner.scanForSubClassesInPackage( + "io.cucumber.core.resource.test", ExampleInterface.class); assertThat(classes, contains(ExampleClass.class)); From 6c852fc93ca83c8fc3f20a5f96a553dee3f330ab Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 26 Mar 2021 19:15:32 +0100 Subject: [PATCH 13/28] Display before/after hook failure in teamcity plugin --- .../cucumber/core/plugin/TeamCityPlugin.java | 20 +++++++ .../runtime/CucumberExecutionContext.java | 18 +++--- .../io/cucumber/core/runtime/Runtime.java | 13 ++-- .../backend/StubStaticHookDefinition.java | 60 +++++++++++++++++++ .../core/plugin/TeamCityPluginTest.java | 32 ++++++++++ .../core/runtime/StubBackendSupplier.java | 20 ++++++- 6 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java diff --git a/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java b/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java index f9929323ce..0a8e737648 100644 --- a/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java +++ b/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java @@ -81,6 +81,13 @@ public class TeamCityPlugin implements EventListener { private static final String TEMPLATE_TEST_IGNORED = TEAMCITY_PREFIX + "[testIgnored timestamp = '%s' duration = '%s' message = '%s' name = '%s']"; + private static final String TEMPLATE_BEFORE_ALL_AFTER_ALL_STARTED = TEAMCITY_PREFIX + + "[testStarted timestamp = '%s' name = '%s']"; + private static final String TEMPLATE_BEFORE_ALL_AFTER_ALL_FAILED = TEAMCITY_PREFIX + + "[testFailed timestamp = '%s' message = '%s' details = '%s' name = '%s']"; + private static final String TEMPLATE_BEFORE_ALL_AFTER_ALL_FINISHED = TEAMCITY_PREFIX + + "[testFinished timestamp = '%s' name = '%s']"; + private static final String TEMPLATE_PROGRESS_COUNTING_STARTED = TEAMCITY_PREFIX + "[customProgressStatus testsCategory = 'Scenarios' count = '0' timestamp = '%s']"; private static final String TEMPLATE_PROGRESS_COUNTING_FINISHED = TEAMCITY_PREFIX @@ -364,9 +371,22 @@ private void printTestRunFinished(TestRunFinished event) { poppedNodes(emptyStack).forEach(node -> finishNode(timestamp, node)); currentStack = emptyStack; + printSystemResults(event, timestamp); print(TEMPLATE_TEST_RUN_FINISHED, timestamp); } + private void printSystemResults(TestRunFinished event, String timestamp) { + Throwable error = event.getResult().getError(); + if (error == null) { + return; + } + String name = "Before All/After All"; + print(TEMPLATE_BEFORE_ALL_AFTER_ALL_STARTED, timestamp, name); + String details = extractStackTrace(error); + print(TEMPLATE_BEFORE_ALL_AFTER_ALL_FAILED, timestamp, name, details, name); + print(TEMPLATE_BEFORE_ALL_AFTER_ALL_FINISHED, timestamp, name); + } + private void handleSnippetSuggested(SnippetsSuggestedEvent event) { suggestions.add(event); } diff --git a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java index b32fff6885..31dd328f55 100644 --- a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java +++ b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java @@ -88,34 +88,34 @@ public void runAfterAllHooks() { public void finishTestRun() { log.debug(() -> "Sending test run finished event"); - CucumberException cucumberException = getException(); + Throwable cucumberException = getException(); emitTestRunFinished(cucumberException); } - public CucumberException getException() { + public Throwable getException() { if (thrown.isEmpty()) { return null; } if (thrown.size() == 1) { - return new CucumberException(thrown.get(0)); + return thrown.get(0); } return new CompositeCucumberException(thrown); } - private void emitTestRunFinished(CucumberException cucumberException) { + private void emitTestRunFinished(Throwable exception) { Instant instant = bus.getInstant(); Result result = new Result( - cucumberException != null ? Status.FAILED : exitStatus.getStatus(), + exception != null ? Status.FAILED : exitStatus.getStatus(), Duration.between(start, instant), - cucumberException); + exception); bus.send(new TestRunFinished(instant, result)); Messages.TestRunFinished.Builder testRunFinished = Messages.TestRunFinished.newBuilder() - .setSuccess(cucumberException == null && exitStatus.isSuccess()) + .setSuccess(exception == null && exitStatus.isSuccess()) .setTimestamp(javaInstantToTimestamp(instant)); - if (cucumberException != null) { - testRunFinished.setMessage(cucumberException.getMessage()); + if (exception != null) { + testRunFinished.setMessage(exception.getMessage()); } bus.send(Envelope.newBuilder() .setTestRunFinished(testRunFinished) diff --git a/core/src/main/java/io/cucumber/core/runtime/Runtime.java b/core/src/main/java/io/cucumber/core/runtime/Runtime.java index 920c2224ba..043b12e11d 100644 --- a/core/src/main/java/io/cucumber/core/runtime/Runtime.java +++ b/core/src/main/java/io/cucumber/core/runtime/Runtime.java @@ -1,7 +1,6 @@ package io.cucumber.core.runtime; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.exception.CucumberException; import io.cucumber.core.feature.FeatureParser; import io.cucumber.core.filter.Filters; import io.cucumber.core.gherkin.Feature; @@ -78,14 +77,12 @@ public void run() { try { context.runBeforeAllHooks(); runFeatures(); - context.runAfterAllHooks(); } finally { - context.finishTestRun(); - } - - CucumberException exception = context.getException(); - if (exception != null) { - throw exception; + try { + context.runAfterAllHooks(); + } finally { + context.finishTestRun(); + } } } diff --git a/core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java b/core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java new file mode 100644 index 0000000000..0ed31f336e --- /dev/null +++ b/core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java @@ -0,0 +1,60 @@ +package io.cucumber.core.backend; + +public class StubStaticHookDefinition implements StaticHookDefinition { + + private static final String STUBBED_LOCATION_WITH_DETAILS = "{stubbed location with details}"; + private final String location; + private final RuntimeException exception; + private final Runnable action; + + public StubStaticHookDefinition(String location, RuntimeException exception, Runnable action) { + this.location = location; + this.exception = exception; + this.action = action; + } + + public StubStaticHookDefinition(String location, Runnable action) { + this(location, null, action); + } + + public StubStaticHookDefinition() { + this(STUBBED_LOCATION_WITH_DETAILS, null, null); + } + + public StubStaticHookDefinition(Runnable action) { + this(STUBBED_LOCATION_WITH_DETAILS, null, action); + } + + public StubStaticHookDefinition(RuntimeException exception) { + this(STUBBED_LOCATION_WITH_DETAILS, exception, null); + } + + public StubStaticHookDefinition(String location) { + this(location, null, null); + } + @Override + public void execute() { + if (action != null) { + action.run(); + } + if (exception != null) { + throw exception; + } + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public boolean isDefinedAt(StackTraceElement stackTraceElement) { + return false; + } + + @Override + public String getLocation() { + return location; + } + +} diff --git a/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java b/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java index 159f866b77..87cdb4d02d 100755 --- a/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java @@ -2,6 +2,7 @@ import io.cucumber.core.backend.StubHookDefinition; import io.cucumber.core.backend.StubPendingException; +import io.cucumber.core.backend.StubStaticHookDefinition; import io.cucumber.core.backend.StubStepDefinition; import io.cucumber.core.backend.TestCaseState; import io.cucumber.core.feature.TestFeatureParser; @@ -26,6 +27,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; @DisabledOnOs(OS.WINDOWS) class TeamCityPluginTest { @@ -309,4 +311,34 @@ void should_print_location_hint_for_lambda_hooks() { "##teamcity[testStarted timestamp = '1970-01-01T12:00:00.000+0000' locationHint = 'java:test://com.example.HookDefinition/HookDefinition' captureStandardOutput = 'true' name = 'Before']\n")); } + @Test + void should_print_system_failure_for_failed_hooks() { + Feature feature = TestFeatureParser.parse("path/test.feature", "" + + "Feature: feature name\n" + + " Scenario: scenario name\n" + + " Given first step\n"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + assertThrows(StubException.class, () -> Runtime.builder() + .withFeatureSupplier(new StubFeatureSupplier(feature)) + .withAdditionalPlugins(new TeamCityPlugin(new PrintStream(out))) + .withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID)) + .withBackendSupplier(new StubBackendSupplier( + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + singletonList(new StubStaticHookDefinition(new StubException("Hook failed", "the stack trace"))))) + .build() + .run()); + + assertThat(out, bytesContainsString("" + + "##teamcity[testStarted timestamp = '1970-01-01T12:00:00.000+0000' name = 'Cucumber']\n" + + "##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' message = 'System failure' details = 'the stack trace' name = 'Cucumber']\n" + + "##teamcity[testFinished timestamp = '1970-01-01T12:00:00.000+0000' name = 'Cucumber']")); + } + } diff --git a/core/src/test/java/io/cucumber/core/runtime/StubBackendSupplier.java b/core/src/test/java/io/cucumber/core/runtime/StubBackendSupplier.java index 68d096a6a9..b8a8ae5c10 100644 --- a/core/src/test/java/io/cucumber/core/runtime/StubBackendSupplier.java +++ b/core/src/test/java/io/cucumber/core/runtime/StubBackendSupplier.java @@ -4,8 +4,8 @@ import io.cucumber.core.backend.Glue; import io.cucumber.core.backend.HookDefinition; import io.cucumber.core.backend.Snippet; +import io.cucumber.core.backend.StaticHookDefinition; import io.cucumber.core.backend.StepDefinition; -import io.cucumber.core.runtime.BackendSupplier; import io.cucumber.core.snippets.TestSnippet; import java.net.URI; @@ -16,11 +16,13 @@ public class StubBackendSupplier implements BackendSupplier { + private final List beforeAll; private final List before; private final List beforeStep; private final List steps; private final List afterStep; private final List after; + private final List afterAll; public StubBackendSupplier(StepDefinition... steps) { this(Collections.emptyList(), Arrays.asList(steps), Collections.emptyList()); @@ -33,11 +35,25 @@ public StubBackendSupplier( List afterStep, List after ) { + this(Collections.emptyList(), before, beforeStep, steps, afterStep, after, Collections.emptyList()); + } + + public StubBackendSupplier( + List beforeAll, + List before, + List beforeStep, + List steps, + List afterStep, + List after, + List afterAll + ) { + this.beforeAll = beforeAll; this.before = before; this.beforeStep = beforeStep; this.steps = steps; this.afterStep = afterStep; this.after = after; + this.afterAll = afterAll; } public StubBackendSupplier( @@ -53,11 +69,13 @@ public Collection get() { return Collections.singletonList(new Backend() { @Override public void loadGlue(Glue glue, List gluePaths) { + beforeAll.forEach(glue::addBeforeAllHook); before.forEach(glue::addBeforeHook); beforeStep.forEach(glue::addBeforeStepHook); steps.forEach(glue::addStepDefinition); afterStep.forEach(glue::addAfterStepHook); after.forEach(glue::addAfterHook); + afterAll.forEach(glue::addAfterAllHook); } @Override From 314eeb736678eb47234c3f9f7a22b32cc2f91518 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 26 Mar 2021 20:00:13 +0100 Subject: [PATCH 14/28] Display before/after all failures in pretty formatter --- .../cucumber/core/plugin/JsonFormatter.java | 10 +++--- .../cucumber/core/plugin/PrettyFormatter.java | 17 +++++++++- .../cucumber/core/plugin/TeamCityPlugin.java | 25 ++++++--------- .../runtime/CucumberExecutionContext.java | 4 +-- .../core/plugin/PrettyFormatterTest.java | 31 +++++++++++++++++++ .../core/plugin/TeamCityPluginTest.java | 6 ++-- .../examples/java/RpnCalculatorSteps.java | 1 + .../examples/java/RunCucumberTest.java | 2 +- 8 files changed, 68 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java b/core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java index c169f2358c..8b2be5c8b9 100644 --- a/core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java +++ b/core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java @@ -354,9 +354,9 @@ private Map createDummyFeatureForFailure(TestRunFinished event) scenario.put("start_timestamp", getDateTimeFromTimeStamp(event.getInstant())); scenario.put("line", 2); - scenario.put("name", "Could not execute Cucumber"); + scenario.put("name", "Failure while executing Cucumber"); scenario.put("description", ""); - scenario.put("id", "failure;could-not-execute-cucumber"); + scenario.put("id", "failure;failure-while-executing-cucumber"); scenario.put("type", "scenario"); scenario.put("keyword", "Scenario"); @@ -372,18 +372,18 @@ private Map createDummyFeatureForFailure(TestRunFinished event) whenResult.put("status", "passed"); } when.put("line", 3); - when.put("name", "Cucumber could not execute"); + when.put("name", "Cucumber failed while executing"); Map whenMatch = new LinkedHashMap<>(); when.put("match", whenMatch); whenMatch.put("arguments", new ArrayList<>()); - whenMatch.put("location", "io.cucumber.core.Failure.cucumber_could_not_execute()"); + whenMatch.put("location", "io.cucumber.core.Failure.failure_while_executing_cucumber()"); when.put("keyword", "When "); { Map thenResult = new LinkedHashMap<>(); then.put("result", thenResult); thenResult.put("duration", 0); - thenResult.put("error_message", exception.getMessage()); + thenResult.put("error_message", printStackTrace(exception)); thenResult.put("status", "failed"); } then.put("line", 4); diff --git a/core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java b/core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java index 3eb9689da0..72017fb509 100644 --- a/core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java +++ b/core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java @@ -84,6 +84,7 @@ private void handleEmbed(EmbedEvent event) { } private void handleTestRunFinished(TestRunFinished event) { + printError(event); out.close(); } @@ -132,11 +133,25 @@ private void printStep(TestStepFinished event) { private void printError(TestStepFinished event) { Result result = event.getResult(); + printError(result); + } + + private void printError(TestRunFinished event) { + Result result = event.getResult(); + printError(result); + } + + private void printError(Result result) { Throwable error = result.getError(); if (error != null) { String name = result.getStatus().name().toLowerCase(ROOT); + Format format = formats.get(name); + String message = error.getMessage(); + if (message != null) { + out.println(" " + format.text(error.getMessage())); + } String text = printStackTrace(error); - out.println(" " + formats.get(name).text(text)); + out.println(" " + format.text(text)); } } diff --git a/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java b/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java index 0a8e737648..25a2dc98dc 100644 --- a/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java +++ b/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java @@ -24,10 +24,8 @@ import io.cucumber.plugin.event.TestStepStarted; import io.cucumber.plugin.event.WriteEvent; -import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.net.URI; -import java.nio.charset.StandardCharsets; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -45,6 +43,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import static io.cucumber.core.exception.ExceptionUtils.printStackTrace; import static java.util.Collections.emptyList; import static java.util.stream.Collectors.joining; @@ -52,8 +51,8 @@ * Outputs Teamcity services messages to std out. * * @see TeamCity - * - Service Messages + * href=https://www.jetbrains.com/help/teamcity/service-messages.html>TeamCity + * - Service Messages */ public class TeamCityPlugin implements EventListener { @@ -281,7 +280,7 @@ private void printTestStepFinished(TestStepFinished event) { } case AMBIGUOUS: case FAILED: { - String details = extractStackTrace(error); + String details = printStackTrace(error); print(TEMPLATE_TEST_FAILED, timeStamp, duration, "Step failed", details, name); break; } @@ -291,13 +290,6 @@ private void printTestStepFinished(TestStepFinished event) { print(TEMPLATE_TEST_FINISHED, timeStamp, duration, name); } - private String extractStackTrace(Throwable error) { - ByteArrayOutputStream s = new ByteArrayOutputStream(); - PrintStream printStream = new PrintStream(s); - error.printStackTrace(printStream); - return new String(s.toByteArray(), StandardCharsets.UTF_8); - } - private String extractName(TestStep step) { if (step instanceof PickleStepTestStep) { PickleStepTestStep pickleStepTestStep = (PickleStepTestStep) step; @@ -371,19 +363,20 @@ private void printTestRunFinished(TestRunFinished event) { poppedNodes(emptyStack).forEach(node -> finishNode(timestamp, node)); currentStack = emptyStack; - printSystemResults(event, timestamp); + printBeforeAfterAllResult(event, timestamp); print(TEMPLATE_TEST_RUN_FINISHED, timestamp); } - private void printSystemResults(TestRunFinished event, String timestamp) { + private void printBeforeAfterAllResult(TestRunFinished event, String timestamp) { Throwable error = event.getResult().getError(); if (error == null) { return; } + // Use dummy test to display before all after all failures String name = "Before All/After All"; print(TEMPLATE_BEFORE_ALL_AFTER_ALL_STARTED, timestamp, name); - String details = extractStackTrace(error); - print(TEMPLATE_BEFORE_ALL_AFTER_ALL_FAILED, timestamp, name, details, name); + String details = printStackTrace(error); + print(TEMPLATE_BEFORE_ALL_AFTER_ALL_FAILED, timestamp, "Before All/ After All failed", details, name); print(TEMPLATE_BEFORE_ALL_AFTER_ALL_FINISHED, timestamp, name); } diff --git a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java index 31dd328f55..b1667c0b0c 100644 --- a/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java +++ b/core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java @@ -2,7 +2,6 @@ import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.exception.CompositeCucumberException; -import io.cucumber.core.exception.CucumberException; import io.cucumber.core.gherkin.Feature; import io.cucumber.core.logging.Logger; import io.cucumber.core.logging.LoggerFactory; @@ -23,6 +22,7 @@ import java.util.ResourceBundle; import java.util.function.Consumer; +import static io.cucumber.core.exception.ExceptionUtils.printStackTrace; import static io.cucumber.core.exception.ExceptionUtils.throwAsUncheckedException; import static io.cucumber.createmeta.CreateMeta.createMeta; import static io.cucumber.messages.TimeConversion.javaInstantToTimestamp; @@ -115,7 +115,7 @@ private void emitTestRunFinished(Throwable exception) { .setTimestamp(javaInstantToTimestamp(instant)); if (exception != null) { - testRunFinished.setMessage(exception.getMessage()); + testRunFinished.setMessage(printStackTrace(exception)); } bus.send(Envelope.newBuilder() .setTestRunFinished(testRunFinished) diff --git a/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java b/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java index 2a2e188a42..956d74350c 100755 --- a/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java @@ -2,6 +2,7 @@ import io.cucumber.core.backend.StepDefinition; import io.cucumber.core.backend.StubHookDefinition; +import io.cucumber.core.backend.StubStaticHookDefinition; import io.cucumber.core.backend.StubStepDefinition; import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.feature.TestFeatureParser; @@ -30,6 +31,7 @@ import static java.util.Collections.singletonList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.jupiter.api.Assertions.assertThrows; class PrettyFormatterTest { @@ -452,4 +454,33 @@ void should_mark_nested_arguments_as_part_of_enclosing_argument() { AnsiEscapes.GREEN + AnsiEscapes.INTENSITY_BOLD + " and not yet confirmed" + AnsiEscapes.RESET)); } + + @Test + void should_print_system_failure_for_failed_hooks() { + Feature feature = TestFeatureParser.parse("path/test.feature", "" + + "Feature: feature name\n" + + " Scenario: scenario name\n" + + " Given first step\n"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + assertThrows(StubException.class, () -> Runtime.builder() + .withFeatureSupplier(new StubFeatureSupplier(feature)) + .withAdditionalPlugins(new PrettyFormatter(out)) + .withBackendSupplier(new StubBackendSupplier( + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + singletonList(new StubStaticHookDefinition(new StubException("Hook failed", "the stack trace"))))) + .build() + .run()); + + assertThat(out, bytesContainsString("" + + " " + AnsiEscapes.RED + "Hook failed" + AnsiEscapes.RESET + "\n" + + " " + AnsiEscapes.RED + "the stack trace" + AnsiEscapes.RESET + "\n")); + } + + } diff --git a/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java b/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java index 87cdb4d02d..991fe7d837 100755 --- a/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java @@ -336,9 +336,9 @@ void should_print_system_failure_for_failed_hooks() { .run()); assertThat(out, bytesContainsString("" + - "##teamcity[testStarted timestamp = '1970-01-01T12:00:00.000+0000' name = 'Cucumber']\n" + - "##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' message = 'System failure' details = 'the stack trace' name = 'Cucumber']\n" + - "##teamcity[testFinished timestamp = '1970-01-01T12:00:00.000+0000' name = 'Cucumber']")); + "##teamcity[testStarted timestamp = '1970-01-01T12:00:00.000+0000' name = 'Before All/After All']\n" + + "##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' message = 'Before All/ After All failed' details = 'the stack trace' name = 'Before All/After All']\n" + + "##teamcity[testFinished timestamp = '1970-01-01T12:00:00.000+0000' name = 'Before All/After All']")); } } diff --git a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java b/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java index 501bb8af53..ebc5f062b8 100644 --- a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java +++ b/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java @@ -25,6 +25,7 @@ public static void enable_super_math_engine() { @AfterAll public static void disable_super_math_engine() { // System.disableSuperMaths() + throw new RuntimeException("Hello!"); } @Given("a calculator I just turned on") diff --git a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RunCucumberTest.java b/examples/java-calculator/src/test/java/io/cucumber/examples/java/RunCucumberTest.java index 869e8d244e..d20e09bf19 100644 --- a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RunCucumberTest.java +++ b/examples/java-calculator/src/test/java/io/cucumber/examples/java/RunCucumberTest.java @@ -5,7 +5,7 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(plugin = { "html:target/results.html", "message:target/results.ndjson" }) +@CucumberOptions(plugin = { "html:target/results.html", "message:target/results.ndjson", "pretty" }) public class RunCucumberTest { } From 3518c59473d5591e22c60f8a2b0f1a554b9f8850 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 26 Mar 2021 20:00:37 +0100 Subject: [PATCH 15/28] Fix --- .../test/java/io/cucumber/examples/java/RpnCalculatorSteps.java | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java b/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java index ebc5f062b8..501bb8af53 100644 --- a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java +++ b/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java @@ -25,7 +25,6 @@ public static void enable_super_math_engine() { @AfterAll public static void disable_super_math_engine() { // System.disableSuperMaths() - throw new RuntimeException("Hello!"); } @Given("a calculator I just turned on") From ce7967e8e2f7459d55f335cc399b116f6524f6a0 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 26 Mar 2021 20:00:54 +0100 Subject: [PATCH 16/28] Fix --- .../test/java/io/cucumber/examples/java/RunCucumberTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RunCucumberTest.java b/examples/java-calculator/src/test/java/io/cucumber/examples/java/RunCucumberTest.java index d20e09bf19..869e8d244e 100644 --- a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RunCucumberTest.java +++ b/examples/java-calculator/src/test/java/io/cucumber/examples/java/RunCucumberTest.java @@ -5,7 +5,7 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(plugin = { "html:target/results.html", "message:target/results.ndjson", "pretty" }) +@CucumberOptions(plugin = { "html:target/results.html", "message:target/results.ndjson" }) public class RunCucumberTest { } From 27817582ce3a8f5c8cd42938c3352d7ea35b3370 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Fri, 26 Mar 2021 21:24:39 +0100 Subject: [PATCH 17/28] TODO: Extract error collector and move up --- .../cucumber/core/plugin/PrettyFormatter.java | 4 ---- .../io/cucumber/core/plugin/TeamCityPlugin.java | 4 ++-- .../core/backend/StubStaticHookDefinition.java | 1 + .../core/plugin/PrettyFormatterTest.java | 16 +++++++--------- .../core/plugin/TeamCityPluginTest.java | 17 +++++++++-------- .../runtime/CucumberExecutionContextTest.java | 4 ++-- .../examples/testng/RpnCalculatorSteps.java | 1 + 7 files changed, 22 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java b/core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java index 72017fb509..1caed6f449 100644 --- a/core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java +++ b/core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java @@ -146,10 +146,6 @@ private void printError(Result result) { if (error != null) { String name = result.getStatus().name().toLowerCase(ROOT); Format format = formats.get(name); - String message = error.getMessage(); - if (message != null) { - out.println(" " + format.text(error.getMessage())); - } String text = printStackTrace(error); out.println(" " + format.text(text)); } diff --git a/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java b/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java index 25a2dc98dc..a1ceee0d27 100644 --- a/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java +++ b/core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java @@ -51,8 +51,8 @@ * Outputs Teamcity services messages to std out. * * @see TeamCity - * - Service Messages + * href=https://www.jetbrains.com/help/teamcity/service-messages.html>TeamCity + * - Service Messages */ public class TeamCityPlugin implements EventListener { diff --git a/core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java b/core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java index 0ed31f336e..361f74e90b 100644 --- a/core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java +++ b/core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java @@ -32,6 +32,7 @@ public StubStaticHookDefinition(RuntimeException exception) { public StubStaticHookDefinition(String location) { this(location, null, null); } + @Override public void execute() { if (action != null) { diff --git a/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java b/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java index 956d74350c..86fead5f62 100755 --- a/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java @@ -454,7 +454,6 @@ void should_mark_nested_arguments_as_part_of_enclosing_argument() { AnsiEscapes.GREEN + AnsiEscapes.INTENSITY_BOLD + " and not yet confirmed" + AnsiEscapes.RESET)); } - @Test void should_print_system_failure_for_failed_hooks() { Feature feature = TestFeatureParser.parse("path/test.feature", "" + @@ -467,13 +466,13 @@ void should_print_system_failure_for_failed_hooks() { .withFeatureSupplier(new StubFeatureSupplier(feature)) .withAdditionalPlugins(new PrettyFormatter(out)) .withBackendSupplier(new StubBackendSupplier( - emptyList(), - emptyList(), - emptyList(), - emptyList(), - emptyList(), - emptyList(), - singletonList(new StubStaticHookDefinition(new StubException("Hook failed", "the stack trace"))))) + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + singletonList(new StubStaticHookDefinition(new StubException("Hook failed", "the stack trace"))))) .build() .run()); @@ -482,5 +481,4 @@ void should_print_system_failure_for_failed_hooks() { " " + AnsiEscapes.RED + "the stack trace" + AnsiEscapes.RESET + "\n")); } - } diff --git a/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java b/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java index 991fe7d837..36f72cd5e9 100755 --- a/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java @@ -325,19 +325,20 @@ void should_print_system_failure_for_failed_hooks() { .withAdditionalPlugins(new TeamCityPlugin(new PrintStream(out))) .withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID)) .withBackendSupplier(new StubBackendSupplier( - emptyList(), - emptyList(), - emptyList(), - emptyList(), - emptyList(), - emptyList(), - singletonList(new StubStaticHookDefinition(new StubException("Hook failed", "the stack trace"))))) + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + singletonList(new StubStaticHookDefinition(new StubException("Hook failed", "the stack trace"))))) .build() .run()); assertThat(out, bytesContainsString("" + "##teamcity[testStarted timestamp = '1970-01-01T12:00:00.000+0000' name = 'Before All/After All']\n" + - "##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' message = 'Before All/ After All failed' details = 'the stack trace' name = 'Before All/After All']\n" + + "##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' message = 'Before All/ After All failed' details = 'the stack trace' name = 'Before All/After All']\n" + + "##teamcity[testFinished timestamp = '1970-01-01T12:00:00.000+0000' name = 'Before All/After All']")); } diff --git a/core/src/test/java/io/cucumber/core/runtime/CucumberExecutionContextTest.java b/core/src/test/java/io/cucumber/core/runtime/CucumberExecutionContextTest.java index f7d9a40813..8c4615ff9f 100644 --- a/core/src/test/java/io/cucumber/core/runtime/CucumberExecutionContextTest.java +++ b/core/src/test/java/io/cucumber/core/runtime/CucumberExecutionContextTest.java @@ -50,7 +50,7 @@ public void collects_and_rethrows_failures_in_runner() { throw failure; })); assertThat(thrown, is(failure)); - assertThat(context.getException().getCause(), is(failure)); + assertThat(context.getException(), is(failure)); } @Test @@ -87,7 +87,7 @@ public void emits_failures_in_events() { assertThat(testRunStarted.get(0), notNullValue()); Result result = testRunFinished.get(0).getResult(); assertThat(result.getStatus(), is(Status.FAILED)); - assertThat(result.getError().getCause(), is(failure)); + assertThat(result.getError(), is(failure)); } } diff --git a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RpnCalculatorSteps.java b/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RpnCalculatorSteps.java index 1ecea46aea..0429f2410e 100644 --- a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RpnCalculatorSteps.java +++ b/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RpnCalculatorSteps.java @@ -2,6 +2,7 @@ import io.cucumber.java.After; import io.cucumber.java.Before; +import io.cucumber.java.BeforeAll; import io.cucumber.java.DataTableType; import io.cucumber.java.Scenario; import io.cucumber.java.en.Given; From 4b047fb0c316f584e17cd86f4a0a600eefcd9417 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Sat, 27 Mar 2021 23:29:36 +0100 Subject: [PATCH 18/28] This works --- core/src/main/java/io/cucumber/core/runtime/Runtime.java | 6 ++++++ .../java/io/cucumber/core/plugin/PrettyFormatterTest.java | 1 - junit/src/main/java/io/cucumber/junit/Cucumber.java | 1 - 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/cucumber/core/runtime/Runtime.java b/core/src/main/java/io/cucumber/core/runtime/Runtime.java index 043b12e11d..37a999f63b 100644 --- a/core/src/main/java/io/cucumber/core/runtime/Runtime.java +++ b/core/src/main/java/io/cucumber/core/runtime/Runtime.java @@ -1,6 +1,7 @@ package io.cucumber.core.runtime; import io.cucumber.core.eventbus.EventBus; +import io.cucumber.core.exception.ExceptionUtils; import io.cucumber.core.feature.FeatureParser; import io.cucumber.core.filter.Filters; import io.cucumber.core.gherkin.Feature; @@ -84,6 +85,11 @@ public void run() { context.finishTestRun(); } } + + Throwable exception = context.getException(); + if (exception != null) { + ExceptionUtils.throwAsUncheckedException(exception); + } } private void runFeatures() { diff --git a/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java b/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java index 86fead5f62..c1f032c15f 100755 --- a/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java @@ -477,7 +477,6 @@ void should_print_system_failure_for_failed_hooks() { .run()); assertThat(out, bytesContainsString("" + - " " + AnsiEscapes.RED + "Hook failed" + AnsiEscapes.RESET + "\n" + " " + AnsiEscapes.RED + "the stack trace" + AnsiEscapes.RESET + "\n")); } diff --git a/junit/src/main/java/io/cucumber/junit/Cucumber.java b/junit/src/main/java/io/cucumber/junit/Cucumber.java index 956146c190..3e58946015 100644 --- a/junit/src/main/java/io/cucumber/junit/Cucumber.java +++ b/junit/src/main/java/io/cucumber/junit/Cucumber.java @@ -233,7 +233,6 @@ public void evaluate() throws Throwable { plugins.setEventBusOnEventListenerPlugins(bus); } context.startTestRun(); - next.evaluate(); } From 26b75ab009b63f199b3fcbe7faa1a74926611b35 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Sun, 28 Mar 2021 00:23:45 +0100 Subject: [PATCH 19/28] Improve the examples --- .../main/java/io/cucumber/core/cli/Main.java | 4 - .../core/plugin/ProgressFormatter.java | 6 - examples/README.md | 2 +- .../.gitignore | 0 examples/calculator-java-cli/pom.xml | 68 ++++++++ .../examples/calculator}/DateCalculator.java | 2 +- .../examples/calculator}/RpnCalculator.java | 2 +- .../examples/calculator/DateSteps.java | 38 +++++ .../examples/calculator}/ParameterTypes.java | 2 +- .../calculator/RpnCalculatorSteps.java | 104 ++++++++++++ .../examples/calculator/ShoppingSteps.java | 159 ++++++++++++++++++ .../src/test/resources/cucumber.properties | 0 .../calculator}/basic_arithmetic.feature | 0 .../calculator/date_calculator.feature | 0 .../examples/calculator}/shopping.feature | 0 .../.gitignore | 0 .../pom.xml | 28 +-- .../examples/calculator/DateCalculator.java | 17 ++ .../examples/calculator}/RpnCalculator.java | 2 +- .../examples/calculator}/DateSteps.java | 2 +- .../examples/calculator}/ParameterTypes.java | 2 +- .../calculator}/RpnCalculatorSteps.java | 2 +- .../examples/calculator}/RunCucumberTest.java | 2 +- .../examples/calculator}/ShoppingSteps.java | 2 +- .../src/test/resources/cucumber.properties | 0 .../calculator/basic_arithmetic.feature | 36 ++++ .../calculator}/date_calculator.feature | 0 .../examples/calculator}/shopping.feature | 0 examples/calculator-java-junit5/.gitignore | 3 + .../pom.xml | 10 +- .../examples/calculator}/DateCalculator.java | 2 +- .../examples/calculator}/RpnCalculator.java | 2 +- .../calculator/DateStepDefinitions.java | 2 +- .../cucumber/examples/calculator/Example.java | 5 + .../examples}/calculator/ParameterTypes.java | 2 +- .../RpnCalculatorStepDefinitions.java | 2 +- .../examples/calculator/RunCucumberTest.java | 13 ++ .../calculator/ShoppingStepDefinitions.java | 2 +- .../calculator/basic_arithmetic.feature | 0 .../calculator/date_calculator.feature | 8 + .../examples}/calculator/shopping.feature | 0 .../test/resources/junit-platform.properties | 0 .../.gitignore | 0 .../README.md | 0 .../pom.xml | 6 +- .../examples}/calculator/DateCalculator.java | 2 +- .../examples}/calculator/RpnCalculator.java | 2 +- .../examples/calculator}/DateSteps.java | 2 +- .../calculator}/RpnCalculatorSteps.java | 3 +- .../RunCucumberByCompositionBase.java | 2 +- .../RunCucumberByCompositionTest.java | 2 +- .../examples/calculator}/RunCucumberTest.java | 2 +- .../calculator}/ShoppingStepDefinitions.java | 2 +- .../src/test/resources/cucumber.properties | 0 .../calculator}/basic_arithmetic.feature | 0 .../calculator}/date_calculator.feature | 0 .../examples/calculator}/shopping.feature | 0 examples/calculator-java8-cli/pom.xml | 62 +++++++ .../examples/calculator/RpnCalculator.java | 42 +++++ .../calculator}/RpnCalculatorSteps.java | 7 +- .../examples/calculator}/ShoppingSteps.java | 9 +- .../src/test/resources/cucumber.properties | 0 .../calculator}/basic_arithmetic.feature | 0 .../examples/calculator/shopping.feature | 26 +++ .../examples/junit5/calculator/Example.java | 5 - examples/java8-calculator/pom.xml | 40 ----- .../examples/java8/RunCucumberTest.java | 11 -- examples/pom.xml | 13 +- .../pom.xml | 26 +-- .../spring/application}/Application.java | 2 +- .../examples/spring/application}/Message.java | 2 +- .../application}/MessageRepository.java | 2 +- .../examples/spring/application}/User.java | 2 +- .../spring/application}/UserController.java | 2 +- .../spring/application}/UserRepository.java | 2 +- .../src/main/resources/logback.xml | 0 .../src/main/resources/templates/user.html | 0 .../CucumberTestContextConfiguration.java | 2 +- .../application}/MessageStepDefinitions.java | 2 +- .../spring/application/ParameterTypes.java | 21 +++ .../spring/application}/RunCucumberTest.java | 2 +- .../SeeMessagesStepDefinitions.java | 2 +- .../application}/SpringTransactionHooks.java | 2 +- .../application}/UserStepDefinitions.java | 2 +- .../application}/search-messages.feature | 0 .../spring/application}/see-messages.feature | 0 .../test/resources/junit-platform.properties | 1 + .../examples/spring/txn/RunCucumberTest.java | 8 - .../README.md | 0 .../pom.xml | 6 +- .../wicket-main}/pom.xml | 4 +- .../examples/wicket/main}/Application.java | 16 +- .../wicket/main}/model/dao/CarDAO.java | 4 +- .../main}/model/dao/InMemoryCarDAO.java | 4 +- .../wicket/main}/model/entity/Car.java | 2 +- .../examples/wicket/main}/view/Available.java | 4 +- .../examples/wicket/main}/view/Create.java | 4 +- .../examples/wicket/main}/view/Rent.java | 4 +- .../examples/wicket/main}/view/Available.html | 0 .../examples/wicket/main}/view/Create.html | 0 .../examples/wicket/main}/view/Rent.html | 0 .../src/main/resources/logback.xml | 0 .../src/main/webapp/WEB-INF/web.xml | 2 +- .../examples/wicket/main}/RentCarTest.java | 8 +- .../wicket-test}/pom.xml | 8 +- .../examples/wicket/test}/RunCucumberIT.java | 2 +- .../wicket/test}/steps/RentACarSupport.java | 4 +- .../wicket/test}/steps/RentSteps.java | 2 +- .../src/test/resources/cucumber.properties | 1 + .../examples/wicket/main}/Rent.feature | 0 .../src/test/resources/logback-test.xml | 0 .../engine/CucumberTestEngineTest.java | 4 + picocontainer/pom.xml | 2 +- 113 files changed, 722 insertions(+), 208 deletions(-) rename examples/{java-calculator-junit5 => calculator-java-cli}/.gitignore (100%) create mode 100644 examples/calculator-java-cli/pom.xml rename examples/{java-calculator/src/main/java/io/cucumber/examples/java => calculator-java-cli/src/main/java/io/cucumber/examples/calculator}/DateCalculator.java (87%) rename examples/{java-calculator/src/main/java/io/cucumber/examples/java => calculator-java-cli/src/main/java/io/cucumber/examples/calculator}/RpnCalculator.java (96%) create mode 100644 examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/DateSteps.java rename examples/{spring-txn/src/test/java/io/cucumber/examples/spring/txn => calculator-java-cli/src/test/java/io/cucumber/examples/calculator}/ParameterTypes.java (93%) create mode 100644 examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java create mode 100644 examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/ShoppingSteps.java rename examples/{java-calculator-testng => calculator-java-cli}/src/test/resources/cucumber.properties (100%) rename examples/{java-calculator/src/test/resources/io/cucumber/examples/java => calculator-java-cli/src/test/resources/io/cucumber/examples/calculator}/basic_arithmetic.feature (100%) rename examples/{java-calculator-junit5/src/test/resources/io/cucumber/examples/junit5 => calculator-java-cli/src/test/resources/io/cucumber/examples}/calculator/date_calculator.feature (100%) rename examples/{java-calculator/src/test/resources/io/cucumber/examples/java => calculator-java-cli/src/test/resources/io/cucumber/examples/calculator}/shopping.feature (100%) rename examples/{java-calculator => calculator-java-junit4}/.gitignore (100%) rename examples/{java-calculator => calculator-java-junit4}/pom.xml (74%) create mode 100644 examples/calculator-java-junit4/src/main/java/io/cucumber/examples/calculator/DateCalculator.java rename examples/{java8-calculator/src/main/java/io/cucumber/examples/java8 => calculator-java-junit4/src/main/java/io/cucumber/examples/calculator}/RpnCalculator.java (96%) rename examples/{java-calculator/src/test/java/io/cucumber/examples/java => calculator-java-junit4/src/test/java/io/cucumber/examples/calculator}/DateSteps.java (96%) rename examples/{java-calculator/src/test/java/io/cucumber/examples/java => calculator-java-junit4/src/test/java/io/cucumber/examples/calculator}/ParameterTypes.java (93%) rename examples/{java-calculator/src/test/java/io/cucumber/examples/java => calculator-java-junit4/src/test/java/io/cucumber/examples/calculator}/RpnCalculatorSteps.java (98%) rename examples/{java-calculator/src/test/java/io/cucumber/examples/java => calculator-java-junit4/src/test/java/io/cucumber/examples/calculator}/RunCucumberTest.java (86%) rename examples/{java-calculator/src/test/java/io/cucumber/examples/java => calculator-java-junit4/src/test/java/io/cucumber/examples/calculator}/ShoppingSteps.java (99%) rename examples/{java-calculator => calculator-java-junit4}/src/test/resources/cucumber.properties (100%) create mode 100644 examples/calculator-java-junit4/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature rename examples/{java-calculator/src/test/resources/io/cucumber/examples/java => calculator-java-junit4/src/test/resources/io/cucumber/examples/calculator}/date_calculator.feature (100%) rename examples/{java8-calculator/src/test/resources/io/cucumber/examples/java8 => calculator-java-junit4/src/test/resources/io/cucumber/examples/calculator}/shopping.feature (100%) create mode 100644 examples/calculator-java-junit5/.gitignore rename examples/{java-calculator-junit5 => calculator-java-junit5}/pom.xml (91%) rename examples/{java-calculator-testng/src/main/java/io/cucumber/examples/testng => calculator-java-junit5/src/main/java/io/cucumber/examples/calculator}/DateCalculator.java (86%) rename examples/{java-calculator-testng/src/main/java/io/cucumber/examples/testng => calculator-java-junit5/src/main/java/io/cucumber/examples/calculator}/RpnCalculator.java (96%) rename examples/{java-calculator-junit5/src/test/java/io/cucumber/examples/junit5 => calculator-java-junit5/src/test/java/io/cucumber/examples}/calculator/DateStepDefinitions.java (94%) create mode 100644 examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/Example.java rename examples/{java-calculator-junit5/src/test/java/io/cucumber/examples/junit5 => calculator-java-junit5/src/test/java/io/cucumber/examples}/calculator/ParameterTypes.java (92%) rename examples/{java-calculator-junit5/src/test/java/io/cucumber/examples/junit5 => calculator-java-junit5/src/test/java/io/cucumber/examples}/calculator/RpnCalculatorStepDefinitions.java (97%) create mode 100644 examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/RunCucumberTest.java rename examples/{java-calculator-junit5/src/test/java/io/cucumber/examples/junit5 => calculator-java-junit5/src/test/java/io/cucumber/examples}/calculator/ShoppingStepDefinitions.java (96%) rename examples/{java-calculator-junit5/src/test/resources/io/cucumber/examples/junit5 => calculator-java-junit5/src/test/resources/io/cucumber/examples}/calculator/basic_arithmetic.feature (100%) create mode 100644 examples/calculator-java-junit5/src/test/resources/io/cucumber/examples/calculator/date_calculator.feature rename examples/{java-calculator-junit5/src/test/resources/io/cucumber/examples/junit5 => calculator-java-junit5/src/test/resources/io/cucumber/examples}/calculator/shopping.feature (100%) rename examples/{java-calculator-junit5 => calculator-java-junit5}/src/test/resources/junit-platform.properties (100%) rename examples/{java-calculator-testng => calculator-java-testng}/.gitignore (100%) rename examples/{java-calculator-testng => calculator-java-testng}/README.md (100%) rename examples/{java-calculator-testng => calculator-java-testng}/pom.xml (80%) rename examples/{java-calculator-junit5/src/main/java/io/cucumber/examples/junit5 => calculator-java-testng/src/main/java/io/cucumber/examples}/calculator/DateCalculator.java (84%) rename examples/{java-calculator-junit5/src/main/java/io/cucumber/examples/junit5 => calculator-java-testng/src/main/java/io/cucumber/examples}/calculator/RpnCalculator.java (95%) rename examples/{java-calculator-testng/src/test/java/io/cucumber/examples/testng => calculator-java-testng/src/test/java/io/cucumber/examples/calculator}/DateSteps.java (96%) rename examples/{java-calculator-testng/src/test/java/io/cucumber/examples/testng => calculator-java-testng/src/test/java/io/cucumber/examples/calculator}/RpnCalculatorSteps.java (96%) rename examples/{java-calculator-testng/src/test/java/io/cucumber/examples/testng => calculator-java-testng/src/test/java/io/cucumber/examples/calculator}/RunCucumberByCompositionBase.java (89%) rename examples/{java-calculator-testng/src/test/java/io/cucumber/examples/testng => calculator-java-testng/src/test/java/io/cucumber/examples/calculator}/RunCucumberByCompositionTest.java (97%) rename examples/{java-calculator-testng/src/test/java/io/cucumber/examples/testng => calculator-java-testng/src/test/java/io/cucumber/examples/calculator}/RunCucumberTest.java (91%) rename examples/{java-calculator-testng/src/test/java/io/cucumber/examples/testng => calculator-java-testng/src/test/java/io/cucumber/examples/calculator}/ShoppingStepDefinitions.java (97%) rename examples/{java-wicket/java-wicket-test => calculator-java-testng}/src/test/resources/cucumber.properties (100%) rename examples/{java-calculator-testng/src/test/resources/io/cucumber/examples/testng => calculator-java-testng/src/test/resources/io/cucumber/examples/calculator}/basic_arithmetic.feature (100%) rename examples/{java-calculator-testng/src/test/resources/io/cucumber/examples/testng => calculator-java-testng/src/test/resources/io/cucumber/examples/calculator}/date_calculator.feature (100%) rename examples/{java-calculator-testng/src/test/resources/io/cucumber/examples/testng => calculator-java-testng/src/test/resources/io/cucumber/examples/calculator}/shopping.feature (100%) create mode 100644 examples/calculator-java8-cli/pom.xml create mode 100644 examples/calculator-java8-cli/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java rename examples/{java8-calculator/src/test/java/io/cucumber/examples/java8 => calculator-java8-cli/src/test/java/io/cucumber/examples/calculator}/RpnCalculatorSteps.java (90%) rename examples/{java8-calculator/src/test/java/io/cucumber/examples/java8 => calculator-java8-cli/src/test/java/io/cucumber/examples/calculator}/ShoppingSteps.java (93%) rename examples/{java8-calculator => calculator-java8-cli}/src/test/resources/cucumber.properties (100%) rename examples/{java8-calculator/src/test/resources/io/cucumber/examples/java8 => calculator-java8-cli/src/test/resources/io/cucumber/examples/calculator}/basic_arithmetic.feature (100%) create mode 100644 examples/calculator-java8-cli/src/test/resources/io/cucumber/examples/calculator/shopping.feature delete mode 100644 examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/Example.java delete mode 100644 examples/java8-calculator/pom.xml delete mode 100644 examples/java8-calculator/src/test/java/io/cucumber/examples/java8/RunCucumberTest.java rename examples/{spring-txn => spring-java-junit5}/pom.xml (88%) rename examples/{spring-txn/src/main/java/io/cucumber/examples/spring/txn => spring-java-junit5/src/main/java/io/cucumber/examples/spring/application}/Application.java (88%) rename examples/{spring-txn/src/main/java/io/cucumber/examples/spring/txn => spring-java-junit5/src/main/java/io/cucumber/examples/spring/application}/Message.java (96%) rename examples/{spring-txn/src/main/java/io/cucumber/examples/spring/txn => spring-java-junit5/src/main/java/io/cucumber/examples/spring/application}/MessageRepository.java (85%) rename examples/{spring-txn/src/main/java/io/cucumber/examples/spring/txn => spring-java-junit5/src/main/java/io/cucumber/examples/spring/application}/User.java (97%) rename examples/{spring-txn/src/main/java/io/cucumber/examples/spring/txn => spring-java-junit5/src/main/java/io/cucumber/examples/spring/application}/UserController.java (95%) rename examples/{spring-txn/src/main/java/io/cucumber/examples/spring/txn => spring-java-junit5/src/main/java/io/cucumber/examples/spring/application}/UserRepository.java (79%) rename examples/{java-wicket/java-wicket-main => spring-java-junit5}/src/main/resources/logback.xml (100%) rename examples/{spring-txn => spring-java-junit5}/src/main/resources/templates/user.html (100%) rename examples/{spring-txn/src/test/java/io/cucumber/examples/spring/txn => spring-java-junit5/src/test/java/io/cucumber/examples/spring/application}/CucumberTestContextConfiguration.java (86%) rename examples/{spring-txn/src/test/java/io/cucumber/examples/spring/txn => spring-java-junit5/src/test/java/io/cucumber/examples/spring/application}/MessageStepDefinitions.java (97%) create mode 100644 examples/spring-java-junit5/src/test/java/io/cucumber/examples/spring/application/ParameterTypes.java rename examples/{java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator => spring-java-junit5/src/test/java/io/cucumber/examples/spring/application}/RunCucumberTest.java (66%) rename examples/{spring-txn/src/test/java/io/cucumber/examples/spring/txn => spring-java-junit5/src/test/java/io/cucumber/examples/spring/application}/SeeMessagesStepDefinitions.java (96%) rename examples/{spring-txn/src/test/java/io/cucumber/examples/spring/txn => spring-java-junit5/src/test/java/io/cucumber/examples/spring/application}/SpringTransactionHooks.java (97%) rename examples/{spring-txn/src/test/java/io/cucumber/examples/spring/txn => spring-java-junit5/src/test/java/io/cucumber/examples/spring/application}/UserStepDefinitions.java (89%) rename examples/{spring-txn/src/test/resources/io/cucumber/examples/spring/txn => spring-java-junit5/src/test/resources/io/cucumber/examples/spring/application}/search-messages.feature (100%) rename examples/{spring-txn/src/test/resources/io/cucumber/examples/spring/txn => spring-java-junit5/src/test/resources/io/cucumber/examples/spring/application}/see-messages.feature (100%) create mode 100644 examples/spring-java-junit5/src/test/resources/junit-platform.properties delete mode 100644 examples/spring-txn/src/test/java/io/cucumber/examples/spring/txn/RunCucumberTest.java rename examples/{java-wicket => wicket-java-junit4}/README.md (100%) rename examples/{java-wicket => wicket-java-junit4}/pom.xml (80%) rename examples/{java-wicket/java-wicket-main => wicket-java-junit4/wicket-main}/pom.xml (96%) rename examples/{java-wicket/java-wicket-main/src/main/java/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-main/src/main/java/io/cucumber/examples/wicket/main}/Application.java (65%) rename examples/{java-wicket/java-wicket-main/src/main/java/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-main/src/main/java/io/cucumber/examples/wicket/main}/model/dao/CarDAO.java (52%) rename examples/{java-wicket/java-wicket-main/src/main/java/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-main/src/main/java/io/cucumber/examples/wicket/main}/model/dao/InMemoryCarDAO.java (88%) rename examples/{java-wicket/java-wicket-main/src/main/java/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-main/src/main/java/io/cucumber/examples/wicket/main}/model/entity/Car.java (75%) rename examples/{java-wicket/java-wicket-main/src/main/java/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-main/src/main/java/io/cucumber/examples/wicket/main}/view/Available.java (81%) rename examples/{java-wicket/java-wicket-main/src/main/java/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-main/src/main/java/io/cucumber/examples/wicket/main}/view/Create.java (93%) rename examples/{java-wicket/java-wicket-main/src/main/java/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-main/src/main/java/io/cucumber/examples/wicket/main}/view/Rent.java (88%) rename examples/{java-wicket/java-wicket-main/src/main/resources/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-main/src/main/resources/io/cucumber/examples/wicket/main}/view/Available.html (100%) rename examples/{java-wicket/java-wicket-main/src/main/resources/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-main/src/main/resources/io/cucumber/examples/wicket/main}/view/Create.html (100%) rename examples/{java-wicket/java-wicket-main/src/main/resources/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-main/src/main/resources/io/cucumber/examples/wicket/main}/view/Rent.html (100%) rename examples/{spring-txn => wicket-java-junit4/wicket-main}/src/main/resources/logback.xml (100%) rename examples/{java-wicket/java-wicket-main => wicket-java-junit4/wicket-main}/src/main/webapp/WEB-INF/web.xml (91%) rename examples/{java-wicket/java-wicket-main/src/test/java/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-main/src/test/java/io/cucumber/examples/wicket/main}/RentCarTest.java (81%) rename examples/{java-wicket/java-wicket-test => wicket-java-junit4/wicket-test}/pom.xml (95%) rename examples/{java-wicket/java-wicket-test/src/test/java/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-test/src/test/java/io/cucumber/examples/wicket/test}/RunCucumberIT.java (75%) rename examples/{java-wicket/java-wicket-test/src/test/java/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-test/src/test/java/io/cucumber/examples/wicket/test}/steps/RentACarSupport.java (95%) rename examples/{java-wicket/java-wicket-test/src/test/java/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-test/src/test/java/io/cucumber/examples/wicket/test}/steps/RentSteps.java (94%) create mode 100644 examples/wicket-java-junit4/wicket-test/src/test/resources/cucumber.properties rename examples/{java-wicket/java-wicket-test/src/test/resources/io/cucumber/examples/java/wicket => wicket-java-junit4/wicket-test/src/test/resources/io/cucumber/examples/wicket/main}/Rent.feature (100%) rename examples/{java-wicket/java-wicket-test => wicket-java-junit4/wicket-test}/src/test/resources/logback-test.xml (100%) diff --git a/core/src/main/java/io/cucumber/core/cli/Main.java b/core/src/main/java/io/cucumber/core/cli/Main.java index d12979f93b..4660c15d3c 100644 --- a/core/src/main/java/io/cucumber/core/cli/Main.java +++ b/core/src/main/java/io/cucumber/core/cli/Main.java @@ -1,7 +1,5 @@ package io.cucumber.core.cli; -import io.cucumber.core.logging.Logger; -import io.cucumber.core.logging.LoggerFactory; import io.cucumber.core.options.CommandlineOptionsParser; import io.cucumber.core.options.Constants; import io.cucumber.core.options.CucumberProperties; @@ -28,8 +26,6 @@ @API(status = API.Status.STABLE) public class Main { - private static final Logger log = LoggerFactory.getLogger(Main.class); - public static void main(String... argv) { byte exitStatus = run(argv, Thread.currentThread().getContextClassLoader()); System.exit(exitStatus); diff --git a/core/src/main/java/io/cucumber/core/plugin/ProgressFormatter.java b/core/src/main/java/io/cucumber/core/plugin/ProgressFormatter.java index 5351ba2500..44283c9268 100644 --- a/core/src/main/java/io/cucumber/core/plugin/ProgressFormatter.java +++ b/core/src/main/java/io/cucumber/core/plugin/ProgressFormatter.java @@ -7,7 +7,6 @@ import io.cucumber.plugin.event.Status; import io.cucumber.plugin.event.TestRunFinished; import io.cucumber.plugin.event.TestStepFinished; -import io.cucumber.plugin.event.WriteEvent; import java.io.OutputStream; import java.util.HashMap; @@ -51,7 +50,6 @@ public void setMonochrome(boolean monochrome) { @Override public void setEventPublisher(EventPublisher publisher) { publisher.registerHandlerFor(TestStepFinished.class, this::handleTestStepFinished); - publisher.registerHandlerFor(WriteEvent.class, this::handleWrite); publisher.registerHandlerFor(TestRunFinished.class, event -> handleTestRunFinished()); } @@ -67,10 +65,6 @@ private void handleTestStepFinished(TestStepFinished event) { } } - private void handleWrite(WriteEvent event) { - out.append(event.getText()); - } - private void handleTestRunFinished() { out.println(); out.close(); diff --git a/examples/README.md b/examples/README.md index 1d8ac73e4f..1ae0b28524 100644 --- a/examples/README.md +++ b/examples/README.md @@ -13,4 +13,4 @@ cd .. # the dir above this dir mvn clean install ``` -Any of the examples can be built and run with `mvn clean integration-test`. See individual `README.md` files for details. +Any of the examples can be built and run with `mvn clean verify`. diff --git a/examples/java-calculator-junit5/.gitignore b/examples/calculator-java-cli/.gitignore similarity index 100% rename from examples/java-calculator-junit5/.gitignore rename to examples/calculator-java-cli/.gitignore diff --git a/examples/calculator-java-cli/pom.xml b/examples/calculator-java-cli/pom.xml new file mode 100644 index 0000000000..0c667a9210 --- /dev/null +++ b/examples/calculator-java-cli/pom.xml @@ -0,0 +1,68 @@ + + 4.0.0 + + + io.cucumber + cucumber-examples + 6.10.3-SNAPSHOT + + + calculator-java-cli + jar + Examples: Calculator - Annotations - CLI + + + io.cucumber.examples.calculator + 2.12.0 + + + + + io.cucumber + cucumber-java + test + + + org.hamcrest + hamcrest-core + test + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-databind.version} + test + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + cli-test + test + + run + + + + + + + + + + + + + + + + + + + + diff --git a/examples/java-calculator/src/main/java/io/cucumber/examples/java/DateCalculator.java b/examples/calculator-java-cli/src/main/java/io/cucumber/examples/calculator/DateCalculator.java similarity index 87% rename from examples/java-calculator/src/main/java/io/cucumber/examples/java/DateCalculator.java rename to examples/calculator-java-cli/src/main/java/io/cucumber/examples/calculator/DateCalculator.java index fa29904c91..412f9b31d4 100644 --- a/examples/java-calculator/src/main/java/io/cucumber/examples/java/DateCalculator.java +++ b/examples/calculator-java-cli/src/main/java/io/cucumber/examples/calculator/DateCalculator.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.java; +package io.cucumber.examples.calculator; import java.time.LocalDate; diff --git a/examples/java-calculator/src/main/java/io/cucumber/examples/java/RpnCalculator.java b/examples/calculator-java-cli/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java similarity index 96% rename from examples/java-calculator/src/main/java/io/cucumber/examples/java/RpnCalculator.java rename to examples/calculator-java-cli/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java index 7d50370ea8..eee9caea06 100644 --- a/examples/java-calculator/src/main/java/io/cucumber/examples/java/RpnCalculator.java +++ b/examples/calculator-java-cli/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.java; +package io.cucumber.examples.calculator; import java.util.Deque; import java.util.LinkedList; diff --git a/examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/DateSteps.java b/examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/DateSteps.java new file mode 100644 index 0000000000..e23b8df6bf --- /dev/null +++ b/examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/DateSteps.java @@ -0,0 +1,38 @@ +package io.cucumber.examples.calculator; + +import io.cucumber.java.ParameterType; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; + +import java.time.LocalDate; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +public class DateSteps { + + private String result; + private DateCalculator calculator; + + @ParameterType("([0-9]{4})-([0-9]{2})-([0-9]{2})") + public LocalDate iso8601Date(String year, String month, String day) { + return LocalDate.of(Integer.parseInt(year), Integer.parseInt(month), Integer.parseInt(day)); + } + + @Given("today is {iso8601Date}") + public void today_is(LocalDate date) { + calculator = new DateCalculator(date); + } + + @When("I ask if {iso8601Date} is in the past") + public void I_ask_if_date_is_in_the_past(LocalDate date) { + result = calculator.isDateInThePast(date); + } + + @Then("^the result should be (yes|no)$") + public void the_result_should_be(String expectedResult) { + assertThat(result, equalTo(expectedResult)); + } + +} diff --git a/examples/spring-txn/src/test/java/io/cucumber/examples/spring/txn/ParameterTypes.java b/examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/ParameterTypes.java similarity index 93% rename from examples/spring-txn/src/test/java/io/cucumber/examples/spring/txn/ParameterTypes.java rename to examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/ParameterTypes.java index 360539624c..370a9749e7 100644 --- a/examples/spring-txn/src/test/java/io/cucumber/examples/spring/txn/ParameterTypes.java +++ b/examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/ParameterTypes.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.spring.txn; +package io.cucumber.examples.calculator; import com.fasterxml.jackson.databind.ObjectMapper; import io.cucumber.java.DefaultDataTableCellTransformer; diff --git a/examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java b/examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java new file mode 100644 index 0000000000..2cee03442e --- /dev/null +++ b/examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java @@ -0,0 +1,104 @@ +package io.cucumber.examples.calculator; + +import io.cucumber.java.After; +import io.cucumber.java.AfterAll; +import io.cucumber.java.Before; +import io.cucumber.java.BeforeAll; +import io.cucumber.java.Scenario; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +public class RpnCalculatorSteps { + + private RpnCalculator calc; + + @BeforeAll + public static void enable_super_math_engine() { + // System.enableSuperMaths() + } + + @AfterAll + public static void disable_super_math_engine() { + // System.disableSuperMaths() + } + + @Given("a calculator I just turned on") + public void a_calculator_I_just_turned_on() { + calc = new RpnCalculator(); + } + + @When("I add {int} and {int}") + public void adding(int arg1, int arg2) { + calc.push(arg1); + calc.push(arg2); + calc.push("+"); + } + + @Given("^I press (.+)$") + public void I_press(String what) { + calc.push(what); + } + + @Then("the result is {int}") + public void the_result_is(double expected) { + assertThat(calc.value(), equalTo(expected)); + } + + @Before("not @foo") + public void before(Scenario scenario) { + scenario.log("Runs before scenarios *not* tagged with @foo"); + } + + @After + public void after(Scenario scenario) { + // scenario.write("HELLLLOO"); + } + + @Given("the previous entries:") + public void thePreviousEntries(List entries) { + for (Entry entry : entries) { + calc.push(entry.first); + calc.push(entry.second); + calc.push(entry.operation); + } + } + + static final class Entry { + + private Integer first; + private Integer second; + private String operation; + + public Integer getFirst() { + return first; + } + + public void setFirst(Integer first) { + this.first = first; + } + + public Integer getSecond() { + return second; + } + + public void setSecond(Integer second) { + this.second = second; + } + + public String getOperation() { + return operation; + } + + public void setOperation(String operation) { + this.operation = operation; + } + + } + +} diff --git a/examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/ShoppingSteps.java b/examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/ShoppingSteps.java new file mode 100644 index 0000000000..dff23a2975 --- /dev/null +++ b/examples/calculator-java-cli/src/test/java/io/cucumber/examples/calculator/ShoppingSteps.java @@ -0,0 +1,159 @@ +package io.cucumber.examples.calculator; + +import io.cucumber.java.DataTableType; +import io.cucumber.java.DocStringType; +import io.cucumber.java.ParameterType; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; + +import java.math.BigDecimal; +import java.util.Currency; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +public class ShoppingSteps { + + private final RpnCalculator calc = new RpnCalculator(); + + private List shoppingList; + private List shopStock; + private int groceriesPrice; + + @DataTableType + public Grocery defineGrocery(Map entry) { + return new Grocery(entry.get("name"), ShoppingSteps.Price.fromString(entry.get("price"))); + } + + @ParameterType(name = "amount", value = "\\d+\\.\\d+\\s[a-zA-Z]+") + public Amount defineAmount(String value) { + String[] arr = value.split("\\s"); + return new Amount(new BigDecimal(arr[0]), Currency.getInstance(arr[1])); + } + + @DocStringType(contentType = "shopping_list") + public List defineShoppingList(String docstring) { + return Stream.of(docstring.split("\\s")).map(Grocery::new).collect(Collectors.toList()); + } + + @Given("the following groceries:") + public void the_following_groceries(List groceries) { + for (Grocery grocery : groceries) { + calc.push(grocery.price.value); + calc.push("+"); + } + } + + @When("I pay {amount}") + public void i_pay(Amount amount) { + calc.push(amount.price); + calc.push("-"); + } + + @Then("my change should be {}") + public void my_change_should_be_(int change) { + assertThat(-calc.value().intValue(), equalTo(change)); + } + + @Given("the following shopping list:") + public void the_following_shopping_list(List list) { + shoppingList = list; + } + + @Given("the shop has following groceries:") + public void the_shop_has_following_groceries(List shopStock) { + this.shopStock = shopStock; + + } + + @When("I count shopping price") + public void i_count_shopping_price() { + shoppingList.forEach(grocery -> { + for (Grocery shopGrocery : shopStock) { + if (grocery.equals(shopGrocery)) { + groceriesPrice += shopGrocery.price.value; + } + } + }); + } + + @Then("price would be {int}") + public void price_would_be(int totalPrice) { + assertThat(groceriesPrice, equalTo(totalPrice)); + } + + static class Grocery { + + private String name; + private Price price; + + public Grocery(String name, Price price) { + this.name = name; + this.price = price; + } + + public Grocery(String name) { + this.name = name; + } + + public Price getPrice() { + return price; + } + + public void setPrice(Price price) { + this.price = price; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Grocery grocery = (Grocery) o; + return Objects.equals(name, grocery.name); + } + + } + + static final class Price { + + private final int value; + + Price(int value) { + this.value = value; + } + + static Price fromString(String value) { + return new Price(Integer.parseInt(value)); + } + + } + + static final class Amount { + + private final BigDecimal price; + private final Currency currency; + + public Amount(BigDecimal price, Currency currency) { + this.price = price; + this.currency = currency; + } + + } + +} diff --git a/examples/java-calculator-testng/src/test/resources/cucumber.properties b/examples/calculator-java-cli/src/test/resources/cucumber.properties similarity index 100% rename from examples/java-calculator-testng/src/test/resources/cucumber.properties rename to examples/calculator-java-cli/src/test/resources/cucumber.properties diff --git a/examples/java-calculator/src/test/resources/io/cucumber/examples/java/basic_arithmetic.feature b/examples/calculator-java-cli/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature similarity index 100% rename from examples/java-calculator/src/test/resources/io/cucumber/examples/java/basic_arithmetic.feature rename to examples/calculator-java-cli/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature diff --git a/examples/java-calculator-junit5/src/test/resources/io/cucumber/examples/junit5/calculator/date_calculator.feature b/examples/calculator-java-cli/src/test/resources/io/cucumber/examples/calculator/date_calculator.feature similarity index 100% rename from examples/java-calculator-junit5/src/test/resources/io/cucumber/examples/junit5/calculator/date_calculator.feature rename to examples/calculator-java-cli/src/test/resources/io/cucumber/examples/calculator/date_calculator.feature diff --git a/examples/java-calculator/src/test/resources/io/cucumber/examples/java/shopping.feature b/examples/calculator-java-cli/src/test/resources/io/cucumber/examples/calculator/shopping.feature similarity index 100% rename from examples/java-calculator/src/test/resources/io/cucumber/examples/java/shopping.feature rename to examples/calculator-java-cli/src/test/resources/io/cucumber/examples/calculator/shopping.feature diff --git a/examples/java-calculator/.gitignore b/examples/calculator-java-junit4/.gitignore similarity index 100% rename from examples/java-calculator/.gitignore rename to examples/calculator-java-junit4/.gitignore diff --git a/examples/java-calculator/pom.xml b/examples/calculator-java-junit4/pom.xml similarity index 74% rename from examples/java-calculator/pom.xml rename to examples/calculator-java-junit4/pom.xml index 1120d74dd5..b77a67a940 100644 --- a/examples/java-calculator/pom.xml +++ b/examples/calculator-java-junit4/pom.xml @@ -7,12 +7,12 @@ 6.10.3-SNAPSHOT - java-calculator + calculator-java--junit4 jar - Examples: Calculator - Annotation + Examples: Calculator - Annotations - Junit4 - io.cucumber.examples.java + io.cucumber.examples.calculator 2.12.0 @@ -28,34 +28,20 @@ test - org.junit.jupiter - junit-jupiter + org.junit.vintage + junit-vintage-engine test - org.junit.vintage - junit-vintage-engine + org.junit.jupiter + junit-jupiter test - com.fasterxml.jackson.core jackson-databind ${jackson-databind.version} test - - - - - - maven-surefire-plugin - - both - 4 - - - - diff --git a/examples/calculator-java-junit4/src/main/java/io/cucumber/examples/calculator/DateCalculator.java b/examples/calculator-java-junit4/src/main/java/io/cucumber/examples/calculator/DateCalculator.java new file mode 100644 index 0000000000..412f9b31d4 --- /dev/null +++ b/examples/calculator-java-junit4/src/main/java/io/cucumber/examples/calculator/DateCalculator.java @@ -0,0 +1,17 @@ +package io.cucumber.examples.calculator; + +import java.time.LocalDate; + +public class DateCalculator { + + private final LocalDate now; + + public DateCalculator(LocalDate now) { + this.now = now; + } + + public String isDateInThePast(LocalDate date) { + return (date.isBefore(now)) ? "yes" : "no"; + } + +} diff --git a/examples/java8-calculator/src/main/java/io/cucumber/examples/java8/RpnCalculator.java b/examples/calculator-java-junit4/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java similarity index 96% rename from examples/java8-calculator/src/main/java/io/cucumber/examples/java8/RpnCalculator.java rename to examples/calculator-java-junit4/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java index f5b256a301..eee9caea06 100644 --- a/examples/java8-calculator/src/main/java/io/cucumber/examples/java8/RpnCalculator.java +++ b/examples/calculator-java-junit4/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.java8; +package io.cucumber.examples.calculator; import java.util.Deque; import java.util.LinkedList; diff --git a/examples/java-calculator/src/test/java/io/cucumber/examples/java/DateSteps.java b/examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/DateSteps.java similarity index 96% rename from examples/java-calculator/src/test/java/io/cucumber/examples/java/DateSteps.java rename to examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/DateSteps.java index 2f6a90f9c9..8cc4eed3bc 100644 --- a/examples/java-calculator/src/test/java/io/cucumber/examples/java/DateSteps.java +++ b/examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/DateSteps.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.java; +package io.cucumber.examples.calculator; import io.cucumber.java.ParameterType; import io.cucumber.java.en.Given; diff --git a/examples/java-calculator/src/test/java/io/cucumber/examples/java/ParameterTypes.java b/examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/ParameterTypes.java similarity index 93% rename from examples/java-calculator/src/test/java/io/cucumber/examples/java/ParameterTypes.java rename to examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/ParameterTypes.java index f0be019bda..370a9749e7 100644 --- a/examples/java-calculator/src/test/java/io/cucumber/examples/java/ParameterTypes.java +++ b/examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/ParameterTypes.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.java; +package io.cucumber.examples.calculator; import com.fasterxml.jackson.databind.ObjectMapper; import io.cucumber.java.DefaultDataTableCellTransformer; diff --git a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java b/examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java similarity index 98% rename from examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java rename to examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java index 501bb8af53..72f1be43b7 100644 --- a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RpnCalculatorSteps.java +++ b/examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.java; +package io.cucumber.examples.calculator; import io.cucumber.java.After; import io.cucumber.java.AfterAll; diff --git a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RunCucumberTest.java b/examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/RunCucumberTest.java similarity index 86% rename from examples/java-calculator/src/test/java/io/cucumber/examples/java/RunCucumberTest.java rename to examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/RunCucumberTest.java index 869e8d244e..20dec5b46a 100644 --- a/examples/java-calculator/src/test/java/io/cucumber/examples/java/RunCucumberTest.java +++ b/examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/RunCucumberTest.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.java; +package io.cucumber.examples.calculator; import io.cucumber.junit.Cucumber; import io.cucumber.junit.CucumberOptions; diff --git a/examples/java-calculator/src/test/java/io/cucumber/examples/java/ShoppingSteps.java b/examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/ShoppingSteps.java similarity index 99% rename from examples/java-calculator/src/test/java/io/cucumber/examples/java/ShoppingSteps.java rename to examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/ShoppingSteps.java index cdb28329a3..bada980bec 100644 --- a/examples/java-calculator/src/test/java/io/cucumber/examples/java/ShoppingSteps.java +++ b/examples/calculator-java-junit4/src/test/java/io/cucumber/examples/calculator/ShoppingSteps.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.java; +package io.cucumber.examples.calculator; import io.cucumber.java.DataTableType; import io.cucumber.java.DocStringType; diff --git a/examples/java-calculator/src/test/resources/cucumber.properties b/examples/calculator-java-junit4/src/test/resources/cucumber.properties similarity index 100% rename from examples/java-calculator/src/test/resources/cucumber.properties rename to examples/calculator-java-junit4/src/test/resources/cucumber.properties diff --git a/examples/calculator-java-junit4/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature b/examples/calculator-java-junit4/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature new file mode 100644 index 0000000000..39e14b6380 --- /dev/null +++ b/examples/calculator-java-junit4/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature @@ -0,0 +1,36 @@ +@foo +Feature: Basic Arithmetic + + Background: A Calculator + Given a calculator I just turned on + + Scenario: Addition + # Try to change one of the values below to provoke a failure + When I add 4 and 5 + Then the result is 9 + + Scenario: Another Addition + # Try to change one of the values below to provoke a failure + When I add 4 and 7 + Then the result is 11 + + Scenario Outline: Many additions + Given the previous entries: + | first | second | operation | + | 1 | 1 | + | + | 2 | 1 | + | + When I press + + And I add and + And I press + + Then the result is + + Examples: Single digits + | a | b | c | + | 1 | 2 | 8 | + | 2 | 3 | 10 | + + Examples: Double digits + | a | b | c | + | 10 | 20 | 35 | + | 20 | 30 | 55 | + diff --git a/examples/java-calculator/src/test/resources/io/cucumber/examples/java/date_calculator.feature b/examples/calculator-java-junit4/src/test/resources/io/cucumber/examples/calculator/date_calculator.feature similarity index 100% rename from examples/java-calculator/src/test/resources/io/cucumber/examples/java/date_calculator.feature rename to examples/calculator-java-junit4/src/test/resources/io/cucumber/examples/calculator/date_calculator.feature diff --git a/examples/java8-calculator/src/test/resources/io/cucumber/examples/java8/shopping.feature b/examples/calculator-java-junit4/src/test/resources/io/cucumber/examples/calculator/shopping.feature similarity index 100% rename from examples/java8-calculator/src/test/resources/io/cucumber/examples/java8/shopping.feature rename to examples/calculator-java-junit4/src/test/resources/io/cucumber/examples/calculator/shopping.feature diff --git a/examples/calculator-java-junit5/.gitignore b/examples/calculator-java-junit5/.gitignore new file mode 100644 index 0000000000..5e68373517 --- /dev/null +++ b/examples/calculator-java-junit5/.gitignore @@ -0,0 +1,3 @@ +/.settings +/.classpath +/.project diff --git a/examples/java-calculator-junit5/pom.xml b/examples/calculator-java-junit5/pom.xml similarity index 91% rename from examples/java-calculator-junit5/pom.xml rename to examples/calculator-java-junit5/pom.xml index eb36372d90..e30e1c706b 100644 --- a/examples/java-calculator-junit5/pom.xml +++ b/examples/calculator-java-junit5/pom.xml @@ -7,12 +7,12 @@ 6.10.3-SNAPSHOT - java-calculator-junit5 + calculator-java-junit5 jar - Examples: Calculator - Junit 5 + Examples: Calculator - Annotations - Junit 5 - io.cucumber.examples.junit5 + io.cucumber.calculator 2.12.0 @@ -56,8 +56,8 @@ Alternatively annotate a class with `io.cucumber.junit.platform.engine.Cucumber` to mark its package for the discovery of feature files. --> - CLI-test - integration-test + cli-test + test run diff --git a/examples/java-calculator-testng/src/main/java/io/cucumber/examples/testng/DateCalculator.java b/examples/calculator-java-junit5/src/main/java/io/cucumber/examples/calculator/DateCalculator.java similarity index 86% rename from examples/java-calculator-testng/src/main/java/io/cucumber/examples/testng/DateCalculator.java rename to examples/calculator-java-junit5/src/main/java/io/cucumber/examples/calculator/DateCalculator.java index fc626b9bb2..ff7b1c34ab 100644 --- a/examples/java-calculator-testng/src/main/java/io/cucumber/examples/testng/DateCalculator.java +++ b/examples/calculator-java-junit5/src/main/java/io/cucumber/examples/calculator/DateCalculator.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.testng; +package io.cucumber.examples.calculator; import java.util.Date; diff --git a/examples/java-calculator-testng/src/main/java/io/cucumber/examples/testng/RpnCalculator.java b/examples/calculator-java-junit5/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java similarity index 96% rename from examples/java-calculator-testng/src/main/java/io/cucumber/examples/testng/RpnCalculator.java rename to examples/calculator-java-junit5/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java index ba367f20b4..eee9caea06 100644 --- a/examples/java-calculator-testng/src/main/java/io/cucumber/examples/testng/RpnCalculator.java +++ b/examples/calculator-java-junit5/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.testng; +package io.cucumber.examples.calculator; import java.util.Deque; import java.util.LinkedList; diff --git a/examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/DateStepDefinitions.java b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/DateStepDefinitions.java similarity index 94% rename from examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/DateStepDefinitions.java rename to examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/DateStepDefinitions.java index 998a49825c..02326770fc 100644 --- a/examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/DateStepDefinitions.java +++ b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/DateStepDefinitions.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.junit5.calculator; +package io.cucumber.examples.calculator; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; diff --git a/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/Example.java b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/Example.java new file mode 100644 index 0000000000..1f5051f70e --- /dev/null +++ b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/Example.java @@ -0,0 +1,5 @@ +package io.cucumber.examples.calculator; + +public interface Example { + +} diff --git a/examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/ParameterTypes.java b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/ParameterTypes.java similarity index 92% rename from examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/ParameterTypes.java rename to examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/ParameterTypes.java index 2cb56ec728..370a9749e7 100644 --- a/examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/ParameterTypes.java +++ b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/ParameterTypes.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.junit5.calculator; +package io.cucumber.examples.calculator; import com.fasterxml.jackson.databind.ObjectMapper; import io.cucumber.java.DefaultDataTableCellTransformer; diff --git a/examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/RpnCalculatorStepDefinitions.java b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/RpnCalculatorStepDefinitions.java similarity index 97% rename from examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/RpnCalculatorStepDefinitions.java rename to examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/RpnCalculatorStepDefinitions.java index d2b249640c..87e9034de9 100644 --- a/examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/RpnCalculatorStepDefinitions.java +++ b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/RpnCalculatorStepDefinitions.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.junit5.calculator; +package io.cucumber.examples.calculator; import io.cucumber.java.After; import io.cucumber.java.Before; diff --git a/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/RunCucumberTest.java b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/RunCucumberTest.java new file mode 100644 index 0000000000..9d505a8adc --- /dev/null +++ b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/RunCucumberTest.java @@ -0,0 +1,13 @@ +package io.cucumber.examples.calculator; + +import io.cucumber.junit.platform.engine.Cucumber; + +/** + * Work around. Surefire does not use JUnits Test Engine discovery + * functionality. Alternatively execute the the + * org.junit.platform.console.ConsoleLauncher with the maven-antrun-plugin. + */ +@Cucumber +public class RunCucumberTest { + +} diff --git a/examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/ShoppingStepDefinitions.java b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/ShoppingStepDefinitions.java similarity index 96% rename from examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/ShoppingStepDefinitions.java rename to examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/ShoppingStepDefinitions.java index 9eb919174e..788483917c 100644 --- a/examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/ShoppingStepDefinitions.java +++ b/examples/calculator-java-junit5/src/test/java/io/cucumber/examples/calculator/ShoppingStepDefinitions.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.junit5.calculator; +package io.cucumber.examples.calculator; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; diff --git a/examples/java-calculator-junit5/src/test/resources/io/cucumber/examples/junit5/calculator/basic_arithmetic.feature b/examples/calculator-java-junit5/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature similarity index 100% rename from examples/java-calculator-junit5/src/test/resources/io/cucumber/examples/junit5/calculator/basic_arithmetic.feature rename to examples/calculator-java-junit5/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature diff --git a/examples/calculator-java-junit5/src/test/resources/io/cucumber/examples/calculator/date_calculator.feature b/examples/calculator-java-junit5/src/test/resources/io/cucumber/examples/calculator/date_calculator.feature new file mode 100644 index 0000000000..c22dc47c25 --- /dev/null +++ b/examples/calculator-java-junit5/src/test/resources/io/cucumber/examples/calculator/date_calculator.feature @@ -0,0 +1,8 @@ +Feature: Dates with different date formats + This feature shows you can have different date formats, as long as you annotate the + corresponding step definition method accordingly. + + Scenario: Determine past date + Given today is 2011-01-20 + When I ask if 2011-01-19 is in the past + Then the result should be yes diff --git a/examples/java-calculator-junit5/src/test/resources/io/cucumber/examples/junit5/calculator/shopping.feature b/examples/calculator-java-junit5/src/test/resources/io/cucumber/examples/calculator/shopping.feature similarity index 100% rename from examples/java-calculator-junit5/src/test/resources/io/cucumber/examples/junit5/calculator/shopping.feature rename to examples/calculator-java-junit5/src/test/resources/io/cucumber/examples/calculator/shopping.feature diff --git a/examples/java-calculator-junit5/src/test/resources/junit-platform.properties b/examples/calculator-java-junit5/src/test/resources/junit-platform.properties similarity index 100% rename from examples/java-calculator-junit5/src/test/resources/junit-platform.properties rename to examples/calculator-java-junit5/src/test/resources/junit-platform.properties diff --git a/examples/java-calculator-testng/.gitignore b/examples/calculator-java-testng/.gitignore similarity index 100% rename from examples/java-calculator-testng/.gitignore rename to examples/calculator-java-testng/.gitignore diff --git a/examples/java-calculator-testng/README.md b/examples/calculator-java-testng/README.md similarity index 100% rename from examples/java-calculator-testng/README.md rename to examples/calculator-java-testng/README.md diff --git a/examples/java-calculator-testng/pom.xml b/examples/calculator-java-testng/pom.xml similarity index 80% rename from examples/java-calculator-testng/pom.xml rename to examples/calculator-java-testng/pom.xml index c8da9ec021..d8c2eb8097 100644 --- a/examples/java-calculator-testng/pom.xml +++ b/examples/calculator-java-testng/pom.xml @@ -7,12 +7,12 @@ 6.10.3-SNAPSHOT - java-calculator-testng + calculator-java-testng jar - Examples: Calculator - TestNG + Examples: Calculator - Annotations - TestNG - io.cucumber.examples.testng + io.cucumber.examples.calculator diff --git a/examples/java-calculator-junit5/src/main/java/io/cucumber/examples/junit5/calculator/DateCalculator.java b/examples/calculator-java-testng/src/main/java/io/cucumber/examples/calculator/DateCalculator.java similarity index 84% rename from examples/java-calculator-junit5/src/main/java/io/cucumber/examples/junit5/calculator/DateCalculator.java rename to examples/calculator-java-testng/src/main/java/io/cucumber/examples/calculator/DateCalculator.java index 615611274d..ff7b1c34ab 100644 --- a/examples/java-calculator-junit5/src/main/java/io/cucumber/examples/junit5/calculator/DateCalculator.java +++ b/examples/calculator-java-testng/src/main/java/io/cucumber/examples/calculator/DateCalculator.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.junit5.calculator; +package io.cucumber.examples.calculator; import java.util.Date; diff --git a/examples/java-calculator-junit5/src/main/java/io/cucumber/examples/junit5/calculator/RpnCalculator.java b/examples/calculator-java-testng/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java similarity index 95% rename from examples/java-calculator-junit5/src/main/java/io/cucumber/examples/junit5/calculator/RpnCalculator.java rename to examples/calculator-java-testng/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java index e00cb41599..eee9caea06 100644 --- a/examples/java-calculator-junit5/src/main/java/io/cucumber/examples/junit5/calculator/RpnCalculator.java +++ b/examples/calculator-java-testng/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.junit5.calculator; +package io.cucumber.examples.calculator; import java.util.Deque; import java.util.LinkedList; diff --git a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/DateSteps.java b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/DateSteps.java similarity index 96% rename from examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/DateSteps.java rename to examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/DateSteps.java index 90fe3f438a..83c2f0bb6d 100644 --- a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/DateSteps.java +++ b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/DateSteps.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.testng; +package io.cucumber.examples.calculator; import io.cucumber.java.ParameterType; import io.cucumber.java.en.Given; diff --git a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RpnCalculatorSteps.java b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java similarity index 96% rename from examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RpnCalculatorSteps.java rename to examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java index 0429f2410e..65ecf5ab89 100644 --- a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RpnCalculatorSteps.java +++ b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java @@ -1,8 +1,7 @@ -package io.cucumber.examples.testng; +package io.cucumber.examples.calculator; import io.cucumber.java.After; import io.cucumber.java.Before; -import io.cucumber.java.BeforeAll; import io.cucumber.java.DataTableType; import io.cucumber.java.Scenario; import io.cucumber.java.en.Given; diff --git a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RunCucumberByCompositionBase.java b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RunCucumberByCompositionBase.java similarity index 89% rename from examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RunCucumberByCompositionBase.java rename to examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RunCucumberByCompositionBase.java index 74d05dd059..4018d78496 100644 --- a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RunCucumberByCompositionBase.java +++ b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RunCucumberByCompositionBase.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.testng; +package io.cucumber.examples.calculator; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; diff --git a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RunCucumberByCompositionTest.java b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RunCucumberByCompositionTest.java similarity index 97% rename from examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RunCucumberByCompositionTest.java rename to examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RunCucumberByCompositionTest.java index ca6e805f9f..6b1615308c 100644 --- a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RunCucumberByCompositionTest.java +++ b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RunCucumberByCompositionTest.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.testng; +package io.cucumber.examples.calculator; import io.cucumber.testng.CucumberOptions; import io.cucumber.testng.FeatureWrapper; diff --git a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RunCucumberTest.java b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RunCucumberTest.java similarity index 91% rename from examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RunCucumberTest.java rename to examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RunCucumberTest.java index 6bf0809106..82d2b3adfc 100644 --- a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/RunCucumberTest.java +++ b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/RunCucumberTest.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.testng; +package io.cucumber.examples.calculator; import io.cucumber.testng.AbstractTestNGCucumberTests; import io.cucumber.testng.CucumberOptions; diff --git a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/ShoppingStepDefinitions.java b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/ShoppingStepDefinitions.java similarity index 97% rename from examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/ShoppingStepDefinitions.java rename to examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/ShoppingStepDefinitions.java index b212c39388..3038989810 100644 --- a/examples/java-calculator-testng/src/test/java/io/cucumber/examples/testng/ShoppingStepDefinitions.java +++ b/examples/calculator-java-testng/src/test/java/io/cucumber/examples/calculator/ShoppingStepDefinitions.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.testng; +package io.cucumber.examples.calculator; import io.cucumber.java.DataTableType; import io.cucumber.java.en.Given; diff --git a/examples/java-wicket/java-wicket-test/src/test/resources/cucumber.properties b/examples/calculator-java-testng/src/test/resources/cucumber.properties similarity index 100% rename from examples/java-wicket/java-wicket-test/src/test/resources/cucumber.properties rename to examples/calculator-java-testng/src/test/resources/cucumber.properties diff --git a/examples/java-calculator-testng/src/test/resources/io/cucumber/examples/testng/basic_arithmetic.feature b/examples/calculator-java-testng/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature similarity index 100% rename from examples/java-calculator-testng/src/test/resources/io/cucumber/examples/testng/basic_arithmetic.feature rename to examples/calculator-java-testng/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature diff --git a/examples/java-calculator-testng/src/test/resources/io/cucumber/examples/testng/date_calculator.feature b/examples/calculator-java-testng/src/test/resources/io/cucumber/examples/calculator/date_calculator.feature similarity index 100% rename from examples/java-calculator-testng/src/test/resources/io/cucumber/examples/testng/date_calculator.feature rename to examples/calculator-java-testng/src/test/resources/io/cucumber/examples/calculator/date_calculator.feature diff --git a/examples/java-calculator-testng/src/test/resources/io/cucumber/examples/testng/shopping.feature b/examples/calculator-java-testng/src/test/resources/io/cucumber/examples/calculator/shopping.feature similarity index 100% rename from examples/java-calculator-testng/src/test/resources/io/cucumber/examples/testng/shopping.feature rename to examples/calculator-java-testng/src/test/resources/io/cucumber/examples/calculator/shopping.feature diff --git a/examples/calculator-java8-cli/pom.xml b/examples/calculator-java8-cli/pom.xml new file mode 100644 index 0000000000..dfe4a9f92a --- /dev/null +++ b/examples/calculator-java8-cli/pom.xml @@ -0,0 +1,62 @@ + + 4.0.0 + + + io.cucumber + cucumber-examples + 6.10.3-SNAPSHOT + + + calculator-java8-cli + jar + Examples: Calculator - Lambda - CLI + + + io.cucumber.examples.calculator + + + + + io.cucumber + cucumber-java8 + test + + + org.hamcrest + hamcrest-core + test + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + cli-test + test + + run + + + + + + + + + + + + + + + + + + + + diff --git a/examples/calculator-java8-cli/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java b/examples/calculator-java8-cli/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java new file mode 100644 index 0000000000..eee9caea06 --- /dev/null +++ b/examples/calculator-java8-cli/src/main/java/io/cucumber/examples/calculator/RpnCalculator.java @@ -0,0 +1,42 @@ +package io.cucumber.examples.calculator; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +import static java.util.Arrays.asList; + +public class RpnCalculator { + + private static final List OPS = asList("-", "+", "*", "/"); + private final Deque stack = new LinkedList<>(); + + public void push(Object arg) { + if (OPS.contains(arg)) { + Number y = stack.removeLast(); + Number x = stack.isEmpty() ? 0 : stack.removeLast(); + Double val = null; + if (arg.equals("-")) { + val = x.doubleValue() - y.doubleValue(); + } else if (arg.equals("+")) { + val = x.doubleValue() + y.doubleValue(); + } else if (arg.equals("*")) { + val = x.doubleValue() * y.doubleValue(); + } else if (arg.equals("/")) { + val = x.doubleValue() / y.doubleValue(); + } + push(val); + } else { + stack.add((Number) arg); + } + } + + public void PI() { + push(Math.PI); + } + + public Number value() { + return stack.getLast(); + } + +} diff --git a/examples/java8-calculator/src/test/java/io/cucumber/examples/java8/RpnCalculatorSteps.java b/examples/calculator-java8-cli/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java similarity index 90% rename from examples/java8-calculator/src/test/java/io/cucumber/examples/java8/RpnCalculatorSteps.java rename to examples/calculator-java8-cli/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java index 6a3d1edb69..73526180d5 100644 --- a/examples/java8-calculator/src/test/java/io/cucumber/examples/java8/RpnCalculatorSteps.java +++ b/examples/calculator-java8-cli/src/test/java/io/cucumber/examples/calculator/RpnCalculatorSteps.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.java8; +package io.cucumber.examples.calculator; import io.cucumber.datatable.DataTable; import io.cucumber.java8.En; @@ -7,7 +7,8 @@ import java.util.List; import java.util.Map; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; public class RpnCalculatorSteps implements En { @@ -26,7 +27,7 @@ public RpnCalculatorSteps() { Given("^I press (.+)$", (String what) -> calc.push(what)); - Then("the result is {double}", (Double expected) -> assertEquals(expected, calc.value())); + Then("the result is {double}", (Double expected) -> assertThat(calc.value(), equalTo(expected))); Before("not @foo", (Scenario scenario) -> { scenario.log("Runs before scenarios *not* tagged with @foo"); diff --git a/examples/java8-calculator/src/test/java/io/cucumber/examples/java8/ShoppingSteps.java b/examples/calculator-java8-cli/src/test/java/io/cucumber/examples/calculator/ShoppingSteps.java similarity index 93% rename from examples/java8-calculator/src/test/java/io/cucumber/examples/java8/ShoppingSteps.java rename to examples/calculator-java8-cli/src/test/java/io/cucumber/examples/calculator/ShoppingSteps.java index 7de20ba40f..dacaa73795 100644 --- a/examples/java8-calculator/src/test/java/io/cucumber/examples/java8/ShoppingSteps.java +++ b/examples/calculator-java8-cli/src/test/java/io/cucumber/examples/calculator/ShoppingSteps.java @@ -1,4 +1,4 @@ -package io.cucumber.examples.java8; +package io.cucumber.examples.calculator; import io.cucumber.datatable.DataTable; import io.cucumber.java8.En; @@ -11,7 +11,8 @@ import java.util.Objects; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; public class ShoppingSteps implements En { @@ -37,7 +38,7 @@ public ShoppingSteps() { }); Then("my change should be {}", (Integer change) -> { - assertEquals(-calc.value().intValue(), change.intValue()); + assertThat(-calc.value().intValue(), equalTo(change)); }); Given("the following shopping list:", (Grocery[] array) -> { @@ -56,7 +57,7 @@ public ShoppingSteps() { } })); - Then("price would be {int}", (Integer totalPrice) -> assertEquals(groceriesPrice, totalPrice)); + Then("price would be {int}", (Integer totalPrice) -> assertThat(groceriesPrice, equalTo(totalPrice))); DataTableType((Map row) -> new ShoppingSteps.Grocery( row.get("name"), diff --git a/examples/java8-calculator/src/test/resources/cucumber.properties b/examples/calculator-java8-cli/src/test/resources/cucumber.properties similarity index 100% rename from examples/java8-calculator/src/test/resources/cucumber.properties rename to examples/calculator-java8-cli/src/test/resources/cucumber.properties diff --git a/examples/java8-calculator/src/test/resources/io/cucumber/examples/java8/basic_arithmetic.feature b/examples/calculator-java8-cli/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature similarity index 100% rename from examples/java8-calculator/src/test/resources/io/cucumber/examples/java8/basic_arithmetic.feature rename to examples/calculator-java8-cli/src/test/resources/io/cucumber/examples/calculator/basic_arithmetic.feature diff --git a/examples/calculator-java8-cli/src/test/resources/io/cucumber/examples/calculator/shopping.feature b/examples/calculator-java8-cli/src/test/resources/io/cucumber/examples/calculator/shopping.feature new file mode 100644 index 0000000000..6b6af2cca2 --- /dev/null +++ b/examples/calculator-java8-cli/src/test/resources/io/cucumber/examples/calculator/shopping.feature @@ -0,0 +1,26 @@ +Feature: Shopping + + Scenario: Give correct change + Given the following groceries: + | name | price | + | milk | 9 | + | bread | 7 | + | soap | 5 | + When I pay 25.0 USD + Then my change should be 4 + + Scenario: Count shopping list cost + Given the following shopping list: + """shopping_list + milk + bread + coffee + """ + And the shop has following groceries: + | name | price | + | milk | 9 | + | bread | 7 | + | coffee | 2 | + | soap | 5 | + When I count shopping price + Then price would be 18 diff --git a/examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/Example.java b/examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/Example.java deleted file mode 100644 index 1dcb25cedc..0000000000 --- a/examples/java-calculator-junit5/src/test/java/io/cucumber/examples/junit5/calculator/Example.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.cucumber.examples.junit5.calculator; - -public interface Example { - -} diff --git a/examples/java8-calculator/pom.xml b/examples/java8-calculator/pom.xml deleted file mode 100644 index 1696590600..0000000000 --- a/examples/java8-calculator/pom.xml +++ /dev/null @@ -1,40 +0,0 @@ - - 4.0.0 - - - io.cucumber - cucumber-examples - 6.10.3-SNAPSHOT - - - java8-calculator - jar - Examples: Calculator - Lambda - - - io.cucumber.examples.java8 - - - - - io.cucumber - cucumber-java8 - test - - - io.cucumber - cucumber-junit - test - - - org.junit.jupiter - junit-jupiter - test - - - org.junit.vintage - junit-vintage-engine - test - - - diff --git a/examples/java8-calculator/src/test/java/io/cucumber/examples/java8/RunCucumberTest.java b/examples/java8-calculator/src/test/java/io/cucumber/examples/java8/RunCucumberTest.java deleted file mode 100644 index 9443cc4aa5..0000000000 --- a/examples/java8-calculator/src/test/java/io/cucumber/examples/java8/RunCucumberTest.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.cucumber.examples.java8; - -import io.cucumber.junit.Cucumber; -import io.cucumber.junit.CucumberOptions; -import org.junit.runner.RunWith; - -@RunWith(Cucumber.class) -@CucumberOptions(plugin = "message:target/cucumber-report.ndjson") -public class RunCucumberTest { - -} diff --git a/examples/pom.xml b/examples/pom.xml index 99f51619c2..9303e97457 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -12,12 +12,13 @@ Examples - spring-txn - java-calculator - java-calculator-junit5 - java8-calculator - java-calculator-testng - java-wicket + calculator-java8-cli + calculator-java-cli + calculator-java-junit4 + calculator-java-junit5 + calculator-java-testng + spring-java-junit5 + wicket-java-junit4 diff --git a/examples/spring-txn/pom.xml b/examples/spring-java-junit5/pom.xml similarity index 88% rename from examples/spring-txn/pom.xml rename to examples/spring-java-junit5/pom.xml index d93dbb084f..63c61163f8 100644 --- a/examples/spring-txn/pom.xml +++ b/examples/spring-java-junit5/pom.xml @@ -7,12 +7,11 @@ 6.10.3-SNAPSHOT - spring-txn - war + spring-java-junit5 Examples: Spring Transactions - io.cucumber.examples.spring.txn + io.cucumber.examples.spring.application 2.4.1 @@ -29,10 +28,6 @@ - - org.springframework.boot - spring-boot-starter-logging - org.springframework.boot spring-boot-starter-web @@ -41,11 +36,6 @@ org.springframework.boot spring-boot-starter-thymeleaf - - org.springframework.boot - spring-boot-devtools - true - org.springframework.boot spring-boot-starter-data-jpa @@ -54,6 +44,13 @@ com.h2database h2 + + + org.springframework.boot + spring-boot-devtools + true + + org.springframework.boot spring-boot-starter-test @@ -74,11 +71,6 @@ cucumber-junit-platform-engine test - - org.junit.jupiter - junit-jupiter - test - + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.0.0-M5 + From 28f9d3c1e80b1f52a3aab31268cfe9d730fcd56a Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Sat, 3 Apr 2021 22:01:28 +0200 Subject: [PATCH 27/28] Update tests --- .../io/cucumber/core/runner/RunnerTest.java | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/core/src/test/java/io/cucumber/core/runner/RunnerTest.java b/core/src/test/java/io/cucumber/core/runner/RunnerTest.java index f5b7d81ba9..b74477cfb1 100644 --- a/core/src/test/java/io/cucumber/core/runner/RunnerTest.java +++ b/core/src/test/java/io/cucumber/core/runner/RunnerTest.java @@ -144,7 +144,7 @@ public void execute(Object[] args) { Pickle pickleMatchingStepDefinitions = createPickleMatchingStepDefinitions(stepDefinition); - final HookDefinition afterStepHook = addAfterStepHook(); + final HookDefinition afterStepHook = createHook(); TestRunnerSupplier runnerSupplier = new TestRunnerSupplier(bus, runtimeOptions) { @Override @@ -161,17 +161,13 @@ public void loadGlue(Glue glue, List gluePaths) { inOrder.verify(afterStepHook).execute(any(TestCaseState.class)); } - private HookDefinition addAfterStepHook() { - return createHook(); - } - @Test void aftersteps_executed_for_passed_step() { StubStepDefinition stepDefinition = spy(new StubStepDefinition("some step")); Pickle pickle = createPickleMatchingStepDefinitions(stepDefinition); - HookDefinition afteStepHook1 = addAfterStepHook(); - HookDefinition afteStepHook2 = addAfterStepHook(); + HookDefinition afteStepHook1 = createHook(); + HookDefinition afteStepHook2 = createHook(); TestRunnerSupplier runnerSupplier = new TestRunnerSupplier(bus, runtimeOptions) { @Override @@ -192,10 +188,11 @@ public void loadGlue(Glue glue, List gluePaths) { @Test void hooks_execute_also_after_failure() { - final HookDefinition failingBeforeHook = createHook(); + HookDefinition beforeHook = createHook(); + HookDefinition afterHook = createHook(); + + HookDefinition failingBeforeHook = createHook(); doThrow(new RuntimeException("boom")).when(failingBeforeHook).execute(any(TestCaseState.class)); - final HookDefinition beforeHook = createHook(); - final HookDefinition afterHook = createHook(); TestRunnerSupplier runnerSupplier = new TestRunnerSupplier(bus, runtimeOptions) { @Override @@ -248,38 +245,51 @@ public void loadGlue(Glue glue, List gluePaths) { void hooks_not_executed_in_dry_run_mode() { RuntimeOptions runtimeOptions = new RuntimeOptionsBuilder().setDryRun().build(); - final HookDefinition beforeHook = createHook(); - final HookDefinition afterHook = createHook(); - final HookDefinition afterStepHook = addAfterStepHook(); + StaticHookDefinition beforeAllHook = createStaticHook(); + StaticHookDefinition afterAllHook = createStaticHook(); + HookDefinition beforeHook = createHook(); + HookDefinition afterHook = createHook(); + HookDefinition beforeStepHook = createHook(); + HookDefinition afterStepHook = createHook(); TestRunnerSupplier runnerSupplier = new TestRunnerSupplier(bus, runtimeOptions) { @Override public void loadGlue(Glue glue, List gluePaths) { + glue.addBeforeAllHook(beforeAllHook); + glue.addAfterAllHook(afterAllHook); glue.addBeforeHook(beforeHook); - glue.addBeforeHook(afterHook); + glue.addAfterHook(afterHook); + glue.addBeforeStepHook(beforeStepHook); glue.addAfterStepHook(afterStepHook); } }; + runnerSupplier.get().runBeforeAllHooks(); runnerSupplier.get().runPickle(createPicklesWithSteps()); + runnerSupplier.get().runAfterAllHooks(); + verify(beforeAllHook, never()).execute(); + verify(afterAllHook, never()).execute(); verify(beforeHook, never()).execute(any(TestCaseState.class)); - verify(afterStepHook, never()).execute(any(TestCaseState.class)); verify(afterHook, never()).execute(any(TestCaseState.class)); + verify(beforeStepHook, never()).execute(any(TestCaseState.class)); + verify(afterStepHook, never()).execute(any(TestCaseState.class)); } @Test - void hooks_not_executed_for_empty_pickles() { - final HookDefinition beforeHook = createHook(); - final HookDefinition afterHook = createHook(); - final HookDefinition afterStepHook = addAfterStepHook(); + void scenario_hooks_not_executed_for_empty_pickles() { + HookDefinition beforeHook = createHook(); + HookDefinition afterHook = createHook(); + HookDefinition beforeStepHook = createHook(); + HookDefinition afterStepHook = createHook(); TestRunnerSupplier runnerSupplier = new TestRunnerSupplier(bus, runtimeOptions) { @Override public void loadGlue(Glue glue, List gluePaths) { glue.addBeforeHook(beforeHook); - glue.addBeforeHook(afterHook); + glue.addAfterHook(afterHook); + glue.addBeforeStepHook(beforeStepHook); glue.addAfterStepHook(afterStepHook); } }; From 67889c28b02a34af4c0628ba25eb740ace7cf9e3 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Sat, 3 Apr 2021 23:14:50 +0200 Subject: [PATCH 28/28] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0abe4571d..c16e2249e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] (In Git) ### Added + * [Java] Added `BeforeAll` and `AfterAll` hooks ([cucumber/#1876](https://github.com/cucumber/cucumber/pull/1876) M.P. Korstanje) ### Changed * [Core] Updated Cucumber Expressions to v11 ([cucumber/#711](https://github.com/cucumber/cucumber/pull/771) M.P. Korstanje)