diff --git a/core/pom.xml b/core/pom.xml index 25e1e92aaf..e6d3cad2d2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 @@ -21,9 +22,24 @@ + + io.cucumber + cucumber-gherkin + + + io.cucumber + cucumber-gherkin-vintage + + io.cucumber gherkin + ${gherkin-vintage.version} + + + io.cucumber + gherkin-jvm-deps + 1.0.6 io.cucumber @@ -125,6 +141,35 @@ + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.0 + + + package + + shade + + + + + io.cucumber:gherkin + io.cucumber:gherkin-jvm-deps + + + + + gherkin + io.cucumber.core.internal.gherkin + + + + + + + diff --git a/core/src/main/java/io/cucumber/core/eventbus/EventBus.java b/core/src/main/java/io/cucumber/core/eventbus/EventBus.java index 8b70931fb9..4df0859a1a 100644 --- a/core/src/main/java/io/cucumber/core/eventbus/EventBus.java +++ b/core/src/main/java/io/cucumber/core/eventbus/EventBus.java @@ -1,6 +1,7 @@ package io.cucumber.core.eventbus; import java.time.Instant; +import java.util.UUID; import io.cucumber.plugin.event.Event; import io.cucumber.plugin.event.EventPublisher; @@ -9,6 +10,8 @@ public interface EventBus extends EventPublisher { Instant getInstant(); + UUID generateId(); + void send(Event event); void sendAll(Iterable queue); diff --git a/core/src/main/java/io/cucumber/core/feature/Container.java b/core/src/main/java/io/cucumber/core/feature/Container.java deleted file mode 100644 index ee197b768c..0000000000 --- a/core/src/main/java/io/cucumber/core/feature/Container.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.cucumber.core.feature; - -import java.util.stream.Stream; - -public interface Container { - - Stream children(); -} diff --git a/core/src/main/java/io/cucumber/core/feature/CucumberExamples.java b/core/src/main/java/io/cucumber/core/feature/CucumberExamples.java deleted file mode 100644 index 4b07f7888f..0000000000 --- a/core/src/main/java/io/cucumber/core/feature/CucumberExamples.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.cucumber.core.feature; - -import gherkin.ast.Examples; - -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Stream; - -public final class CucumberExamples implements Located, Named, Container { - - private final Examples examples; - - CucumberExamples(Examples examples) { - this.examples = examples; - } - - @Override - public Stream children() { - if (examples.getTableBody() == null) { - return Stream.empty(); - } - - AtomicInteger rowCounter = new AtomicInteger(1); - return examples.getTableBody().stream() - .map(tableRow -> new CucumberExample(tableRow, rowCounter.getAndIncrement())); - } - - @Override - public CucumberLocation getLocation() { - return CucumberLocation.from(examples.getLocation()); - } - - @Override - public String getKeyWord() { - return examples.getKeyword(); - } - - public String getName() { - return examples.getName(); - } -} diff --git a/core/src/main/java/io/cucumber/core/feature/CucumberFeature.java b/core/src/main/java/io/cucumber/core/feature/CucumberFeature.java deleted file mode 100644 index 95bab1e762..0000000000 --- a/core/src/main/java/io/cucumber/core/feature/CucumberFeature.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.cucumber.core.feature; - -import gherkin.ast.GherkinDocument; -import gherkin.ast.ScenarioOutline; - -import java.net.URI; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Stream; - -public final class CucumberFeature implements Located, Named, Container { - private final URI uri; - private final List pickles; - private final GherkinDocument gherkinDocument; - private final String gherkinSource; - - CucumberFeature(GherkinDocument gherkinDocument, URI uri, String gherkinSource, List pickles) { - this.gherkinDocument = gherkinDocument; - this.uri = uri; - this.gherkinSource = gherkinSource; - this.pickles = pickles; - } - - @Override - public Stream children() { - return gherkinDocument.getFeature().getChildren() - .stream() - .map(scenarioDefinition -> { - if (scenarioDefinition instanceof ScenarioOutline) { - ScenarioOutline scenarioOutline = (ScenarioOutline) scenarioDefinition; - return new CucumberScenarioOutline(scenarioOutline); - } - return new CucumberScenario(scenarioDefinition); - }); - } - - public String getKeyword() { - return gherkinDocument.getFeature().getKeyword(); - } - - @Override - public CucumberLocation getLocation() { - return CucumberLocation.from(gherkinDocument.getFeature().getLocation()); - } - - public List getPickles() { - return pickles; - } - - @Override - public String getKeyWord() { - return gherkinDocument.getFeature().getKeyword(); - } - - public String getName() { - return gherkinDocument.getFeature().getName(); - } - - public URI getUri() { - return uri; - } - - public String getSource() { - return gherkinSource; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - CucumberFeature that = (CucumberFeature) o; - return uri.equals(that.uri); - } - - @Override - public int hashCode() { - return Objects.hash(uri); - } - - public Optional getPickleAt(CucumberLocation line) { - return pickles.stream().filter(cucumberPickle -> cucumberPickle.getLocation().equals(line)).findFirst(); - } - -} diff --git a/core/src/main/java/io/cucumber/core/feature/CucumberPickle.java b/core/src/main/java/io/cucumber/core/feature/CucumberPickle.java deleted file mode 100644 index 6204633f7f..0000000000 --- a/core/src/main/java/io/cucumber/core/feature/CucumberPickle.java +++ /dev/null @@ -1,93 +0,0 @@ -package io.cucumber.core.feature; - -import gherkin.GherkinDialect; -import gherkin.ast.GherkinDocument; -import gherkin.pickles.Pickle; -import gherkin.pickles.PickleLocation; -import gherkin.pickles.PickleStep; -import gherkin.pickles.PickleTag; - -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Wraps {@link Pickle} to avoid exposing the gherkin library to all of - * Cucumber. - */ -public final class CucumberPickle implements Located { - - private final Pickle pickle; - private final List steps; - private final URI uri; - - CucumberPickle(Pickle pickle, URI uri, GherkinDocument document, GherkinDialect dialect) { - this.pickle = pickle; - this.uri = uri; - this.steps = createCucumberSteps(pickle, document, dialect); - } - - private static List createCucumberSteps(Pickle pickle, GherkinDocument document, GherkinDialect dialect) { - List list = new ArrayList<>(); - String previousGivenWhenThen = dialect.getGivenKeywords() - .stream() - .filter(s -> !StepType.isAstrix(s)) - .findFirst() - .orElseThrow(() -> new IllegalStateException("No Given keyword for dialect: " + dialect.getName())); - - for (PickleStep step : pickle.getSteps()) { - CucumberStep cucumberStep = new CucumberStep(step, document, dialect, previousGivenWhenThen); - if (cucumberStep.getStepType().isGivenWhenThen()) { - previousGivenWhenThen = cucumberStep.getKeyWord(); - } - list.add(cucumberStep); - } - return list; - } - - public String getLanguage() { - return pickle.getLanguage(); - } - - /** - * Returns the location in feature file of the Scenario this pickle was created - * from. If this pickle was created from a Scenario Outline this line is the - * line in the Example section used to fill in the place holders. - * - * @return line in the feature file - */ - @Override - public CucumberLocation getLocation() { - return CucumberLocation.from(pickle.getLocations().get(0)); - } - - public String getName() { - return pickle.getName(); - } - - /** - * Returns the location in feature file of the Scenario this pickle was created - * from. If this pickle was created from a Scenario Outline this line is the - * - * @return line in the feature file - */ - public CucumberLocation getScenarioLocation() { - List stepLocations = pickle.getLocations(); - PickleLocation scenarioLocation = stepLocations.get(stepLocations.size() - 1); - return CucumberLocation.from(scenarioLocation); - } - - public List getSteps() { - return steps; - } - - public List getTags() { - return pickle.getTags().stream().map(PickleTag::getName).collect(Collectors.toList()); - } - - public URI getUri() { - return uri; - } - -} diff --git a/core/src/main/java/io/cucumber/core/feature/CucumberScenario.java b/core/src/main/java/io/cucumber/core/feature/CucumberScenario.java deleted file mode 100644 index 60e2e335d3..0000000000 --- a/core/src/main/java/io/cucumber/core/feature/CucumberScenario.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.cucumber.core.feature; - -import gherkin.ast.ScenarioDefinition; - -public final class CucumberScenario implements CucumberScenarioDefinition { - - private final ScenarioDefinition scenarioDefinition; - - CucumberScenario(ScenarioDefinition scenarioDefinition) { - this.scenarioDefinition = scenarioDefinition; - } - - public int getLine() { - return scenarioDefinition.getLocation().getLine(); - } - - @Override - public CucumberLocation getLocation() { - return CucumberLocation.from(scenarioDefinition.getLocation()); - } - - @Override - public String getKeyWord() { - return scenarioDefinition.getKeyword(); - } - - public String getName() { - return scenarioDefinition.getName(); - } -} diff --git a/core/src/main/java/io/cucumber/core/feature/CucumberScenarioDefinition.java b/core/src/main/java/io/cucumber/core/feature/CucumberScenarioDefinition.java deleted file mode 100644 index 8ffb1c8aa9..0000000000 --- a/core/src/main/java/io/cucumber/core/feature/CucumberScenarioDefinition.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.cucumber.core.feature; - -public interface CucumberScenarioDefinition extends Located, Named { -} diff --git a/core/src/main/java/io/cucumber/core/feature/CucumberScenarioOutline.java b/core/src/main/java/io/cucumber/core/feature/CucumberScenarioOutline.java deleted file mode 100644 index a1584c5e6e..0000000000 --- a/core/src/main/java/io/cucumber/core/feature/CucumberScenarioOutline.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.cucumber.core.feature; - -import gherkin.ast.ScenarioOutline; - -import java.util.stream.Stream; - -public final class CucumberScenarioOutline implements CucumberScenarioDefinition, Container { - - private final ScenarioOutline scenarioOutline; - - CucumberScenarioOutline(ScenarioOutline scenarioOutline) { - this.scenarioOutline = scenarioOutline; - } - - @Override - public Stream children() { - return scenarioOutline.getExamples().stream() - .map(CucumberExamples::new); - } - - @Override - public CucumberLocation getLocation() { - return CucumberLocation.from(scenarioOutline.getLocation()); - } - - @Override - public String getKeyWord() { - return scenarioOutline.getKeyword(); - } - - public String getName() { - return scenarioOutline.getName(); - } -} diff --git a/core/src/main/java/io/cucumber/core/feature/FeatureParser.java b/core/src/main/java/io/cucumber/core/feature/FeatureParser.java index ed1c2ae7ae..203d5a2d3f 100644 --- a/core/src/main/java/io/cucumber/core/feature/FeatureParser.java +++ b/core/src/main/java/io/cucumber/core/feature/FeatureParser.java @@ -1,44 +1,46 @@ package io.cucumber.core.feature; -import gherkin.AstBuilder; -import gherkin.GherkinDialect; -import gherkin.GherkinDialectProvider; -import gherkin.Parser; -import gherkin.ParserException; -import gherkin.TokenMatcher; -import gherkin.ast.GherkinDocument; -import gherkin.pickles.Compiler; import io.cucumber.core.exception.CucumberException; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.resource.Resource; import java.io.IOException; import java.net.URI; +import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; import java.util.List; -import java.util.stream.Collectors; +import java.util.ServiceLoader; +import java.util.UUID; +import java.util.function.Supplier; +import static java.util.Comparator.comparing; import static java.util.Objects.requireNonNull; -public class FeatureParser { - private FeatureParser() { +public final class FeatureParser { + private final Supplier idGenerator; + + public FeatureParser(Supplier idGenerator) { + this.idGenerator = idGenerator; } - public static CucumberFeature parseResource(Resource resource) { + + public Feature parseResource(Resource resource) { requireNonNull(resource); URI uri = resource.getUri(); String source = read(resource); - - try { - Parser parser = new Parser<>(new AstBuilder()); - TokenMatcher matcher = new TokenMatcher(); - GherkinDocument gherkinDocument = parser.parse(source, matcher); - GherkinDialectProvider dialectProvider = new GherkinDialectProvider(); - List pickles = compilePickles(gherkinDocument, dialectProvider, resource); - return new CucumberFeature(gherkinDocument, uri, source, pickles); - } catch (ParserException e) { - throw new CucumberException("Failed to parse resource at: " + uri.toString(), e); + ServiceLoader services = + ServiceLoader.load(io.cucumber.core.gherkin.FeatureParser.class); + Iterator iterator = services.iterator(); + List parser = new ArrayList<>(); + while (iterator.hasNext()) { + parser.add(iterator.next()); } + Comparator version = + comparing(io.cucumber.core.gherkin.FeatureParser::version); + return Collections.max(parser, version).parse(uri, source, idGenerator); } private static String read(Resource resource) { @@ -50,15 +52,4 @@ private static String read(Resource resource) { } - private static List compilePickles(GherkinDocument document, GherkinDialectProvider dialectProvider, Resource resource) { - if (document.getFeature() == null) { - return Collections.emptyList(); - } - String language = document.getFeature().getLanguage(); - GherkinDialect dialect = dialectProvider.getDialect(language, null); - return new Compiler().compile(document) - .stream() - .map(pickle -> new CucumberPickle(pickle, resource.getUri(), document, dialect)) - .collect(Collectors.toList()); - } } diff --git a/core/src/main/java/io/cucumber/core/feature/Located.java b/core/src/main/java/io/cucumber/core/feature/Located.java deleted file mode 100644 index 0ce1f96121..0000000000 --- a/core/src/main/java/io/cucumber/core/feature/Located.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.cucumber.core.feature; - -public interface Located { - - CucumberLocation getLocation(); -} diff --git a/core/src/main/java/io/cucumber/core/filter/Filters.java b/core/src/main/java/io/cucumber/core/filter/Filters.java index 18b7c3cfa0..4c51e3e376 100644 --- a/core/src/main/java/io/cucumber/core/filter/Filters.java +++ b/core/src/main/java/io/cucumber/core/filter/Filters.java @@ -1,6 +1,6 @@ package io.cucumber.core.filter; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Pickle; import java.net.URI; import java.util.Collection; @@ -9,9 +9,9 @@ import java.util.function.Predicate; import java.util.regex.Pattern; -public final class Filters implements Predicate { +public final class Filters implements Predicate { - private Predicate filter = t -> true; + private Predicate filter = t -> true; public Filters(Options options) { List tagExpressions = options.getTagExpressions(); @@ -29,7 +29,7 @@ public Filters(Options options) { } @Override - public boolean test(CucumberPickle pickle) { + public boolean test(Pickle pickle) { return this.filter.test(pickle); } } diff --git a/core/src/main/java/io/cucumber/core/filter/LinePredicate.java b/core/src/main/java/io/cucumber/core/filter/LinePredicate.java index 46a43b3872..181cc3a168 100644 --- a/core/src/main/java/io/cucumber/core/filter/LinePredicate.java +++ b/core/src/main/java/io/cucumber/core/filter/LinePredicate.java @@ -1,13 +1,13 @@ package io.cucumber.core.filter; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Pickle; import java.net.URI; import java.util.Collection; import java.util.Map; import java.util.function.Predicate; -final class LinePredicate implements Predicate { +final class LinePredicate implements Predicate { private final Map> lineFilters; LinePredicate(Map> lineFilters) { @@ -15,13 +15,13 @@ final class LinePredicate implements Predicate { } @Override - public boolean test(CucumberPickle pickle) { + public boolean test(Pickle pickle) { URI picklePath = pickle.getUri(); if (!lineFilters.containsKey(picklePath)) { return true; } for (Integer line : lineFilters.get(picklePath)) { - if (line == pickle.getLocation().getLine() || line == pickle.getScenarioLocation().getLine()) { + if (line == pickle.getLocation().getLine() || line == pickle.getLocation().getLine()) { return true; } } diff --git a/core/src/main/java/io/cucumber/core/filter/NamePredicate.java b/core/src/main/java/io/cucumber/core/filter/NamePredicate.java index dffd5fb582..fb5bba31bd 100644 --- a/core/src/main/java/io/cucumber/core/filter/NamePredicate.java +++ b/core/src/main/java/io/cucumber/core/filter/NamePredicate.java @@ -1,12 +1,12 @@ package io.cucumber.core.filter; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Pickle; import java.util.List; import java.util.function.Predicate; import java.util.regex.Pattern; -final class NamePredicate implements Predicate { +final class NamePredicate implements Predicate { private final List patterns; NamePredicate(List patterns) { @@ -14,7 +14,7 @@ final class NamePredicate implements Predicate { } @Override - public boolean test(CucumberPickle pickle) { + public boolean test(Pickle pickle) { String name = pickle.getName(); return patterns.stream().anyMatch(pattern -> pattern.matcher(name).find()); } diff --git a/core/src/main/java/io/cucumber/core/filter/TagPredicate.java b/core/src/main/java/io/cucumber/core/filter/TagPredicate.java index 6bc0a0d240..2410364ec3 100644 --- a/core/src/main/java/io/cucumber/core/filter/TagPredicate.java +++ b/core/src/main/java/io/cucumber/core/filter/TagPredicate.java @@ -1,6 +1,6 @@ package io.cucumber.core.filter; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.tagexpressions.Expression; import io.cucumber.tagexpressions.TagExpressionParser; @@ -12,7 +12,7 @@ import static java.util.Collections.singletonList; -final class TagPredicate implements Predicate { +final class TagPredicate implements Predicate { private final List expressions = new ArrayList<>(); TagPredicate(String tagExpression) { @@ -30,7 +30,7 @@ final class TagPredicate implements Predicate { } @Override - public boolean test(CucumberPickle pickle) { + public boolean test(Pickle pickle) { if (expressions.isEmpty()) { return true; } diff --git a/core/src/main/java/io/cucumber/core/options/PluginOption.java b/core/src/main/java/io/cucumber/core/options/PluginOption.java index 696216b02f..a70b066248 100644 --- a/core/src/main/java/io/cucumber/core/options/PluginOption.java +++ b/core/src/main/java/io/cucumber/core/options/PluginOption.java @@ -64,29 +64,6 @@ private PluginOption(String pluginString, Class pluginClass, S this.argument = argument; } - @Override - public Class pluginClass() { - return pluginClass; - } - - @Override - public String argument() { - return argument; - } - - @Override - public String pluginString() { - return pluginString; - } - - boolean isFormatter() { - return EventListener.class.isAssignableFrom(pluginClass) || ConcurrentEventListener.class.isAssignableFrom(pluginClass); - } - - boolean isSummaryPrinter() { - return SummaryPrinter.class.isAssignableFrom(pluginClass); - } - public static PluginOption parse(String pluginArgumentPattern) { Matcher pluginWithFile = PLUGIN_WITH_ARGUMENT_PATTERN.matcher(pluginArgumentPattern); if (!pluginWithFile.matches()) { @@ -125,5 +102,28 @@ private static Class loadClass(String className) { } } + @Override + public Class pluginClass() { + return pluginClass; + } + + @Override + public String argument() { + return argument; + } + + @Override + public String pluginString() { + return pluginString; + } + + boolean isFormatter() { + return EventListener.class.isAssignableFrom(pluginClass) || ConcurrentEventListener.class.isAssignableFrom(pluginClass); + } + + boolean isSummaryPrinter() { + return SummaryPrinter.class.isAssignableFrom(pluginClass); + } + } diff --git a/core/src/main/java/io/cucumber/core/order/PickleOrder.java b/core/src/main/java/io/cucumber/core/order/PickleOrder.java index 3fcb4e862d..0d5ca0712d 100644 --- a/core/src/main/java/io/cucumber/core/order/PickleOrder.java +++ b/core/src/main/java/io/cucumber/core/order/PickleOrder.java @@ -1,10 +1,10 @@ package io.cucumber.core.order; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Pickle; import java.util.List; public interface PickleOrder { - List orderPickles(List pickles); + List orderPickles(List pickles); } diff --git a/core/src/main/java/io/cucumber/core/order/StandardPickleOrders.java b/core/src/main/java/io/cucumber/core/order/StandardPickleOrders.java index 85660d76d1..36d5ff20c6 100644 --- a/core/src/main/java/io/cucumber/core/order/StandardPickleOrders.java +++ b/core/src/main/java/io/cucumber/core/order/StandardPickleOrders.java @@ -1,6 +1,6 @@ package io.cucumber.core.order; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Pickle; import java.util.Collections; import java.util.Comparator; @@ -33,10 +33,10 @@ public static PickleOrder random(final long seed) { }; } - private static class PickleUriComparator implements Comparator { + private static class PickleUriComparator implements Comparator { @Override - public int compare(CucumberPickle a, CucumberPickle b) { + public int compare(Pickle a, Pickle b) { return a.getUri().compareTo(b.getUri()); } } diff --git a/core/src/main/java/io/cucumber/core/plugin/CanonicalEventOrder.java b/core/src/main/java/io/cucumber/core/plugin/CanonicalEventOrder.java index d8a49f51cc..b82a898102 100644 --- a/core/src/main/java/io/cucumber/core/plugin/CanonicalEventOrder.java +++ b/core/src/main/java/io/cucumber/core/plugin/CanonicalEventOrder.java @@ -75,7 +75,7 @@ public int compare(final Event a, final Event b) { private int requireInFixOrder(Class o) { int index = findInFixedOrder(o); if (index < 0) { - throw new IllegalStateException(o + "was not in " + fixedOrder); + throw new IllegalStateException(o + " was not in " + fixedOrder); } return index; } diff --git a/core/src/main/java/io/cucumber/core/plugin/JUnitFormatter.java b/core/src/main/java/io/cucumber/core/plugin/JUnitFormatter.java index 0552349ea1..c1212224f2 100644 --- a/core/src/main/java/io/cucumber/core/plugin/JUnitFormatter.java +++ b/core/src/main/java/io/cucumber/core/plugin/JUnitFormatter.java @@ -1,6 +1,8 @@ package io.cucumber.core.plugin; import io.cucumber.core.exception.CucumberException; +import io.cucumber.core.feature.FeatureParser; +import io.cucumber.core.gherkin.Feature; import io.cucumber.plugin.EventListener; import io.cucumber.plugin.StrictAware; import io.cucumber.plugin.event.EventPublisher; @@ -37,8 +39,11 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.UUID; import static java.util.Locale.ROOT; import static java.util.concurrent.TimeUnit.SECONDS; @@ -49,7 +54,6 @@ public final class JUnitFormatter implements EventListener, StrictAware { private final Writer writer; private final Document document; private final Element rootElement; - private final TestSourcesModel testSources = new TestSourcesModel(); private Element root; private TestCase testCase; private boolean strict = false; @@ -57,6 +61,8 @@ public final class JUnitFormatter implements EventListener, StrictAware { private String previousTestCaseName; private int exampleNumber; private Instant started; + private final Map featuresNames = new HashMap<>(); + private final FeatureParser parser = new FeatureParser(UUID::randomUUID); @SuppressWarnings("WeakerAccess") // Used by plugin factory public JUnitFormatter(URL writer) throws IOException { @@ -100,7 +106,8 @@ public void setStrict(boolean strict) { } private void handleTestSourceRead(TestSourceRead event) { - testSources.addTestSourceReadEvent(event.getUri(), event); + Feature feature = parser.parseResource(new TestSourceReadResource(event)); + featuresNames.put(feature.getUri(), feature.getName()); } private void handleTestCaseStarted(TestCaseStarted event) { @@ -186,7 +193,7 @@ Element createElement(Document doc) { } void writeElement(Element tc) { - tc.setAttribute("classname", testSources.getFeatureName(currentFeatureFile)); + tc.setAttribute("classname", featuresNames.get(currentFeatureFile)); tc.setAttribute("name", calculateElementName(testCase)); } @@ -248,7 +255,7 @@ private void addStepAndResultListing(StringBuilder sb) { if (i < results.size()) { resultStatus = results.get(i).getStatus().name().toLowerCase(ROOT); } - sb.append(getKeywordFromSource(steps.get(i).getStepLine())); + sb.append(steps.get(i).getStep().getKeyWord()); sb.append(steps.get(i).getStepText()); do { sb.append("."); @@ -258,10 +265,6 @@ private void addStepAndResultListing(StringBuilder sb) { } } - private String getKeywordFromSource(int stepLine) { - return testSources.getKeywordFromSource(currentFeatureFile, stepLine); - } - private void addStackTrace(StringBuilder sb, Result failed) { sb.append("\nStackTrace:\n"); sb.append(printStackTrace(failed.getError())); 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 780984101f..4dc43623e4 100644 --- a/core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java +++ b/core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java @@ -1,26 +1,17 @@ package io.cucumber.core.plugin; -import gherkin.ast.Background; -import gherkin.ast.Examples; -import gherkin.ast.Feature; -import gherkin.ast.ScenarioDefinition; -import gherkin.ast.ScenarioOutline; -import gherkin.ast.Step; -import gherkin.ast.Tag; import io.cucumber.core.exception.CucumberException; import io.cucumber.plugin.ColorAware; -import io.cucumber.plugin.EventListener; +import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.event.Argument; +import io.cucumber.plugin.event.EmbedEvent; import io.cucumber.plugin.event.EventPublisher; import io.cucumber.plugin.event.PickleStepTestStep; import io.cucumber.plugin.event.Result; import io.cucumber.plugin.event.TestCase; import io.cucumber.plugin.event.TestCaseStarted; import io.cucumber.plugin.event.TestRunFinished; -import io.cucumber.plugin.event.TestSourceRead; -import io.cucumber.plugin.event.TestStep; import io.cucumber.plugin.event.TestStepFinished; -import io.cucumber.plugin.event.TestStepStarted; import io.cucumber.plugin.event.WriteEvent; import java.io.BufferedReader; @@ -28,26 +19,25 @@ import java.io.PrintWriter; import java.io.StringReader; import java.io.StringWriter; -import java.net.URI; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.UUID; import static io.cucumber.core.plugin.TestSourcesModel.relativize; +import static java.lang.Math.max; import static java.util.Locale.ROOT; -import static java.util.stream.Collectors.joining; - -public final class PrettyFormatter implements EventListener, ColorAware { - private static final String SCENARIO_INDENT = " "; - private static final String STEP_INDENT = " "; - private static final String EXAMPLES_INDENT = " "; - private static final String STEP_SCENARIO_INDENT = " "; - private final TestSourcesModel testSources = new TestSourcesModel(); + +public final class PrettyFormatter implements ConcurrentEventListener, ColorAware { + private static final String SCENARIO_INDENT = ""; + private static final String STEP_INDENT = " "; + private static final String STEP_SCENARIO_INDENT = " "; + + private final Map commentStartIndex = new HashMap<>(); + private final NiceAppendable out; private Formats formats; - private URI currentFeatureFile; - private TestCase currentTestCase; - private ScenarioOutline currentScenarioOutline; - private Examples currentExamples; - private int locationIndentation; @SuppressWarnings("WeakerAccess") // Used by PluginFactory public PrettyFormatter(Appendable out) { @@ -57,12 +47,11 @@ public PrettyFormatter(Appendable out) { @Override public void setEventPublisher(EventPublisher publisher) { - publisher.registerHandlerFor(TestSourceRead.class, this::handleTestSourceRead); publisher.registerHandlerFor(TestCaseStarted.class, this::handleTestCaseStarted); - publisher.registerHandlerFor(TestStepStarted.class, this::handleTestStepStarted); publisher.registerHandlerFor(TestStepFinished.class, this::handleTestStepFinished); publisher.registerHandlerFor(WriteEvent.class, this::handleWrite); - publisher.registerHandlerFor(TestRunFinished.class, event -> finishReport()); + publisher.registerHandlerFor(EmbedEvent.class, this::handleEmbed); + publisher.registerHandlerFor(TestRunFinished.class, this::handleTestRunFinished); } @Override @@ -74,107 +63,126 @@ public void setMonochrome(boolean monochrome) { } } - private void handleTestSourceRead(TestSourceRead event) { - testSources.addTestSourceReadEvent(event.getUri(), event); + private void handleTestCaseStarted(TestCaseStarted event) { + out.println(); + preCalculateLocationIndent(event); + printTags(event); + printScenarioDefinition(event); } - private void handleTestCaseStarted(TestCaseStarted event) { - handleStartOfFeature(event); - handleScenarioOutline(event); - if (testSources.hasBackground(currentFeatureFile, event.getTestCase().getLine())) { - printBackground(event.getTestCase()); - currentTestCase = event.getTestCase(); - } else { - printScenarioDefinition(event.getTestCase()); - } + private void preCalculateLocationIndent(TestCaseStarted event) { + TestCase testCase = event.getTestCase(); + Integer longestStep = testCase.getTestSteps().stream() + .filter(PickleStepTestStep.class::isInstance) + .map(PickleStepTestStep.class::cast) + .map(PickleStepTestStep::getStep) + .map(step -> formatPlainStep(step.getKeyWord(), step.getText()).length()) + .max(Comparator.naturalOrder()) + .orElse(0); + + int scenarioLength = formatScenarioDefinition(testCase).length(); + commentStartIndex.put(testCase.getId(), max(longestStep, scenarioLength) + 1); } - private void handleTestStepStarted(TestStepStarted event) { - if (event.getTestStep() instanceof PickleStepTestStep) { - if (isFirstStepAfterBackground((PickleStepTestStep) event.getTestStep())) { - printScenarioDefinition(currentTestCase); - currentTestCase = null; - } + + private void printTags(TestCaseStarted event) { + List tags = event.getTestCase().getTags(); + if (!tags.isEmpty()) { + out.println(PrettyFormatter.SCENARIO_INDENT + String.join(" ", tags)); } } + private void printScenarioDefinition(TestCaseStarted event) { + TestCase testCase = event.getTestCase(); + String definitionText = formatScenarioDefinition(testCase); + String path = relativize(testCase.getUri()).getSchemeSpecificPart(); + String locationIndent = calculateLocationIndent(event.getTestCase(), SCENARIO_INDENT + definitionText); + out.println(SCENARIO_INDENT + definitionText + locationIndent + formatLocation(path + ":" + testCase.getLine())); + } + + private String formatScenarioDefinition(TestCase testCase) { + return testCase.getKeyword() + ": " + testCase.getName(); + } + private void handleTestStepFinished(TestStepFinished event) { + printStep(event); + printError(event); + } + + private void printStep(TestStepFinished event) { if (event.getTestStep() instanceof PickleStepTestStep) { - printStep((PickleStepTestStep) event.getTestStep(), event.getResult()); + PickleStepTestStep testStep = (PickleStepTestStep) event.getTestStep(); + String keyword = testStep.getStep().getKeyWord(); + String stepText = testStep.getStep().getText(); + String status = event.getResult().getStatus().name().toLowerCase(ROOT); + String formattedStepText = formatStepText(keyword, stepText, formats.get(status), formats.get(status + "_arg"), testStep.getDefinitionArgument()); + String locationIndent = calculateLocationIndent(event.getTestCase(), formatPlainStep(keyword, stepText)); + out.println(STEP_INDENT + formattedStepText + locationIndent + formatLocation(testStep.getCodeLocation())); } - printError(event.getResult()); } - private void handleWrite(WriteEvent event) { - out.println(); - try (BufferedReader lines = new BufferedReader(new StringReader(event.getText()))) { - String line; - while ((line = lines.readLine()) != null) { - out.println(STEP_SCENARIO_INDENT + line); - } - } catch (IOException e) { - throw new CucumberException(e); + private String formatPlainStep(String keyword, String stepText) { + return STEP_INDENT + keyword + stepText; + } + + private String calculateLocationIndent(TestCase testStep, String prefix) { + Integer commentStartAt = commentStartIndex.getOrDefault(testStep.getId(), 0); + int padding = commentStartAt - prefix.length(); + + if (padding < 0) { + return " "; } - out.println(); + StringBuilder builder = new StringBuilder(padding); + for (int i = 0; i < padding; i++) { + builder.append(" "); + } + return builder.toString(); } - private void finishReport() { - out.close(); + private void printError(TestStepFinished event) { + Result result = event.getResult(); + Throwable error = result.getError(); + if (error != null) { + String name = result.getStatus().name().toLowerCase(ROOT); + String text = formatStackTrace(error); + out.println(" " + formats.get(name).text(text)); + } } - private void handleStartOfFeature(TestCaseStarted event) { - if (currentFeatureFile == null || !currentFeatureFile.equals(event.getTestCase().getUri())) { - if (currentFeatureFile != null) { - out.println(); - } - currentFeatureFile = event.getTestCase().getUri(); - printFeature(currentFeatureFile); - } + + private void handleWrite(WriteEvent event) { + out.println(); + printText(event); + out.println(); + } - private void handleScenarioOutline(TestCaseStarted event) { - TestSourcesModel.AstNode astNode = testSources.getAstNode(currentFeatureFile, event.getTestCase().getLine()); - if (TestSourcesModel.isScenarioOutlineScenario(astNode)) { - ScenarioOutline scenarioOutline = (ScenarioOutline) TestSourcesModel.getScenarioDefinition(astNode); - if (currentScenarioOutline == null || !currentScenarioOutline.equals(scenarioOutline)) { - currentScenarioOutline = scenarioOutline; - printScenarioOutline(currentScenarioOutline); - } - if (currentExamples == null || !currentExamples.equals(astNode.parent.node)) { - currentExamples = (Examples) astNode.parent.node; - printExamples(currentExamples); + private void printText(WriteEvent event) { + try (BufferedReader lines = new BufferedReader(new StringReader(event.getText()))) { + String line; + while ((line = lines.readLine()) != null) { + out.println(STEP_SCENARIO_INDENT + line); } - } else { - currentScenarioOutline = null; - currentExamples = null; + } catch (IOException e) { + throw new CucumberException(e); } } - private void printScenarioOutline(ScenarioOutline scenarioOutline) { + private void handleEmbed(EmbedEvent event) { out.println(); - printTags(scenarioOutline.getTags(), SCENARIO_INDENT); - out.println(SCENARIO_INDENT + getScenarioDefinitionText(scenarioOutline) + " " + getLocationText(currentFeatureFile, scenarioOutline.getLocation().getLine())); - printDescription(scenarioOutline.getDescription()); - for (Step step : scenarioOutline.getSteps()) { - out.println(STEP_INDENT + formats.get("skipped").text(step.getKeyword() + step.getText())); - } + printEmbedding(event); + out.println(); + } - private void printExamples(Examples examples) { - out.println(); - printTags(examples.getTags(), EXAMPLES_INDENT); - out.println(EXAMPLES_INDENT + examples.getKeyword() + ": " + examples.getName()); - printDescription(examples.getDescription()); + private void printEmbedding(EmbedEvent event) { + String line = "Embedding " + event.getName() + " [" + event.getMediaType() + " " + event.getData().length + " bytes]"; + out.println(STEP_SCENARIO_INDENT + line); } - private void printStep(PickleStepTestStep testStep, Result result) { - String keyword = getStepKeyword(testStep); - String stepText = testStep.getStepText(); - String locationPadding = createPaddingToLocation(STEP_INDENT, keyword + stepText); - String status = result.getStatus().name().toLowerCase(ROOT); - String formattedStepText = formatStepText(keyword, stepText, formats.get(status), formats.get(status + "_arg"), testStep.getDefinitionArgument()); - out.println(STEP_INDENT + formattedStepText + locationPadding + getLocationText(testStep.getCodeLocation())); + private void handleTestRunFinished(TestRunFinished event) { + out.close(); } String formatStepText(String keyword, String stepText, Format textFormat, Format argFormat, List arguments) { @@ -206,135 +214,11 @@ String formatStepText(String keyword, String stepText, Format textFormat, Format return result.toString(); } - private String getScenarioDefinitionText(ScenarioDefinition definition) { - return definition.getKeyword() + ": " + definition.getName(); - } - - private String getLocationText(URI file, int line) { - String path = relativize(file).getSchemeSpecificPart(); - return getLocationText(path + ":" + line); - } - - private String getLocationText(String location) { + private String formatLocation(String location) { return formats.get("comment").text("# " + location); } - private StringBuffer stepText(PickleStepTestStep testStep) { - String keyword = getStepKeyword(testStep); - return new StringBuffer(keyword + testStep.getStepText()); - } - - private String getStepKeyword(PickleStepTestStep testStep) { - TestSourcesModel.AstNode astNode = testSources.getAstNode(currentFeatureFile, testStep.getStepLine()); - if (astNode != null) { - Step step = (Step) astNode.node; - return step.getKeyword(); - } else { - return ""; - } - } - - private boolean isFirstStepAfterBackground(PickleStepTestStep testStep) { - return currentTestCase != null && !isBackgroundStep(testStep); - } - - private boolean isBackgroundStep(PickleStepTestStep testStep) { - TestSourcesModel.AstNode astNode = testSources.getAstNode(currentFeatureFile, testStep.getStepLine()); - if (astNode != null) { - return TestSourcesModel.isBackgroundStep(astNode); - } - return false; - } - - private void printFeature(URI path) { - Feature feature = testSources.getFeature(path); - printTags(feature.getTags()); - out.println(feature.getKeyword() + ": " + feature.getName()); - printDescription(feature.getDescription()); - } - - private void printTags(List tags) { - printTags(tags, ""); - } - - private void printTags(List tags, String indent) { - if (!tags.isEmpty()) { - out.println(indent + tags.stream().map(Tag::getName).collect(joining(" "))); - } - } - - private void printPickleTags(List tags, String indent) { - if (!tags.isEmpty()) { - out.println(indent + String.join(" ", tags)); - } - } - - private void printDescription(String description) { - if (description != null) { - out.println(description); - } - } - - private void printBackground(TestCase testCase) { - TestSourcesModel.AstNode astNode = testSources.getAstNode(currentFeatureFile, testCase.getLine()); - if (astNode != null) { - Background background = TestSourcesModel.getBackgroundForTestCase(astNode); - String backgroundText = getScenarioDefinitionText(background); - boolean useBackgroundSteps = true; - calculateLocationIndentation(SCENARIO_INDENT + backgroundText, testCase.getTestSteps(), useBackgroundSteps); - String locationPadding = createPaddingToLocation(SCENARIO_INDENT, backgroundText); - out.println(); - out.println(SCENARIO_INDENT + backgroundText + locationPadding + getLocationText(currentFeatureFile, background.getLocation().getLine())); - printDescription(background.getDescription()); - } - } - - private void printScenarioDefinition(TestCase testCase) { - ScenarioDefinition scenarioDefinition = testSources.getScenarioDefinition(currentFeatureFile, testCase.getLine()); - String definitionText = scenarioDefinition.getKeyword() + ": " + testCase.getName(); - calculateLocationIndentation(SCENARIO_INDENT + definitionText, testCase.getTestSteps()); - String locationPadding = createPaddingToLocation(SCENARIO_INDENT, definitionText); - out.println(); - printPickleTags(testCase.getTags(), SCENARIO_INDENT); - out.println(SCENARIO_INDENT + definitionText + locationPadding + getLocationText(currentFeatureFile, testCase.getLine())); - printDescription(scenarioDefinition.getDescription()); - } - - private void printError(Result result) { - if (result.getError() != null) { - String name = result.getStatus().name().toLowerCase(ROOT); - out.println(" " + formats.get(name).text(printStackTrace(result.getError()))); - } - } - - private void calculateLocationIndentation(String definitionText, List testSteps) { - boolean useBackgroundSteps = false; - calculateLocationIndentation(definitionText, testSteps, useBackgroundSteps); - } - - private void calculateLocationIndentation(String definitionText, List testSteps, boolean useBackgroundSteps) { - int maxTextLength = definitionText.length(); - for (TestStep step : testSteps) { - if (step instanceof PickleStepTestStep) { - PickleStepTestStep testStep = (PickleStepTestStep) step; - if (isBackgroundStep(testStep) == useBackgroundSteps) { - StringBuffer stepText = stepText(testStep); - maxTextLength = Math.max(maxTextLength, STEP_INDENT.length() + stepText.length()); - } - } - } - locationIndentation = maxTextLength + 1; - } - - private String createPaddingToLocation(String indent, String text) { - StringBuilder padding = new StringBuilder(); - for (int i = indent.length() + text.length(); i < locationIndentation; ++i) { - padding.append(' '); - } - return padding.toString(); - } - - private static String printStackTrace(Throwable error) { + private static String formatStackTrace(Throwable error) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); error.printStackTrace(printWriter); diff --git a/core/src/main/java/io/cucumber/core/plugin/Stats.java b/core/src/main/java/io/cucumber/core/plugin/Stats.java index 6c9174eb98..88c13c393f 100755 --- a/core/src/main/java/io/cucumber/core/plugin/Stats.java +++ b/core/src/main/java/io/cucumber/core/plugin/Stats.java @@ -4,6 +4,7 @@ import io.cucumber.plugin.event.PickleStepTestStep; import io.cucumber.plugin.event.Result; import io.cucumber.plugin.event.Status; +import io.cucumber.plugin.event.TestCase; import io.cucumber.plugin.event.TestCaseFinished; import io.cucumber.plugin.event.TestRunFinished; import io.cucumber.plugin.event.TestRunStarted; @@ -236,7 +237,10 @@ private void addStepResult(TestStepFinished event) { } private void addScenario(TestCaseFinished event) { - addScenario(event.getResult().getStatus(), event.getTestCase().getScenarioDesignation()); + TestCase testCase = event.getTestCase(); + String location = testCase.getUri() + ":" + testCase.getLine(); + String scenarioDesignation = location + "# " + testCase.getName(); + addScenario(event.getResult().getStatus(), scenarioDesignation); } diff --git a/core/src/main/java/io/cucumber/core/plugin/TestNGFormatter.java b/core/src/main/java/io/cucumber/core/plugin/TestNGFormatter.java index 21759d133d..83e39bd812 100644 --- a/core/src/main/java/io/cucumber/core/plugin/TestNGFormatter.java +++ b/core/src/main/java/io/cucumber/core/plugin/TestNGFormatter.java @@ -1,6 +1,8 @@ package io.cucumber.core.plugin; import io.cucumber.core.exception.CucumberException; +import io.cucumber.core.feature.FeatureParser; +import io.cucumber.core.gherkin.Feature; import io.cucumber.plugin.EventListener; import io.cucumber.plugin.StrictAware; import io.cucumber.plugin.event.EventPublisher; @@ -37,7 +39,10 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.UUID; import static java.time.Duration.ZERO; import static java.time.format.DateTimeFormatter.ISO_INSTANT; @@ -50,7 +55,6 @@ public final class TestNGFormatter implements EventListener, StrictAware { private final Element results; private final Element suite; private final Element test; - private final TestSourcesModel testSources = new TestSourcesModel(); private Element clazz; private Element root; private TestCase testCase; @@ -59,6 +63,8 @@ public final class TestNGFormatter implements EventListener, StrictAware { private String previousTestCaseName; private int exampleNumber; private Instant started; + private final Map featuresNames = new HashMap<>(); + private final FeatureParser parser = new FeatureParser(UUID::randomUUID); @SuppressWarnings("WeakerAccess") // Used by plugin factory public TestNGFormatter(URL url) throws IOException { @@ -96,7 +102,8 @@ public void setStrict(boolean strict) { } private void handleTestSourceRead(TestSourceRead event) { - testSources.addTestSourceReadEvent(event.getUri(), event); + Feature feature = parser.parseResource(new TestSourceReadResource(event)); + featuresNames.put(feature.getUri(), feature.getName()); } private void handleTestCaseStarted(TestCaseStarted event) { @@ -105,7 +112,7 @@ private void handleTestCaseStarted(TestCaseStarted event) { previousTestCaseName = ""; exampleNumber = 1; clazz = document.createElement("class"); - clazz.setAttribute("name", testSources.getFeature(event.getTestCase().getUri()).getName()); + clazz.setAttribute("name", featuresNames.get(event.getTestCase().getUri())); test.appendChild(clazz); } root = document.createElement("test-method"); @@ -268,7 +275,7 @@ private void addStepAndResultListing(StringBuilder sb) { if (i < results.size()) { resultStatus = results.get(i).getStatus().name().toLowerCase(ROOT); } - sb.append(getKeywordFromSource(steps.get(i).getStepLine())); + sb.append(steps.get(i).getStep().getKeyWord()); sb.append(steps.get(i).getStepText()); do { sb.append("."); @@ -278,10 +285,6 @@ private void addStepAndResultListing(StringBuilder sb) { } } - private String getKeywordFromSource(int stepLine) { - return testSources.getKeywordFromSource(currentFeatureFile, stepLine); - } - private Element createException(Document doc, String clazz, String message, String stacktrace) { Element exceptionElement = doc.createElement("exception"); exceptionElement.setAttribute("class", clazz); diff --git a/core/src/main/java/io/cucumber/core/plugin/TestSourceReadResource.java b/core/src/main/java/io/cucumber/core/plugin/TestSourceReadResource.java new file mode 100644 index 0000000000..cee73b1a6f --- /dev/null +++ b/core/src/main/java/io/cucumber/core/plugin/TestSourceReadResource.java @@ -0,0 +1,28 @@ +package io.cucumber.core.plugin; + +import io.cucumber.core.resource.Resource; +import io.cucumber.plugin.event.TestSourceRead; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URI; + +import static java.nio.charset.StandardCharsets.UTF_8; + +final class TestSourceReadResource implements Resource { + private final TestSourceRead event; + + TestSourceReadResource(TestSourceRead event) { + this.event = event; + } + + @Override + public URI getUri() { + return event.getUri(); + } + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream(event.getSource().getBytes(UTF_8)); + } +} diff --git a/core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java b/core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java index 3fd77baa61..0cc21506b0 100644 --- a/core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java +++ b/core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java @@ -1,8 +1,6 @@ package io.cucumber.core.plugin; import gherkin.AstBuilder; -import gherkin.GherkinDialect; -import gherkin.GherkinDialectProvider; import gherkin.Parser; import gherkin.ParserException; import gherkin.TokenMatcher; @@ -15,6 +13,7 @@ import gherkin.ast.ScenarioOutline; import gherkin.ast.Step; import gherkin.ast.TableRow; +import io.cucumber.core.exception.CucumberException; import io.cucumber.plugin.event.TestSourceRead; import java.io.File; @@ -81,8 +80,8 @@ static String convertToId(String name) { return name.replaceAll("[\\s'_,!]", "-").toLowerCase(); } - static URI relativize(URI uri){ - if(!"file".equals(uri.getScheme())){ + static URI relativize(URI uri) { + if (!"file".equals(uri.getScheme())) { return uri; } if (!uri.isAbsolute()) { @@ -113,10 +112,6 @@ Feature getFeature(URI path) { return null; } - ScenarioDefinition getScenarioDefinition(URI path, int line) { - return getScenarioDefinition(getAstNode(path, line)); - } - AstNode getAstNode(URI path, int line) { if (!pathToNodeMap.containsKey(path)) { parseGherkinSource(path); @@ -138,36 +133,6 @@ boolean hasBackground(URI path, int line) { return false; } - String getKeywordFromSource(URI uri, int stepLine) { - Feature feature = getFeature(uri); - if (feature != null) { - TestSourceRead event = getTestSourceReadEvent(uri); - String trimmedSourceLine = event.getSource().split("\n")[stepLine - 1].trim(); - GherkinDialect dialect = new GherkinDialectProvider(feature.getLanguage()).getDefaultDialect(); - for (String keyword : dialect.getStepKeywords()) { - if (trimmedSourceLine.startsWith(keyword)) { - return keyword; - } - } - } - return ""; - } - - private TestSourceRead getTestSourceReadEvent(URI uri) { - if (pathToReadEventMap.containsKey(uri)) { - return pathToReadEventMap.get(uri); - } - return null; - } - - String getFeatureName(URI uri) { - Feature feature = getFeature(uri); - if (feature != null) { - return feature.getName(); - } - return ""; - } - private void parseGherkinSource(URI path) { if (!pathToReadEventMap.containsKey(path)) { return; @@ -184,7 +149,16 @@ private void parseGherkinSource(URI path) { } pathToNodeMap.put(path, nodeMap); } catch (ParserException e) { - // Ignore exceptions + // This works because the TestSourceRead event is emitted after + // parsing. So if we couldn't parse the feature, it will throw + // before emitting the event. So if we can't parse it now, it was + // not parsed by the Gherkin 5 parser. + throw new CucumberException("" + + "You are using a plugin that does not support Gherkin 8+.\n" + + "Try to remove the html and/or json formatters. See the\n" + + "Cucumber-JVM 5.0.0 release announcement for more information.", + e + ); } } diff --git a/core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java b/core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java index 7beb379826..fddc43c8e4 100644 --- a/core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java +++ b/core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java @@ -4,6 +4,8 @@ import gherkin.deps.com.google.gson.GsonBuilder; import gherkin.deps.com.google.gson.annotations.SerializedName; import io.cucumber.core.exception.CucumberException; +import io.cucumber.core.feature.FeatureParser; +import io.cucumber.core.gherkin.Feature; import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.event.EventPublisher; import io.cucumber.plugin.event.TestCase; @@ -26,6 +28,7 @@ import java.util.HashMap; import java.util.Map; import java.util.TreeMap; +import java.util.UUID; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Locale.ROOT; @@ -46,11 +49,13 @@ public final class TimelineFormatter implements ConcurrentEventListener { "/io/cucumber/core/plugin/timeline/chosen-sprite.png" }; - private final TestSourcesModel testSources = new TestSourcesModel(); private final Map allTests = new HashMap<>(); private final Map allGroups = new HashMap<>(); private final URL reportDir; private final NiceAppendable reportJs; + private final Map featuresNames = new HashMap<>(); + private final FeatureParser parser = new FeatureParser(UUID::randomUUID); + @SuppressWarnings("unused") // Used by PluginFactory public TimelineFormatter(final URL reportDir) { @@ -71,7 +76,8 @@ public void setEventPublisher(final EventPublisher publisher) { } private void handleTestSourceRead(TestSourceRead event) { - testSources.addTestSourceReadEvent(event.getUri(), event); + Feature feature = parser.parseResource(new TestSourceReadResource(event)); + featuresNames.put(feature.getUri(), feature.getName()); } private void handleTestCaseStarted(final TestCaseStarted event) { @@ -175,10 +181,7 @@ private static void closeQuietly(Closeable out) { } private String getId(final TestCaseEvent testCaseEvent) { - final TestCase testCase = testCaseEvent.getTestCase(); - final URI uri = testCase.getUri(); - final TestSourcesModel.AstNode astNode = testSources.getAstNode(uri, testCase.getLine()); - return TestSourcesModel.calculateId(astNode); + return testCaseEvent.getTestCase().getId().toString(); } class TestData { @@ -205,7 +208,7 @@ class TestData { this.id = getId(started); final TestCase testCase = started.getTestCase(); final URI uri = testCase.getUri(); - this.feature = TimelineFormatter.this.testSources.getFeatureName(uri); + this.feature = featuresNames.get(uri); this.scenario = testCase.getName(); this.startTime = started.getInstant().toEpochMilli(); this.threadId = threadId; diff --git a/core/src/main/java/io/cucumber/core/runner/AmbiguousPickleStepDefinitionsMatch.java b/core/src/main/java/io/cucumber/core/runner/AmbiguousPickleStepDefinitionsMatch.java index e6db81983b..48c9a57a60 100644 --- a/core/src/main/java/io/cucumber/core/runner/AmbiguousPickleStepDefinitionsMatch.java +++ b/core/src/main/java/io/cucumber/core/runner/AmbiguousPickleStepDefinitionsMatch.java @@ -1,7 +1,7 @@ package io.cucumber.core.runner; import io.cucumber.core.backend.TestCaseState; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Step; import java.net.URI; import java.util.Collections; @@ -9,7 +9,7 @@ final class AmbiguousPickleStepDefinitionsMatch extends PickleStepDefinitionMatch { private final AmbiguousStepDefinitionsException exception; - AmbiguousPickleStepDefinitionsMatch(URI uri, CucumberStep step, AmbiguousStepDefinitionsException e) { + AmbiguousPickleStepDefinitionsMatch(URI uri, Step step, AmbiguousStepDefinitionsException e) { super(Collections.emptyList(), new NoStepDefinition(), uri, step); this.exception = e; } diff --git a/core/src/main/java/io/cucumber/core/runner/AmbiguousStepDefinitionsException.java b/core/src/main/java/io/cucumber/core/runner/AmbiguousStepDefinitionsException.java index f03a3f4257..50ed8b9756 100644 --- a/core/src/main/java/io/cucumber/core/runner/AmbiguousStepDefinitionsException.java +++ b/core/src/main/java/io/cucumber/core/runner/AmbiguousStepDefinitionsException.java @@ -1,6 +1,6 @@ package io.cucumber.core.runner; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Step; import java.util.List; @@ -11,12 +11,12 @@ final class AmbiguousStepDefinitionsException extends Exception { private final List matches; - AmbiguousStepDefinitionsException(CucumberStep step, List matches) { + AmbiguousStepDefinitionsException(Step step, List matches) { super(createMessage(step, matches)); this.matches = matches; } - private static String createMessage(CucumberStep step, List matches) { + private static String createMessage(Step step, List matches) { requireNonNull(step); requireNonNull(matches); 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 4eab15b4d3..9244b3a93a 100644 --- a/core/src/main/java/io/cucumber/core/runner/CachingGlue.java +++ b/core/src/main/java/io/cucumber/core/runner/CachingGlue.java @@ -11,7 +11,7 @@ import io.cucumber.core.backend.ScenarioScoped; import io.cucumber.core.backend.StepDefinition; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.stepexpression.Argument; import io.cucumber.core.stepexpression.StepTypeRegistry; import io.cucumber.cucumberexpressions.ParameterByTypeTransformer; @@ -230,7 +230,7 @@ void prepareGlue(StepTypeRegistry stepTypeRegistry) throws DuplicateStepDefiniti }); } - PickleStepDefinitionMatch stepDefinitionMatch(URI uri, CucumberStep step) throws AmbiguousStepDefinitionsException{ + PickleStepDefinitionMatch stepDefinitionMatch(URI uri, Step step) throws AmbiguousStepDefinitionsException { PickleStepDefinitionMatch cachedMatch = cachedStepDefinitionMatch(uri, step); if (cachedMatch != null) { return cachedMatch; @@ -239,7 +239,7 @@ PickleStepDefinitionMatch stepDefinitionMatch(URI uri, CucumberStep step) throws } - private PickleStepDefinitionMatch cachedStepDefinitionMatch(URI uri, CucumberStep step) { + private PickleStepDefinitionMatch cachedStepDefinitionMatch(URI uri, Step step) { String stepDefinitionPattern = stepPatternByStepText.get(step.getText()); if (stepDefinitionPattern == null) { return null; @@ -258,7 +258,7 @@ private PickleStepDefinitionMatch cachedStepDefinitionMatch(URI uri, CucumberSte return new PickleStepDefinitionMatch(arguments, coreStepDefinition.getStepDefinition(), uri, step); } - private PickleStepDefinitionMatch findStepDefinitionMatch(URI uri, CucumberStep step) throws AmbiguousStepDefinitionsException { + private PickleStepDefinitionMatch findStepDefinitionMatch(URI uri, Step step) throws AmbiguousStepDefinitionsException { List matches = stepDefinitionMatches(uri, step); if (matches.isEmpty()) { return null; @@ -274,7 +274,7 @@ private PickleStepDefinitionMatch findStepDefinitionMatch(URI uri, CucumberStep return match; } - private List stepDefinitionMatches(URI uri, CucumberStep step) { + private List stepDefinitionMatches(URI uri, Step step) { List result = new ArrayList<>(); for (CoreStepDefinition coreStepDefinition : stepDefinitionsByPattern.values()) { List arguments = coreStepDefinition.matchedArguments(step); diff --git a/core/src/main/java/io/cucumber/core/runner/CoreStepDefinition.java b/core/src/main/java/io/cucumber/core/runner/CoreStepDefinition.java index df9709e692..5c8653ebf2 100644 --- a/core/src/main/java/io/cucumber/core/runner/CoreStepDefinition.java +++ b/core/src/main/java/io/cucumber/core/runner/CoreStepDefinition.java @@ -2,7 +2,7 @@ import io.cucumber.core.backend.ParameterInfo; import io.cucumber.core.backend.StepDefinition; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.stepexpression.Argument; import io.cucumber.core.stepexpression.ArgumentMatcher; import io.cucumber.core.stepexpression.StepExpression; @@ -49,7 +49,7 @@ public StepDefinition getStepDefinition() { return stepDefinition; } - List matchedArguments(CucumberStep step) { + List matchedArguments(Step step) { return argumentMatcher.argumentsFrom(step, types); } @@ -64,5 +64,4 @@ private static Type[] getTypes(List parameterInfos) { } return types; } - } diff --git a/core/src/main/java/io/cucumber/core/runner/DefinitionArgument.java b/core/src/main/java/io/cucumber/core/runner/DefinitionArgument.java index 4570a365d4..6631782c8e 100644 --- a/core/src/main/java/io/cucumber/core/runner/DefinitionArgument.java +++ b/core/src/main/java/io/cucumber/core/runner/DefinitionArgument.java @@ -1,7 +1,7 @@ package io.cucumber.core.runner; -import io.cucumber.plugin.event.Argument; import io.cucumber.core.stepexpression.ExpressionArgument; +import io.cucumber.plugin.event.Argument; import java.util.ArrayList; import java.util.List; @@ -10,15 +10,16 @@ final class DefinitionArgument implements Argument { private final io.cucumber.cucumberexpressions.Group group; - private DefinitionArgument(ExpressionArgument expressionArgument) { - group = expressionArgument.getGroup(); + private DefinitionArgument(ExpressionArgument argument) { + this.group = argument.getGroup(); } static List createArguments(List match) { List args = new ArrayList<>(); for (io.cucumber.core.stepexpression.Argument argument : match) { if (argument instanceof ExpressionArgument) { - args.add(new DefinitionArgument((ExpressionArgument) argument)); + ExpressionArgument expressionArgument = (ExpressionArgument) argument; + args.add(new DefinitionArgument(expressionArgument)); } } return args; diff --git a/core/src/main/java/io/cucumber/core/runner/HookTestStep.java b/core/src/main/java/io/cucumber/core/runner/HookTestStep.java index 77b62ad8e6..263e547e7e 100644 --- a/core/src/main/java/io/cucumber/core/runner/HookTestStep.java +++ b/core/src/main/java/io/cucumber/core/runner/HookTestStep.java @@ -2,11 +2,13 @@ import io.cucumber.plugin.event.HookType; +import java.util.UUID; + final class HookTestStep extends TestStep implements io.cucumber.plugin.event.HookTestStep { private final HookType hookType; - HookTestStep(HookType hookType, HookDefinitionMatch definitionMatch) { - super(definitionMatch); + HookTestStep(UUID id, HookType hookType, HookDefinitionMatch definitionMatch) { + super(id, definitionMatch); this.hookType = hookType; } diff --git a/core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java b/core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java index 2b1e8851b5..72a391b064 100644 --- a/core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java +++ b/core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java @@ -6,7 +6,7 @@ import io.cucumber.core.backend.StepDefinition; import io.cucumber.core.backend.TestCaseState; import io.cucumber.core.exception.CucumberException; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.stepexpression.Argument; import io.cucumber.cucumberexpressions.CucumberExpressionException; import io.cucumber.datatable.CucumberDataTableException; @@ -22,11 +22,12 @@ import static io.cucumber.core.runner.StackManipulation.removeFrameworkFramesAndAppendStepLocation; class PickleStepDefinitionMatch extends Match implements StepDefinitionMatch { + private final StepDefinition stepDefinition; private final URI uri; - private final CucumberStep step; + private final Step step; - PickleStepDefinitionMatch(List arguments, StepDefinition stepDefinition, URI uri, CucumberStep step) { + PickleStepDefinitionMatch(List arguments, StepDefinition stepDefinition, URI uri, Step step) { super(arguments, stepDefinition.getLocation()); this.stepDefinition = stepDefinition; this.uri = uri; @@ -156,7 +157,7 @@ public String getPattern() { } private StackTraceElement getStepLocation() { - return new StackTraceElement("✽", step.getText(), uri.getSchemeSpecificPart(), step.getStepLine()); + return new StackTraceElement("✽", step.getText(), uri.getSchemeSpecificPart(), step.getLine()); } StepDefinition getStepDefinition() { @@ -167,5 +168,4 @@ StepDefinition getStepDefinition() { public String getCodeLocation() { return stepDefinition.getLocation(); } - } diff --git a/core/src/main/java/io/cucumber/core/runner/PickleStepTestStep.java b/core/src/main/java/io/cucumber/core/runner/PickleStepTestStep.java index 5aa48a8b74..7c98dc3837 100644 --- a/core/src/main/java/io/cucumber/core/runner/PickleStepTestStep.java +++ b/core/src/main/java/io/cucumber/core/runner/PickleStepTestStep.java @@ -1,7 +1,7 @@ package io.cucumber.core.runner; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Step; import io.cucumber.plugin.event.Argument; import io.cucumber.plugin.event.StepArgument; import io.cucumber.plugin.event.TestCase; @@ -9,25 +9,25 @@ import java.net.URI; import java.util.Collections; import java.util.List; +import java.util.UUID; final class PickleStepTestStep extends TestStep implements io.cucumber.plugin.event.PickleStepTestStep { private final URI uri; - private final CucumberStep step; + private final Step step; private final List afterStepHookSteps; private final List beforeStepHookSteps; private final PickleStepDefinitionMatch definitionMatch; - PickleStepTestStep(URI uri, CucumberStep step, PickleStepDefinitionMatch definitionMatch) { - this(uri, step, Collections.emptyList(), Collections.emptyList(), definitionMatch); + PickleStepTestStep(UUID id, URI uri, Step step, PickleStepDefinitionMatch definitionMatch) { + this(id, uri, step, Collections.emptyList(), Collections.emptyList(), definitionMatch); } - PickleStepTestStep(URI uri, - CucumberStep step, + PickleStepTestStep(UUID id, URI uri, + Step step, List beforeStepHookSteps, List afterStepHookSteps, - PickleStepDefinitionMatch definitionMatch - ) { - super(definitionMatch); + PickleStepDefinitionMatch definitionMatch) { + super(id, definitionMatch); this.uri = uri; this.step = step; this.afterStepHookSteps = afterStepHookSteps; @@ -36,17 +36,17 @@ final class PickleStepTestStep extends TestStep implements io.cucumber.plugin.ev } @Override - boolean run(TestCase testCase, EventBus bus, TestCaseState state, boolean skipSteps) { + boolean run(TestCase testCase, EventBus bus, TestCaseState state, boolean skipSteps, UUID testExecutionId) { boolean skipNextStep = skipSteps; for (HookTestStep before : beforeStepHookSteps) { - skipNextStep |= before.run(testCase, bus, state, skipSteps); + skipNextStep |= before.run(testCase, bus, state, skipSteps, testExecutionId); } - skipNextStep |= super.run(testCase, bus, state, skipNextStep); + skipNextStep |= super.run(testCase, bus, state, skipNextStep, testExecutionId); for (HookTestStep after : afterStepHookSteps) { - skipNextStep |= after.run(testCase, bus, state, skipSteps); + skipNextStep |= after.run(testCase, bus, state, skipSteps, testExecutionId); } return skipNextStep; @@ -61,7 +61,7 @@ List getAfterStepHookSteps() { } @Override - public CucumberStep getStep() { + public Step getStep() { return step; } @@ -72,7 +72,7 @@ public URI getUri() { @Override public int getStepLine() { - return step.getStepLine(); + return step.getLine(); } @Override 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 2dac1c0d93..0066fc5113 100644 --- a/core/src/main/java/io/cucumber/core/runner/Runner.java +++ b/core/src/main/java/io/cucumber/core/runner/Runner.java @@ -4,8 +4,8 @@ import io.cucumber.core.backend.Backend; import io.cucumber.core.backend.ObjectFactory; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberPickle; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Pickle; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.logging.Logger; import io.cucumber.core.logging.LoggerFactory; import io.cucumber.core.snippets.SnippetGenerator; @@ -53,7 +53,7 @@ public EventBus getBus() { return bus; } - public void runPickle(CucumberPickle pickle) { + public void runPickle(Pickle pickle) { try { StepTypeRegistry stepTypeRegistry = createTypeRegistryForPickle(pickle); snippetGenerators = createSnippetGeneratorsForPickle(stepTypeRegistry); @@ -77,7 +77,7 @@ private List createSnippetGeneratorsForPickle(StepTypeRegistry .collect(Collectors.toList()); } - private StepTypeRegistry createTypeRegistryForPickle(CucumberPickle pickle) { + private StepTypeRegistry createTypeRegistryForPickle(Pickle pickle) { Locale locale = typeRegistryConfigurer.locale(); if (locale == null) { locale = new Locale(pickle.getLanguage()); @@ -87,31 +87,31 @@ private StepTypeRegistry createTypeRegistryForPickle(CucumberPickle pickle) { return stepTypeRegistry; } - private TestCase createTestCaseForPickle(CucumberPickle pickle) { + private TestCase createTestCaseForPickle(Pickle pickle) { if (pickle.getSteps().isEmpty()) { - return new TestCase(emptyList(), emptyList(), emptyList(), pickle, runnerOptions.isDryRun()); + return new TestCase(bus.generateId(), emptyList(), emptyList(), emptyList(), pickle, runnerOptions.isDryRun()); } List testSteps = createTestStepsForPickleSteps(pickle); List beforeHooks = createTestStepsForBeforeHooks(pickle.getTags()); List afterHooks = createTestStepsForAfterHooks(pickle.getTags()); - return new TestCase(testSteps, beforeHooks, afterHooks, pickle, runnerOptions.isDryRun()); + return new TestCase(bus.generateId(), testSteps, beforeHooks, afterHooks, pickle, runnerOptions.isDryRun()); } - private List createTestStepsForPickleSteps(CucumberPickle pickle) { + private List createTestStepsForPickleSteps(Pickle pickle) { List testSteps = new ArrayList<>(); - for (CucumberStep step : pickle.getSteps()) { + for (Step step : pickle.getSteps()) { PickleStepDefinitionMatch match = matchStepToStepDefinition(pickle, step); List afterStepHookSteps = createAfterStepHooks(pickle.getTags()); List beforeStepHookSteps = createBeforeStepHooks(pickle.getTags()); - testSteps.add(new PickleStepTestStep(pickle.getUri(), step, beforeStepHookSteps, afterStepHookSteps, match)); + testSteps.add(new PickleStepTestStep(bus.generateId(), pickle.getUri(), step, beforeStepHookSteps, afterStepHookSteps, match)); } return testSteps; } - private PickleStepDefinitionMatch matchStepToStepDefinition(CucumberPickle pickle, CucumberStep step) { + private PickleStepDefinitionMatch matchStepToStepDefinition(Pickle pickle, Step step) { try { PickleStepDefinitionMatch match = glue.stepDefinitionMatch(pickle.getUri(), step); if (match != null) { @@ -119,7 +119,7 @@ private PickleStepDefinitionMatch matchStepToStepDefinition(CucumberPickle pickl } List snippets = generateSnippetsForStep(step); if (!snippets.isEmpty()) { - bus.send(new SnippetsSuggestedEvent(bus.getInstant(), pickle.getUri(), step.getStepLine(), snippets)); + bus.send(new SnippetsSuggestedEvent(bus.getInstant(), pickle.getUri(), step.getLine(), snippets)); } return new UndefinedPickleStepDefinitionMatch(pickle.getUri(), step); } catch (AmbiguousStepDefinitionsException e) { @@ -127,7 +127,7 @@ private PickleStepDefinitionMatch matchStepToStepDefinition(CucumberPickle pickl } } - private List generateSnippetsForStep(CucumberStep step) { + private List generateSnippetsForStep(Step step) { List snippets = new ArrayList<>(); for (SnippetGenerator snippetGenerator : snippetGenerators) { List snippet = snippetGenerator.getSnippet(step, runnerOptions.getSnippetType()); @@ -147,7 +147,7 @@ private List createTestStepsForAfterHooks(List tags) { private List createTestStepsForHooks(List tags, Collection hooks, HookType hookType) { return hooks.stream() .filter(hook -> hook.matches(tags)) - .map(hook -> new HookTestStep(hookType, new HookDefinitionMatch(hook))) + .map(hook -> new HookTestStep(bus.generateId(), hookType, new HookDefinitionMatch(hook))) .collect(Collectors.toList()); } diff --git a/core/src/main/java/io/cucumber/core/runner/TestCase.java b/core/src/main/java/io/cucumber/core/runner/TestCase.java index b0de67333f..5345dd448b 100644 --- a/core/src/main/java/io/cucumber/core/runner/TestCase.java +++ b/core/src/main/java/io/cucumber/core/runner/TestCase.java @@ -1,7 +1,7 @@ package io.cucumber.core.runner; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.plugin.event.Result; import io.cucumber.plugin.event.Status; import io.cucumber.plugin.event.TestCaseFinished; @@ -13,19 +13,22 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.UUID; final class TestCase implements io.cucumber.plugin.event.TestCase { - private final CucumberPickle pickle; + private final Pickle pickle; private final List testSteps; private final boolean dryRun; private final List beforeHooks; private final List afterHooks; + private final UUID id; - TestCase(List testSteps, + TestCase(UUID id, List testSteps, List beforeHooks, List afterHooks, - CucumberPickle pickle, + Pickle pickle, boolean dryRun) { + this.id = id; this.testSteps = testSteps; this.beforeHooks = beforeHooks; this.afterHooks = afterHooks; @@ -36,19 +39,20 @@ final class TestCase implements io.cucumber.plugin.event.TestCase { void run(EventBus bus) { boolean skipNextStep = this.dryRun; Instant start = bus.getInstant(); + UUID executionId = bus.generateId(); bus.send(new TestCaseStarted(start, this)); TestCaseState state = new TestCaseState(bus, this); for (HookTestStep before : beforeHooks) { - skipNextStep |= before.run(this, bus, state, dryRun); + skipNextStep |= before.run(this, bus, state, dryRun, executionId); } for (PickleStepTestStep step : testSteps) { - skipNextStep |= step.run(this, bus, state, skipNextStep); + skipNextStep |= step.run(this, bus, state, skipNextStep, executionId); } for (HookTestStep after : afterHooks) { - after.run(this, bus, state, dryRun); + after.run(this, bus, state, dryRun, executionId); } Instant stop = bus.getInstant(); @@ -85,11 +89,21 @@ public URI getUri() { return pickle.getUri(); } + @Override + public UUID getId() { + return id; + } + @Override public Integer getLine() { return pickle.getLocation().getLine(); } + @Override + public String getKeyword() { + return pickle.getKeyword(); + } + private String fileColonLine(Integer line) { return pickle.getUri().getSchemeSpecificPart() + ":" + line; } @@ -98,4 +112,5 @@ private String fileColonLine(Integer line) { public List getTags() { return pickle.getTags(); } + } diff --git a/core/src/main/java/io/cucumber/core/runner/TestStep.java b/core/src/main/java/io/cucumber/core/runner/TestStep.java index 94d943ca22..63cbe0ae13 100644 --- a/core/src/main/java/io/cucumber/core/runner/TestStep.java +++ b/core/src/main/java/io/cucumber/core/runner/TestStep.java @@ -1,16 +1,17 @@ package io.cucumber.core.runner; import io.cucumber.core.backend.Pending; +import io.cucumber.core.eventbus.EventBus; import io.cucumber.plugin.event.Result; import io.cucumber.plugin.event.Status; import io.cucumber.plugin.event.TestCase; import io.cucumber.plugin.event.TestStepFinished; import io.cucumber.plugin.event.TestStepStarted; -import io.cucumber.core.eventbus.EventBus; import java.time.Duration; import java.time.Instant; import java.util.Arrays; +import java.util.UUID; import static java.time.Duration.ZERO; @@ -27,8 +28,10 @@ abstract class TestStep implements io.cucumber.plugin.event.TestStep { } private final StepDefinitionMatch stepDefinitionMatch; + private final UUID id; - TestStep(StepDefinitionMatch stepDefinitionMatch) { + TestStep(UUID id, StepDefinitionMatch stepDefinitionMatch) { + this.id = id; this.stepDefinitionMatch = stepDefinitionMatch; } @@ -37,9 +40,9 @@ public String getCodeLocation() { return stepDefinitionMatch.getCodeLocation(); } - boolean run(TestCase testCase, EventBus bus, TestCaseState state, boolean skipSteps) { - Instant startTimeMillis = bus.getInstant(); - bus.send(new TestStepStarted(startTimeMillis, testCase, this)); + boolean run(TestCase testCase, EventBus bus, TestCaseState state, boolean skipSteps, UUID textExecutionId) { + Instant startTime = bus.getInstant(); + bus.send(new TestStepStarted(startTime, testCase, this)); Status status; Throwable error = null; try { @@ -48,10 +51,11 @@ boolean run(TestCase testCase, EventBus bus, TestCaseState state, boolean skipSt error = t; status = mapThrowableToStatus(t); } - Instant stopTimeNanos = bus.getInstant(); - Result result = mapStatusToResult(status, error, Duration.between(startTimeMillis, stopTimeNanos)); + Instant stopTime = bus.getInstant(); + Duration duration = Duration.between(startTime, stopTime); + Result result = mapStatusToResult(status, error, duration); state.add(result); - bus.send(new TestStepFinished(stopTimeNanos, testCase, this, result)); + bus.send(new TestStepFinished(stopTime, testCase, this, result)); return !result.getStatus().is(Status.PASSED); } diff --git a/core/src/main/java/io/cucumber/core/runner/UndefinedPickleStepDefinitionMatch.java b/core/src/main/java/io/cucumber/core/runner/UndefinedPickleStepDefinitionMatch.java index 92092e3064..d017a0cc1c 100644 --- a/core/src/main/java/io/cucumber/core/runner/UndefinedPickleStepDefinitionMatch.java +++ b/core/src/main/java/io/cucumber/core/runner/UndefinedPickleStepDefinitionMatch.java @@ -1,14 +1,14 @@ package io.cucumber.core.runner; import io.cucumber.core.backend.TestCaseState; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Step; import java.net.URI; import java.util.Collections; final class UndefinedPickleStepDefinitionMatch extends PickleStepDefinitionMatch { - UndefinedPickleStepDefinitionMatch(URI uri, CucumberStep step) { + UndefinedPickleStepDefinitionMatch(URI uri, Step step) { super(Collections.emptyList(), new NoStepDefinition(), uri, step); } diff --git a/core/src/main/java/io/cucumber/core/runtime/FeaturePathFeatureSupplier.java b/core/src/main/java/io/cucumber/core/runtime/FeaturePathFeatureSupplier.java index 007f53aa35..a17347c726 100644 --- a/core/src/main/java/io/cucumber/core/runtime/FeaturePathFeatureSupplier.java +++ b/core/src/main/java/io/cucumber/core/runtime/FeaturePathFeatureSupplier.java @@ -1,8 +1,9 @@ package io.cucumber.core.runtime; -import io.cucumber.core.feature.CucumberFeature; import io.cucumber.core.feature.FeatureIdentifier; +import io.cucumber.core.feature.FeatureParser; import io.cucumber.core.feature.Options; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.logging.Logger; import io.cucumber.core.logging.LoggerFactory; import io.cucumber.core.resource.ResourceScanner; @@ -15,7 +16,6 @@ import java.util.function.Supplier; import static io.cucumber.core.feature.FeatureIdentifier.isFeature; -import static io.cucumber.core.feature.FeatureParser.parseResource; import static java.util.Comparator.comparing; import static java.util.Optional.of; import static java.util.stream.Collectors.joining; @@ -27,23 +27,23 @@ public final class FeaturePathFeatureSupplier implements FeatureSupplier { private static final Logger log = LoggerFactory.getLogger(FeaturePathFeatureSupplier.class); - private final ResourceScanner featureScanner; + private final ResourceScanner featureScanner; private final Options featureOptions; - public FeaturePathFeatureSupplier(Supplier classLoader, Options featureOptions) { + public FeaturePathFeatureSupplier(Supplier classLoader, Options featureOptions, FeatureParser parser) { this.featureOptions = featureOptions; this.featureScanner = new ResourceScanner<>( classLoader, FeatureIdentifier::isFeature, - resource -> of(parseResource(resource)) + resource -> of(parser.parseResource(resource)) ); } @Override - public List get() { + public List get() { List featurePaths = featureOptions.getFeaturePaths(); - List features = loadFeatures(featurePaths); + List features = loadFeatures(featurePaths); if (features.isEmpty()) { if (featurePaths.isEmpty()) { log.warn(() -> "Got no path to feature directory or feature file"); @@ -54,12 +54,12 @@ public List get() { return features; } - private List loadFeatures(List featurePaths) { + private List loadFeatures(List featurePaths) { log.debug(() -> "Loading features from " + featurePaths.stream().map(URI::toString).collect(joining(", "))); final FeatureBuilder builder = new FeatureBuilder(); for (URI featurePath : featurePaths) { - List found = featureScanner.scanForResourcesUri(featurePath); + List found = featureScanner.scanForResourcesUri(featurePath); if (found.isEmpty() && isFeature(featurePath)) { throw new IllegalArgumentException("Feature not found: " + featurePath); } @@ -71,22 +71,22 @@ private List loadFeatures(List featurePaths) { static final class FeatureBuilder { - private final Map> sourceToFeature = new HashMap<>(); - private final List features = new ArrayList<>(); + private final Map> sourceToFeature = new HashMap<>(); + private final List features = new ArrayList<>(); - List build() { - List cucumberFeatures = new ArrayList<>(features); - cucumberFeatures.sort(comparing(CucumberFeature::getUri)); - return cucumberFeatures; + List build() { + List features = new ArrayList<>(this.features); + features.sort(comparing(Feature::getUri)); + return features; } - void addUnique(CucumberFeature parsedFeature) { + void addUnique(Feature parsedFeature) { String parsedFileName = getFileName(parsedFeature); - Map existingFeatures = sourceToFeature.get(parsedFeature.getSource()); + Map existingFeatures = sourceToFeature.get(parsedFeature.getSource()); if (existingFeatures != null) { // Same contents but different file names was probably intentional - CucumberFeature existingFeature = existingFeatures.get(parsedFileName); + Feature existingFeature = existingFeatures.get(parsedFileName); if (existingFeature != null) { log.error(() -> "" + "Duplicate feature found: " + @@ -109,7 +109,7 @@ void addUnique(CucumberFeature parsedFeature) { features.add(parsedFeature); } - private String getFileName(CucumberFeature feature) { + private String getFileName(Feature feature) { String uri = feature.getUri().getSchemeSpecificPart(); int i = uri.lastIndexOf("/"); return i > 0 ? uri.substring(i) : uri; diff --git a/core/src/main/java/io/cucumber/core/runtime/FeatureSupplier.java b/core/src/main/java/io/cucumber/core/runtime/FeatureSupplier.java index 4b1cc58fcd..7237bbed6c 100644 --- a/core/src/main/java/io/cucumber/core/runtime/FeatureSupplier.java +++ b/core/src/main/java/io/cucumber/core/runtime/FeatureSupplier.java @@ -1,9 +1,9 @@ package io.cucumber.core.runtime; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import java.util.List; public interface FeatureSupplier { - List get(); + List get(); } 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 65d4307448..4c3266d6eb 100644 --- a/core/src/main/java/io/cucumber/core/runtime/Runtime.java +++ b/core/src/main/java/io/cucumber/core/runtime/Runtime.java @@ -3,9 +3,10 @@ import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.exception.CompositeCucumberException; import io.cucumber.core.exception.CucumberException; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.feature.FeatureParser; import io.cucumber.core.filter.Filters; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.logging.Logger; import io.cucumber.core.logging.LoggerFactory; import io.cucumber.core.options.RuntimeOptions; @@ -29,6 +30,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.UUID; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -57,7 +59,7 @@ public final class Runtime { private final ExitStatus exitStatus; private final RunnerSupplier runnerSupplier; - private final Predicate filter; + private final Predicate filter; private final int limit; private final EventBus bus; private final FeatureSupplier featureSupplier; @@ -66,7 +68,7 @@ public final class Runtime { private Runtime(final ExitStatus exitStatus, final EventBus bus, - final Predicate filter, + final Predicate filter, final int limit, final RunnerSupplier runnerSupplier, final FeatureSupplier featureSupplier, @@ -83,9 +85,9 @@ private Runtime(final ExitStatus exitStatus, } public void run() { - final List features = featureSupplier.get(); + final List features = featureSupplier.get(); bus.send(new TestRunStarted(bus.getInstant())); - for (CucumberFeature feature : features) { + for (Feature feature : features) { bus.send(new TestSourceRead(bus.getInstant(), feature.getUri(), feature.getSource())); } @@ -101,7 +103,7 @@ public void run() { executor.shutdown(); List thrown = new ArrayList<>(); - for (Future executingPickle : executingPickles) { + for (Future executingPickle : executingPickles) { try { executingPickle.get(); } catch (ExecutionException e) { @@ -131,7 +133,7 @@ public static Builder builder() { public static class Builder { - private EventBus eventBus = new TimeServiceEventBus(Clock.systemUTC()); + private EventBus eventBus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); private Supplier classLoader = ClassLoaders::getDefaultClassLoader; private RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); private BackendSupplier backendSupplier; @@ -204,11 +206,13 @@ public Runtime build() { ? Executors.newFixedThreadPool(runtimeOptions.getThreads(), new CucumberThreadFactory()) : new SameThreadExecutorService(); + final FeatureParser parser = new FeatureParser(eventBus::generateId); + final FeatureSupplier featureSupplier = this.featureSupplier != null ? this.featureSupplier - : new FeaturePathFeatureSupplier(classLoader, runtimeOptions); + : new FeaturePathFeatureSupplier(classLoader, runtimeOptions, parser); - final Predicate filter = new Filters(runtimeOptions); + final Predicate filter = new Filters(runtimeOptions); final int limit = runtimeOptions.getLimitCount(); final PickleOrder pickleOrder = runtimeOptions.getPickleOrder(); diff --git a/core/src/main/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplier.java b/core/src/main/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplier.java index ee6769e24c..ceef89d1a4 100644 --- a/core/src/main/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplier.java +++ b/core/src/main/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplier.java @@ -8,6 +8,7 @@ import io.cucumber.core.runner.Runner; import java.time.Instant; +import java.util.UUID; /** * Creates a distinct runner for each calling thread. Each runner has its own bus, backend- and glue-suppliers. @@ -71,6 +72,11 @@ public void send(final Event event) { public Instant getInstant() { return parent.getInstant(); } + + @Override + public UUID generateId() { + return parent.generateId(); + } } private static final class SynchronizedEventBus implements EventBus { @@ -113,5 +119,10 @@ public synchronized void removeHandlerFor(Class eventType, public Instant getInstant() { return delegate.getInstant(); } + + @Override + public UUID generateId() { + return delegate.generateId(); + } } } diff --git a/core/src/main/java/io/cucumber/core/runtime/TimeServiceEventBus.java b/core/src/main/java/io/cucumber/core/runtime/TimeServiceEventBus.java index 3d2789f98e..b23cac539d 100644 --- a/core/src/main/java/io/cucumber/core/runtime/TimeServiceEventBus.java +++ b/core/src/main/java/io/cucumber/core/runtime/TimeServiceEventBus.java @@ -2,18 +2,27 @@ import java.time.Clock; import java.time.Instant; +import java.util.UUID; +import java.util.function.Supplier; import io.cucumber.core.eventbus.AbstractEventBus; public final class TimeServiceEventBus extends AbstractEventBus { private final Clock clock; + private final Supplier idGenerator; - public TimeServiceEventBus(Clock clock) { + public TimeServiceEventBus(Clock clock, Supplier idGenerator) { this.clock = clock; + this.idGenerator = idGenerator; } @Override public Instant getInstant() { return clock.instant(); } + + @Override + public UUID generateId() { + return idGenerator.get(); + } } diff --git a/core/src/main/java/io/cucumber/core/snippets/SnippetGenerator.java b/core/src/main/java/io/cucumber/core/snippets/SnippetGenerator.java index 0cf3b30800..61749da786 100644 --- a/core/src/main/java/io/cucumber/core/snippets/SnippetGenerator.java +++ b/core/src/main/java/io/cucumber/core/snippets/SnippetGenerator.java @@ -1,8 +1,9 @@ package io.cucumber.core.snippets; import io.cucumber.core.backend.Snippet; -import io.cucumber.core.feature.CucumberStep; -import io.cucumber.core.feature.DocStringArgument; +import io.cucumber.core.gherkin.Argument; +import io.cucumber.core.gherkin.Step; +import io.cucumber.core.gherkin.DocStringArgument; import io.cucumber.cucumberexpressions.CucumberExpressionGenerator; import io.cucumber.cucumberexpressions.GeneratedExpression; import io.cucumber.cucumberexpressions.ParameterType; @@ -33,13 +34,13 @@ public SnippetGenerator(Snippet snippet, ParameterTypeRegistry parameterTypeRegi this.generator = new CucumberExpressionGenerator(parameterTypeRegistry); } - public List getSnippet(CucumberStep step, SnippetType snippetType) { + public List getSnippet(Step step, SnippetType snippetType) { List generatedExpressions = generator.generateExpressions(step.getText()); List snippets = new ArrayList<>(generatedExpressions.size()); FunctionNameGenerator functionNameGenerator = new FunctionNameGenerator(snippetType.joiner()); for (GeneratedExpression expression : generatedExpressions) { snippets.add(snippet.template().format(new String[]{ - sanitize(step.getStepType().isGivenWhenThen() ? step.getKeyWord() : step.getPreviousGivenWhenThenKeyWord()), + sanitize(step.getType().isGivenWhenThen() ? step.getKeyWord() : step.getPreviousGivenWhenThenKeyWord()), snippet.escapePattern(expression.getSource()), functionName(expression.getSource(), functionNameGenerator), snippet.arguments(arguments(step, expression.getParameterNames(), expression.getParameterTypes())), @@ -52,7 +53,7 @@ public List getSnippet(CucumberStep step, SnippetType snippetType) { return snippets; } - private String tableHint(CucumberStep step) { + private String tableHint(Step step) { if (step.getArgument() == null) { return ""; } @@ -79,7 +80,7 @@ private String functionName(String sentence, FunctionNameGenerator functionNameG } - private Map arguments(CucumberStep step, List parameterNames, List> parameterTypes) { + private Map arguments(Step step, List parameterNames, List> parameterTypes) { Map arguments = new LinkedHashMap<>(parameterTypes.size() + 1); for (int i = 0; i < parameterTypes.size(); i++) { @@ -88,12 +89,12 @@ private Map arguments(CucumberStep step, List parameterNam arguments.put(parameterName, parameterType.getType()); } - io.cucumber.core.feature.Argument arg = step.getArgument(); + Argument arg = step.getArgument(); if (arg == null) { return arguments; } else if (arg instanceof DocStringArgument) { arguments.put(parameterName("docString", parameterNames), String.class); - } else if (arg instanceof io.cucumber.core.feature.DataTableArgument) { + } else if (arg instanceof DataTableArgument) { arguments.put(parameterName("dataTable", parameterNames), DataTable.class); } diff --git a/core/src/main/java/io/cucumber/core/stepexpression/ArgumentMatcher.java b/core/src/main/java/io/cucumber/core/stepexpression/ArgumentMatcher.java index bb3be2b6fd..bf60d1c2ea 100644 --- a/core/src/main/java/io/cucumber/core/stepexpression/ArgumentMatcher.java +++ b/core/src/main/java/io/cucumber/core/stepexpression/ArgumentMatcher.java @@ -1,8 +1,8 @@ package io.cucumber.core.stepexpression; -import io.cucumber.core.feature.CucumberStep; -import io.cucumber.core.feature.DataTableArgument; -import io.cucumber.core.feature.DocStringArgument; +import io.cucumber.core.gherkin.Step; +import io.cucumber.core.gherkin.DataTableArgument; +import io.cucumber.core.gherkin.DocStringArgument; import java.lang.reflect.Type; import java.util.List; @@ -16,20 +16,28 @@ public ArgumentMatcher(StepExpression expression) { this.expression = expression; } - public List argumentsFrom(CucumberStep step, Type... types) { - io.cucumber.core.feature.Argument arg = step.getArgument(); + private static List> emptyCellsToNull(List> cells) { + return cells.stream() + .map(row -> row.stream() + .map(s -> s.isEmpty() ? null : s) + .collect(Collectors.toList())) + .collect(Collectors.toList()); + } + + public List argumentsFrom(Step step, Type... types) { + io.cucumber.core.gherkin.Argument arg = step.getArgument(); if (arg == null) { return expression.match(step.getText(), types); } - if (arg instanceof DocStringArgument) { + if (arg instanceof io.cucumber.core.gherkin.DocStringArgument) { DocStringArgument docString = (DocStringArgument) arg; String content = docString.getContent(); String contentType = docString.getContentType(); return expression.match(step.getText(), content, contentType, types); } - if (arg instanceof DataTableArgument) { + if (arg instanceof io.cucumber.core.gherkin.DataTableArgument) { DataTableArgument table = (DataTableArgument) arg; List> cells = emptyCellsToNull(table.cells()); return expression.match(step.getText(), cells, types); @@ -38,12 +46,4 @@ public List argumentsFrom(CucumberStep step, Type... types) { throw new IllegalStateException("Argument was neither PickleString nor PickleTable"); } - private static List> emptyCellsToNull(List> cells) { - return cells.stream() - .map(row -> row.stream() - .map(s -> s.isEmpty() ? null : s) - .collect(Collectors.toList())) - .collect(Collectors.toList()); - } - } diff --git a/core/src/test/java/io/cucumber/core/feature/TestFeatureParser.java b/core/src/test/java/io/cucumber/core/feature/TestFeatureParser.java index 6327351b11..518f4c6b24 100644 --- a/core/src/test/java/io/cucumber/core/feature/TestFeatureParser.java +++ b/core/src/test/java/io/cucumber/core/feature/TestFeatureParser.java @@ -1,23 +1,25 @@ package io.cucumber.core.feature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.resource.Resource; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.util.UUID; public class TestFeatureParser { - public static CucumberFeature parse(final String source) { + public static Feature parse(final String source) { return parse("file:test.feature", source); } - public static CucumberFeature parse(final String uri, final String source) { + public static Feature parse(final String uri, final String source) { return parse(FeatureIdentifier.parse(uri), source); } - public static CucumberFeature parse(final URI uri, final String source) { - return FeatureParser.parseResource(new Resource() { + public static Feature parse(final URI uri, final String source) { + return new FeatureParser(UUID::randomUUID).parseResource(new Resource() { @Override public URI getUri() { return uri; diff --git a/core/src/test/java/io/cucumber/core/filter/LinePredicateTest.java b/core/src/test/java/io/cucumber/core/filter/LinePredicateTest.java index 5b77688b87..99b940894a 100644 --- a/core/src/test/java/io/cucumber/core/filter/LinePredicateTest.java +++ b/core/src/test/java/io/cucumber/core/filter/LinePredicateTest.java @@ -1,7 +1,7 @@ package io.cucumber.core.filter; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.feature.TestFeatureParser; import org.junit.jupiter.api.Test; @@ -16,7 +16,7 @@ class LinePredicateTest { - private final CucumberFeature feature = TestFeatureParser.parse( + private final Feature feature = TestFeatureParser.parse( "file:path/file.feature", "" + "Feature: Test feature\n" + @@ -27,7 +27,7 @@ class LinePredicateTest { " | cucumber | \n" + " | gherkin | \n" ); - private final CucumberPickle pickle = feature.getPickles().get(0); + private final Pickle pickle = feature.getPickles().get(0); @Test void matches_pickles_from_files_not_in_the_predicate_map() { diff --git a/core/src/test/java/io/cucumber/core/filter/NamePredicateTest.java b/core/src/test/java/io/cucumber/core/filter/NamePredicateTest.java index caa4373de8..e0ad978af2 100644 --- a/core/src/test/java/io/cucumber/core/filter/NamePredicateTest.java +++ b/core/src/test/java/io/cucumber/core/filter/NamePredicateTest.java @@ -1,7 +1,7 @@ package io.cucumber.core.filter; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.feature.TestFeatureParser; import org.junit.jupiter.api.Test; @@ -15,7 +15,7 @@ class NamePredicateTest { @Test void anchored_name_pattern_matches_exact_name() { - CucumberPickle pickle = createPickleWithName("a pickle name"); + Pickle pickle = createPickleWithName("a pickle name"); NamePredicate predicate = new NamePredicate(singletonList(Pattern.compile("^a pickle name$"))); assertTrue(predicate.test(pickle)); @@ -23,7 +23,7 @@ void anchored_name_pattern_matches_exact_name() { @Test void anchored_name_pattern_does_not_match_part_of_name() { - CucumberPickle pickle = createPickleWithName("a pickle name with suffix"); + Pickle pickle = createPickleWithName("a pickle name with suffix"); NamePredicate predicate = new NamePredicate(singletonList(Pattern.compile("^a pickle name$"))); assertFalse(predicate.test(pickle)); @@ -31,7 +31,7 @@ void anchored_name_pattern_does_not_match_part_of_name() { @Test void non_anchored_name_pattern_matches_part_of_name() { - CucumberPickle pickle = createPickleWithName("a pickle name with suffix"); + Pickle pickle = createPickleWithName("a pickle name with suffix"); NamePredicate predicate = new NamePredicate(singletonList(Pattern.compile("a pickle name"))); assertTrue(predicate.test(pickle)); @@ -39,14 +39,14 @@ void non_anchored_name_pattern_matches_part_of_name() { @Test void wildcard_name_pattern_matches_part_of_name() { - CucumberPickle pickle = createPickleWithName("a pickle name"); + Pickle pickle = createPickleWithName("a pickle name"); NamePredicate predicate = new NamePredicate(singletonList(Pattern.compile("a .* name"))); assertTrue(predicate.test(pickle)); } - private CucumberPickle createPickleWithName(String pickleName) { - CucumberFeature feature = TestFeatureParser.parse("file:path/file.feature", "" + + private Pickle createPickleWithName(String pickleName) { + Feature feature = TestFeatureParser.parse("file:path/file.feature", "" + "Feature: Test feature\n" + " Scenario: " + pickleName + "\n" + " Given I have 4 cukes in my belly\n" diff --git a/core/src/test/java/io/cucumber/core/filter/TagPredicateTest.java b/core/src/test/java/io/cucumber/core/filter/TagPredicateTest.java index 1bb90ad938..28e0cf0bf4 100644 --- a/core/src/test/java/io/cucumber/core/filter/TagPredicateTest.java +++ b/core/src/test/java/io/cucumber/core/filter/TagPredicateTest.java @@ -1,7 +1,7 @@ package io.cucumber.core.filter; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.feature.TestFeatureParser; import org.junit.jupiter.api.Test; @@ -14,7 +14,7 @@ class TagPredicateTest { @Test void empty_tag_predicate_matches_pickle_with_any_tags() { - CucumberPickle pickle = createPickleWithTags("@FOO"); + Pickle pickle = createPickleWithTags("@FOO"); TagPredicate predicate = new TagPredicate(""); assertTrue(predicate.test(pickle)); } @@ -22,90 +22,90 @@ void empty_tag_predicate_matches_pickle_with_any_tags() { @Test void list_of_empty_tag_predicates_matches_pickle_with_any_tags() { - CucumberPickle pickle = createPickleWithTags("@FOO"); + Pickle pickle = createPickleWithTags("@FOO"); TagPredicate predicate = new TagPredicate(asList("", "")); assertTrue(predicate.test(pickle)); } @Test void single_tag_predicate_does_not_match_pickle_with_no_tags() { - CucumberPickle pickle = createPickleWithTags(); + Pickle pickle = createPickleWithTags(); TagPredicate predicate = new TagPredicate("@FOO"); assertFalse(predicate.test(pickle)); } @Test void single_tag_predicate_matches_pickle_with_same_single_tag() { - CucumberPickle pickle = createPickleWithTags("@FOO"); + Pickle pickle = createPickleWithTags("@FOO"); TagPredicate predicate = new TagPredicate("@FOO"); assertTrue(predicate.test(pickle)); } @Test void single_tag_predicate_matches_pickle_with_more_tags() { - CucumberPickle pickle = createPickleWithTags("@FOO", "@BAR"); + Pickle pickle = createPickleWithTags("@FOO", "@BAR"); TagPredicate predicate = new TagPredicate("@FOO"); assertTrue(predicate.test(pickle)); } @Test void single_tag_predicate_does_not_match_pickle_with_different_single_tag() { - CucumberPickle pickle = createPickleWithTags("@BAR"); + Pickle pickle = createPickleWithTags("@BAR"); TagPredicate predicate = new TagPredicate("@FOO"); assertFalse(predicate.test(pickle)); } @Test void not_tag_predicate_matches_pickle_with_no_tags() { - CucumberPickle pickle = createPickleWithTags(); + Pickle pickle = createPickleWithTags(); TagPredicate predicate = new TagPredicate(singletonList("not @FOO")); assertTrue(predicate.test(pickle)); } @Test void not_tag_predicate_does_not_match_pickle_with_same_single_tag() { - CucumberPickle pickle = createPickleWithTags("@FOO"); + Pickle pickle = createPickleWithTags("@FOO"); TagPredicate predicate = new TagPredicate(singletonList("not @FOO")); assertFalse(predicate.test(pickle)); } @Test void not_tag_predicate_matches_pickle_with_different_single_tag() { - CucumberPickle pickle = createPickleWithTags("@BAR"); + Pickle pickle = createPickleWithTags("@BAR"); TagPredicate predicate = new TagPredicate(singletonList("not @FOO")); assertTrue(predicate.test(pickle)); } @Test void and_tag_predicate_matches_pickle_with_all_tags() { - CucumberPickle pickle = createPickleWithTags("@FOO", "@BAR"); + Pickle pickle = createPickleWithTags("@FOO", "@BAR"); TagPredicate predicate = new TagPredicate(singletonList("@FOO and @BAR")); assertTrue(predicate.test(pickle)); } @Test void and_tag_predicate_does_not_match_pickle_with_one_of_the_tags() { - CucumberPickle pickle = createPickleWithTags("@FOO"); + Pickle pickle = createPickleWithTags("@FOO"); TagPredicate predicate = new TagPredicate(singletonList("@FOO and @BAR")); assertFalse(predicate.test(pickle)); } @Test void or_tag_predicate_matches_pickle_with_one_of_the_tags() { - CucumberPickle pickle = createPickleWithTags("@FOO"); + Pickle pickle = createPickleWithTags("@FOO"); TagPredicate predicate = new TagPredicate(singletonList("@FOO or @BAR")); assertTrue(predicate.test(pickle)); } @Test void or_tag_predicate_does_not_match_pickle_none_of_the_tags() { - CucumberPickle pickle = createPickleWithTags(); + Pickle pickle = createPickleWithTags(); TagPredicate predicate = new TagPredicate(singletonList("@FOO or @BAR")); assertFalse(predicate.test(pickle)); } - private CucumberPickle createPickleWithTags(String... tags) { - CucumberFeature feature = TestFeatureParser.parse("" + + private Pickle createPickleWithTags(String... tags) { + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " " + String.join(" ", tags) + "\n" + " Scenario: Test scenario\n" + diff --git a/core/src/test/java/io/cucumber/core/options/CucumberOptionsAnnotationParserTest.java b/core/src/test/java/io/cucumber/core/options/CucumberOptionsAnnotationParserTest.java index 0180ef5b4f..c801e7c047 100644 --- a/core/src/test/java/io/cucumber/core/options/CucumberOptionsAnnotationParserTest.java +++ b/core/src/test/java/io/cucumber/core/options/CucumberOptionsAnnotationParserTest.java @@ -14,6 +14,7 @@ import java.time.Clock; import java.util.Iterator; import java.util.List; +import java.util.UUID; import java.util.regex.Pattern; import static org.hamcrest.MatcherAssert.assertThat; @@ -62,7 +63,7 @@ void create_without_options() { ); Plugins plugins = new Plugins(new PluginFactory(), runtimeOptions); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertAll("Checking Plugins", () -> assertThat(plugins.getPlugins(), hasSize(2)), @@ -84,7 +85,7 @@ void create_without_options_with_base_class_without_options() { .addDefaultSummaryPrinterIfAbsent() .build(); Plugins plugins = new Plugins(new PluginFactory(), runtimeOptions); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertAll("Checking Plugins", () -> assertThat(runtimeOptions.getFeaturePaths(), contains(uri("classpath:/io/cucumber/core/options"))), @@ -143,7 +144,7 @@ void create_default_summary_printer_when_no_summary_printer_plugin_is_defined() .addDefaultSummaryPrinterIfAbsent() .build(); Plugins plugins = new Plugins(new PluginFactory(), runtimeOptions); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertPluginExists(plugins.getPlugins(), "io.cucumber.core.plugin.DefaultSummaryPrinter"); } @@ -151,7 +152,7 @@ void create_default_summary_printer_when_no_summary_printer_plugin_is_defined() void inherit_plugin_from_baseclass() { RuntimeOptions runtimeOptions = parser().parse(SubClassWithFormatter.class).build(); Plugins plugins = new Plugins(new PluginFactory(), runtimeOptions); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); List pluginList = plugins.getPlugins(); assertAll("Checking Plugin", diff --git a/core/src/test/java/io/cucumber/core/options/RuntimeOptionsTest.java b/core/src/test/java/io/cucumber/core/options/RuntimeOptionsTest.java index 03477fdd82..bdf43b1380 100644 --- a/core/src/test/java/io/cucumber/core/options/RuntimeOptionsTest.java +++ b/core/src/test/java/io/cucumber/core/options/RuntimeOptionsTest.java @@ -1,12 +1,11 @@ package io.cucumber.core.options; import io.cucumber.core.exception.CucumberException; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.plugin.PluginFactory; import io.cucumber.core.plugin.Plugins; -import io.cucumber.core.runner.ClockStub; import io.cucumber.core.runtime.TimeServiceEventBus; import io.cucumber.core.snippets.SnippetType; import io.cucumber.plugin.ColorAware; @@ -31,6 +30,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.regex.Pattern; import static io.cucumber.core.options.Constants.FILTER_TAGS_PROPERTY_NAME; @@ -157,7 +157,7 @@ void creates_html_formatter() { .parse("--plugin", "html:target/some/dir", "--glue", "somewhere") .build(); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(ClockStub.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertThat(plugins.getPlugins().get(0).getClass().getName(), is("io.cucumber.core.plugin.HTMLFormatter")); } @@ -169,7 +169,7 @@ void creates_progress_formatter_as_default() { .addDefaultFormatterIfAbsent() .build(); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertThat(plugins.getPlugins().get(0).getClass().getName(), is("io.cucumber.core.plugin.ProgressFormatter")); } @@ -181,7 +181,7 @@ void creates_default_summary_printer_when_no_summary_printer_plugin_is_specified .addDefaultSummaryPrinterIfAbsent() .build(); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertThat(plugins.getPlugins(), hasItem(plugin("io.cucumber.core.plugin.DefaultSummaryPrinter"))); } @@ -192,7 +192,7 @@ void creates_null_summary_printer() { .parse("--plugin", "null_summary", "--glue", "somewhere") .build(); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertAll( () -> assertThat(plugins.getPlugins(), hasItem(plugin("io.cucumber.core.plugin.NullSummaryPrinter"))), @@ -206,7 +206,7 @@ void replaces_incompatible_intellij_idea_plugin() { .parse("--plugin", "org.jetbrains.plugins.cucumber.java.run.CucumberJvm3SMFormatter") .build(); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertThat(plugins.getPlugins(), not(hasItem(plugin("io.cucumber.core.plugin.PrettyPrinter")))); } @@ -474,7 +474,7 @@ void clobbers_formatter_plugins_from_cli_if_formatters_specified_in_cucumber_opt .parse(properties) .build(runtimeOptions); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertAll("Checking Plugins", () -> assertThat(plugins.getPlugins(), hasItem(plugin("io.cucumber.core.plugin.PrettyFormatter"))), @@ -494,7 +494,7 @@ void adds_to_formatter_plugins_with_add_plugin_option() { .parse(properties) .build(runtimeOptions); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertAll("Checking Plugins", () -> assertThat(plugins.getPlugins(), hasItem(plugin("io.cucumber.core.plugin.HTMLFormatter"))), @@ -513,7 +513,7 @@ void clobbers_summary_plugins_from_cli_if_summary_printer_specified_in_cucumber_ .parse(properties) .build(runtimeOptions); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertAll("Checking Plugins", () -> assertThat(plugins.getPlugins(), hasItem(plugin("io.cucumber.core.plugin.DefaultSummaryPrinter"))), @@ -532,7 +532,7 @@ void adds_to_summary_plugins_with_add_plugin_option() { .parse(properties) .build(runtimeOptions); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertAll("Checking Plugins", () -> assertThat(plugins.getPlugins(), hasItem(plugin("io.cucumber.core.plugin.NullSummaryPrinter"))), @@ -552,7 +552,7 @@ void does_not_clobber_plugins_of_different_type_when_specifying_plugins_in_cucum .parse(properties) .build(runtimeOptions); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); assertAll("Checking Plugins", () -> assertThat(plugins.getPlugins(), hasItem(plugin("io.cucumber.core.plugin.PrettyFormatter"))), @@ -612,7 +612,7 @@ void set_monochrome_on_color_aware_formatters() { .parse("--monochrome", "--plugin", AwareFormatter.class.getName()) .build(); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); AwareFormatter formatter = (AwareFormatter) plugins.getPlugins().get(0); assertThat(formatter.isMonochrome(), is(true)); @@ -624,7 +624,7 @@ void set_strict_on_strict_aware_formatters() { .parse("--strict", "--plugin", AwareFormatter.class.getName()) .build(); Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC())); + plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); AwareFormatter formatter = (AwareFormatter) plugins.getPlugins().get(0); assertThat(formatter.isStrict(), is(true)); @@ -659,8 +659,8 @@ void order_type_default_none() { RuntimeOptions options = new CommandlineOptionsParser() .parse(Collections.emptyList()) .build(); - CucumberPickle a = createPickle("file:path/file1.feature", "a"); - CucumberPickle b = createPickle("file:path/file2.feature", "b"); + Pickle a = createPickle("file:path/file1.feature", "a"); + Pickle b = createPickle("file:path/file2.feature", "b"); assertThat(options.getPickleOrder() .orderPickles(Arrays.asList(a, b)), contains(a, b)); } @@ -670,8 +670,8 @@ void ensure_order_type_reverse_is_used() { RuntimeOptions options = new CommandlineOptionsParser() .parse("--order", "reverse") .build(); - CucumberPickle a = createPickle("file:path/file1.feature", "a"); - CucumberPickle b = createPickle("file:path/file2.feature", "b"); + Pickle a = createPickle("file:path/file1.feature", "a"); + Pickle b = createPickle("file:path/file2.feature", "b"); assertThat(options.getPickleOrder() .orderPickles(Arrays.asList(a, b)), contains(b, a)); } @@ -688,15 +688,15 @@ void ensure_order_type_random_with_seed_is_used() { RuntimeOptions options = new CommandlineOptionsParser() .parse("--order", "random:5000") .build(); - CucumberPickle a = createPickle("file:path/file1.feature", "a"); - CucumberPickle b = createPickle("file:path/file2.feature", "b"); - CucumberPickle c = createPickle("file:path/file3.feature", "c"); + Pickle a = createPickle("file:path/file1.feature", "a"); + Pickle b = createPickle("file:path/file2.feature", "b"); + Pickle c = createPickle("file:path/file3.feature", "c"); assertThat(options.getPickleOrder() .orderPickles(Arrays.asList(a, b, c)), contains(c, a, b)); } - private CucumberPickle createPickle(String uri, String name) { - CucumberFeature feature = TestFeatureParser.parse(uri, "" + + private Pickle createPickle(String uri, String name) { + Feature feature = TestFeatureParser.parse(uri, "" + "Feature: Test feature\n" + " Scenario: " + name + "\n" + " Given I have 4 cukes in my belly\n" diff --git a/core/src/test/java/io/cucumber/core/plugin/HTMLFormatterTest.java b/core/src/test/java/io/cucumber/core/plugin/HTMLFormatterTest.java index 8ce7c2f4fe..269c0edf95 100644 --- a/core/src/test/java/io/cucumber/core/plugin/HTMLFormatterTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/HTMLFormatterTest.java @@ -2,7 +2,7 @@ import gherkin.deps.com.google.gson.JsonParser; import io.cucumber.plugin.event.Result; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.runner.TestHelper; import org.jsoup.Jsoup; @@ -46,7 +46,7 @@ class HTMLFormatterTest { private final static String jsFunctionCallRegexString = "formatter.(\\w*)\\(([^)]*)\\);"; private final static Pattern jsFunctionCallRegex = Pattern.compile(jsFunctionCallRegexString); - private final List features = new ArrayList<>(); + private final List features = new ArrayList<>(); private final Map stepsToResult = new HashMap<>(); private final Map stepsToLocation = new HashMap<>(); private final List> hooks = new ArrayList<>(); @@ -126,7 +126,7 @@ void included_embedding() throws Throwable { @Test void should_handle_a_single_scenario() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -177,7 +177,7 @@ void should_handle_a_single_scenario() { @Test void should_handle_backgound() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Background: background name\n" + " Given first step\n" + @@ -262,7 +262,7 @@ void should_handle_backgound() { @Test void should_handle_scenario_outline() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario Outline: outline name\n" + " Given first step\n" + @@ -379,7 +379,7 @@ void should_handle_scenario_outline() { @Test void should_handle_before_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -415,7 +415,7 @@ void should_handle_before_hooks() { @Test void should_handle_after_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -451,7 +451,7 @@ void should_handle_after_hooks() { @Test void should_handle_after_step_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -510,7 +510,7 @@ void should_handle_after_step_hooks() { @Test void should_handle_output_from_before_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -548,7 +548,7 @@ void should_handle_output_from_before_hooks() { @Test void should_handle_output_from_after_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -586,7 +586,7 @@ void should_handle_output_from_after_hooks() { @Test void should_handle_output_from_after_step_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -641,7 +641,7 @@ void should_handle_output_from_after_step_hooks() { @Test void should_handle_text_embeddings_from_before_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -745,7 +745,7 @@ private void assertContains(String substring, String string) { private void runFeaturesWithFormatter(URL outputDir) { final HTMLFormatter f = new HTMLFormatter(outputDir); - CucumberFeature feature = TestFeatureParser.parse("some/path/some.feature", "" + + Feature feature = TestFeatureParser.parse("some/path/some.feature", "" + "Feature:\n" + " Scenario: some cukes\n" + " Given first step\n"); diff --git a/core/src/test/java/io/cucumber/core/plugin/JSONFormatterTest.java b/core/src/test/java/io/cucumber/core/plugin/JSONFormatterTest.java index a90522eefe..5c2dc84e88 100755 --- a/core/src/test/java/io/cucumber/core/plugin/JSONFormatterTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/JSONFormatterTest.java @@ -3,7 +3,7 @@ import io.cucumber.core.backend.Glue; import io.cucumber.core.backend.HookDefinition; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.options.CommandlineOptionsParser; import io.cucumber.core.options.RuntimeOptions; @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Scanner; +import java.util.UUID; import static io.cucumber.core.runner.TestHelper.createEmbedHookAction; import static io.cucumber.core.runner.TestHelper.createWriteHookAction; @@ -40,7 +41,7 @@ class JSONFormatterTest { - private final List features = new ArrayList<>(); + private final List features = new ArrayList<>(); private final Map stepsToResult = new HashMap<>(); private final Map stepsToLocation = new HashMap<>(); private final List> hooks = new ArrayList<>(); @@ -59,7 +60,6 @@ void featureWithOutlineTest() { assertThat(actual, sameJSONAs(expected)); } - @Test void featureWithOutlineTestParallel() throws Exception { List featurePaths = singletonList("classpath:io/cucumber/core/plugin/JSONPrettyFormatterTest.feature"); @@ -74,7 +74,7 @@ void featureWithOutlineTestParallel() throws Exception { @Test void should_format_scenario_with_an_undefined_step() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + @@ -123,7 +123,7 @@ void should_format_scenario_with_an_undefined_step() { @Test void should_format_scenario_with_a_passed_step() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + @@ -177,7 +177,7 @@ void should_format_scenario_with_a_passed_step() { @Test void should_format_scenario_with_a_failed_step() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + @@ -232,7 +232,7 @@ void should_format_scenario_with_a_failed_step() { @Test void should_format_scenario_outline_with_one_example() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Fruit party\n" + "\n" + " Scenario Outline: Monkey eats fruits\n" + @@ -289,7 +289,7 @@ void should_format_scenario_outline_with_one_example() { @Test void should_format_feature_with_background() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Background: There are bananas\n" + @@ -418,7 +418,7 @@ void should_format_feature_with_background() { @Test void should_format_feature_and_scenario_with_tags() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "@Party @Banana\n" + "Feature: Banana party\n" + " @Monkey\n" + @@ -501,7 +501,7 @@ void should_format_feature_and_scenario_with_tags() { @Test void should_format_scenario_with_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + @@ -581,7 +581,7 @@ void should_format_scenario_with_hooks() { @Test void should_add_step_hooks_to_step() { - CucumberFeature feature = TestFeatureParser.parse("file:path/test.feature", "" + + Feature feature = TestFeatureParser.parse("file:path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + @@ -718,7 +718,7 @@ void should_add_step_hooks_to_step() { @Test void should_handle_write_from_a_hook() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + @@ -789,7 +789,7 @@ void should_handle_write_from_a_hook() { @Test void should_handle_embed_from_a_hook() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + @@ -863,7 +863,7 @@ void should_handle_embed_from_a_hook() { @Test void should_handle_embed_with_name_from_a_hook() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + @@ -938,7 +938,7 @@ void should_handle_embed_with_name_from_a_hook() { @Test void should_format_scenario_with_a_step_with_a_doc_string() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + @@ -999,7 +999,7 @@ void should_format_scenario_with_a_step_with_a_doc_string() { @Test void should_format_scenario_with_a_step_with_a_doc_string_and_content_type() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + @@ -1061,7 +1061,7 @@ void should_format_scenario_with_a_step_with_a_doc_string_and_content_type() { @Test void should_format_scenario_with_a_step_with_a_data_table() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + @@ -1131,12 +1131,12 @@ void should_format_scenario_with_a_step_with_a_data_table() { @Test void should_handle_several_features() { - CucumberFeature feature1 = TestFeatureParser.parse("path/test1.feature", "" + + Feature feature1 = TestFeatureParser.parse("path/test1.feature", "" + "Feature: Banana party\n" + "\n" + " Scenario: Monkey eats bananas\n" + " Given there are bananas\n"); - CucumberFeature feature2 = TestFeatureParser.parse("path/test2.feature", "" + + Feature feature2 = TestFeatureParser.parse("path/test2.feature", "" + "Feature: Orange party\n" + "\n" + " Scenario: Monkey eats oranges\n" + @@ -1244,7 +1244,7 @@ public void loadGlue(Glue glue, List gluePaths) { } }; - final EventBus bus = new TimeServiceEventBus(new ClockStub(ofMillis(1234L))); + final EventBus bus = new TimeServiceEventBus(new ClockStub(ofMillis(1234L)), UUID::randomUUID); Appendable stringBuilder = new StringBuilder(); @@ -1276,7 +1276,7 @@ public void loadGlue(Glue glue, List gluePaths) { } }; - final EventBus bus = new TimeServiceEventBus(new ClockStub(ofMillis(1234L))); + final EventBus bus = new TimeServiceEventBus(new ClockStub(ofMillis(1234L)), UUID::randomUUID); Appendable stringBuilder = new StringBuilder(); diff --git a/core/src/test/java/io/cucumber/core/plugin/JUnitFormatterTest.java b/core/src/test/java/io/cucumber/core/plugin/JUnitFormatterTest.java index 630cd32229..f656eb83b0 100644 --- a/core/src/test/java/io/cucumber/core/plugin/JUnitFormatterTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/JUnitFormatterTest.java @@ -1,6 +1,6 @@ package io.cucumber.core.plugin; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.runner.TestHelper; import io.cucumber.plugin.event.Result; @@ -30,7 +30,7 @@ class JUnitFormatterTest { - private final List features = new ArrayList<>(); + private final List features = new ArrayList<>(); private final Map stepsToResult = new HashMap<>(); private final Map stepsToLocation = new HashMap<>(); private final List> hooks = new ArrayList<>(); @@ -39,7 +39,7 @@ class JUnitFormatterTest { private Duration stepDuration = null; private boolean strict = false; - private static void assertXmlEqual(Object expected, Object actual) throws IOException { + private static void assertXmlEqual(Object expected, Object actual) { assertThat(actual, isIdenticalTo(expected).ignoreWhitespace()); assertThat(actual, valid(JUnitFormatterTest.class.getResourceAsStream("/io/cucumber/core/plugin/surefire-test-report-3.0.xsd"))); } @@ -71,7 +71,7 @@ void featureSimpleStrictTest() throws Exception { @Test void should_format_passed_scenario() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -100,7 +100,7 @@ void should_format_passed_scenario() throws Throwable { @Test void should_format_empty_scenario() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario: scenario name\n"); features.add(feature); @@ -119,7 +119,7 @@ void should_format_empty_scenario() throws Throwable { @Test void should_format_empty_scenario_strict() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario: scenario name\n"); features.add(feature); @@ -139,7 +139,7 @@ void should_format_empty_scenario_strict() throws Throwable { @Test void should_format_skipped_scenario() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -173,7 +173,7 @@ void should_format_skipped_scenario() throws Throwable { @Test void should_format_pending_scenario() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -202,7 +202,7 @@ void should_format_pending_scenario() throws Throwable { @Test void should_format_failed_scenario() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -233,7 +233,7 @@ void should_format_failed_scenario() throws Throwable { @Test void should_handle_failure_in_before_hook() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -266,7 +266,7 @@ void should_handle_failure_in_before_hook() throws Throwable { @Test void should_handle_pending_in_before_hook() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -296,7 +296,7 @@ void should_handle_pending_in_before_hook() throws Throwable { @Test void should_handle_failure_in_before_hook_with_background() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Background: background name\n" + " Given first step\n" + @@ -330,7 +330,7 @@ void should_handle_failure_in_before_hook_with_background() throws Throwable { @Test void should_handle_failure_in_after_hook() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -363,7 +363,7 @@ void should_handle_failure_in_after_hook() throws Throwable { @Test void should_accumulate_time_from_steps_and_hooks() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario: scenario name\n" + " * first step\n" + @@ -391,7 +391,7 @@ void should_accumulate_time_from_steps_and_hooks() throws Throwable { @Test void should_format_scenario_outlines() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario Outline: outline_name\n" + " Given first step \"\"\n" + @@ -432,7 +432,7 @@ void should_format_scenario_outlines() throws Throwable { @Test void should_format_scenario_outlines_with_multiple_examples() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario Outline: outline name\n" + " Given first step \"\"\n" + @@ -493,7 +493,7 @@ void should_format_scenario_outlines_with_multiple_examples() throws Throwable { @Test void should_format_scenario_outlines_with_arguments_in_name() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario Outline: outline name \n" + " Given first step \"\"\n" + diff --git a/core/src/test/java/io/cucumber/core/plugin/JsonParallelRuntimeTest.java b/core/src/test/java/io/cucumber/core/plugin/JsonParallelRuntimeTest.java index 12e05b5add..0e5f846cf6 100644 --- a/core/src/test/java/io/cucumber/core/plugin/JsonParallelRuntimeTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/JsonParallelRuntimeTest.java @@ -6,6 +6,8 @@ import io.cucumber.core.runtime.TimeServiceEventBus; import org.junit.jupiter.api.Test; +import java.util.UUID; + import static java.time.Duration.ZERO; import static org.hamcrest.MatcherAssert.assertThat; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; @@ -26,7 +28,7 @@ void testSingleFeature() { .build() ) .withAdditionalPlugins(new JSONFormatter(parallel)) - .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO))) + .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO), UUID::randomUUID)) .build() .run(); @@ -41,7 +43,7 @@ void testSingleFeature() { .build() ) .withAdditionalPlugins(new JSONFormatter(serial)) - .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO))) + .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO), UUID::randomUUID)) .build() .run(); @@ -61,7 +63,7 @@ void testMultipleFeatures() { .build() ) .withAdditionalPlugins(new JSONFormatter(parallel)) - .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO))) + .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO), UUID::randomUUID)) .build() .run(); @@ -75,7 +77,7 @@ void testMultipleFeatures() { "src/test/resources/io/cucumber/core/plugin/FormatterInParallel.feature") .build()) .withAdditionalPlugins(new JSONFormatter(serial)) - .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO))) + .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO), UUID::randomUUID)) .build() .run(); diff --git a/core/src/test/java/io/cucumber/core/plugin/PluginFactoryTest.java b/core/src/test/java/io/cucumber/core/plugin/PluginFactoryTest.java index a1cd9400bf..85abba0cf9 100644 --- a/core/src/test/java/io/cucumber/core/plugin/PluginFactoryTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/PluginFactoryTest.java @@ -21,6 +21,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.util.UUID; import static io.cucumber.core.options.TestPluginOption.parse; import static java.time.Duration.ZERO; @@ -93,7 +94,7 @@ void plugin_does_not_buffer_its_output() { fc = new PluginFactory(); ProgressFormatter plugin = (ProgressFormatter) fc.create(parse("progress")); - EventBus bus = new TimeServiceEventBus(new ClockStub(ZERO)); + EventBus bus = new TimeServiceEventBus(new ClockStub(ZERO), UUID::randomUUID); plugin.setEventPublisher(bus); Result result = new Result(Status.PASSED, ZERO, null); TestStepFinished event = new TestStepFinished(bus.getInstant(), mock(TestCase.class), mock(PickleStepTestStep.class), result); 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 5e9b911671..9d8f3567cb 100755 --- a/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java @@ -1,12 +1,12 @@ package io.cucumber.core.plugin; -import io.cucumber.plugin.event.Result; -import io.cucumber.core.feature.CucumberFeature; import io.cucumber.core.feature.TestFeatureParser; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.runner.TestHelper; import io.cucumber.core.stepexpression.StepExpression; import io.cucumber.core.stepexpression.StepExpressionFactory; import io.cucumber.core.stepexpression.StepTypeRegistry; +import io.cucumber.plugin.event.Result; import org.junit.jupiter.api.Test; import org.mockito.stubbing.Answer; @@ -26,7 +26,7 @@ class PrettyFormatterTest { - private final List features = new ArrayList<>(); + private final List features = new ArrayList<>(); private final Map stepsToResult = new HashMap<>(); private final Map stepsToLocation = new HashMap<>(); private final List> hooks = new ArrayList<>(); @@ -35,7 +35,7 @@ class PrettyFormatterTest { @Test void should_align_the_indentation_of_location_strings() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -49,17 +49,16 @@ void should_align_the_indentation_of_location_strings() { String formatterOutput = runFeaturesWithFormatter(true); assertThat(formatterOutput, equalTo("" + - "Feature: feature name\n" + "\n" + - " Scenario: scenario name # path/test.feature:2\n" + - " Given first step # path/step_definitions.java:3\n" + - " When second step # path/step_definitions.java:7\n" + - " Then third step # path/step_definitions.java:11\n")); + "Scenario: scenario name # path/test.feature:2\n" + + " Given first step # path/step_definitions.java:3\n" + + " When second step # path/step_definitions.java:7\n" + + " Then third step # path/step_definitions.java:11\n")); } @Test void should_handle_background() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Background: background name\n" + " Given first step\n" + @@ -74,23 +73,20 @@ void should_handle_background() { String formatterOutput = runFeaturesWithFormatter(true); - assertThat(formatterOutput, containsString("\n" + - " Background: background name # path/test.feature:2\n" + - " Given first step # path/step_definitions.java:3\n" + - "\n" + - " Scenario: s1 # path/test.feature:4\n" + - " Then second step # path/step_definitions.java:7\n" + + assertThat(formatterOutput, containsString("" + "\n" + - " Background: background name # path/test.feature:2\n" + - " Given first step # path/step_definitions.java:3\n" + + "Scenario: s1 # path/test.feature:4\n" + + " Given first step # path/step_definitions.java:3\n" + + " Then second step # path/step_definitions.java:7\n" + "\n" + - " Scenario: s2 # path/test.feature:6\n" + - " Then third step # path/step_definitions.java:11\n")); + "Scenario: s2 # path/test.feature:6\n" + + " Given first step # path/step_definitions.java:3\n" + + " Then third step # path/step_definitions.java:11\n")); } @Test void should_handle_scenario_outline() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario Outline: \n" + " Given first step\n" + @@ -106,80 +102,20 @@ void should_handle_scenario_outline() { String formatterOutput = runFeaturesWithFormatter(true); - assertThat(formatterOutput, containsString("\n" + - " Scenario Outline: # path/test.feature:2\n" + - " Given first step\n" + - " Then step\n" + - "\n" + - " Examples: examples name\n" + - "\n" + - " Scenario Outline: name 1 # path/test.feature:7\n" + - " Given first step # path/step_definitions.java:3\n" + - " Then second step # path/step_definitions.java:7\n" + - "\n" + - " Scenario Outline: name 2 # path/test.feature:8\n" + - " Given first step # path/step_definitions.java:3\n" + - " Then third step # path/step_definitions.java:11\n")); - } - - @Test - void should_print_descriptions() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: feature name\n" + - " feature description\n" + - " ...\n" + - " Background: background name\n" + - " background description\n" + - " Given first step\n" + - " Scenario: scenario name\n" + - " scenario description\n" + - " Then second step\n" + - " Scenario Outline: scenario outline name\n" + - " scenario outline description\n" + - " Then step\n" + - " Examples: examples name\n" + - " examples description\n" + - " | arg |\n" + - " | third |\n"); - features.add(feature); - stepsToLocation.put("first step", "path/step_definitions.java:3"); - stepsToLocation.put("second step", "path/step_definitions.java:7"); - stepsToLocation.put("third step", "path/step_definitions.java:11"); - - String formatterOutput = runFeaturesWithFormatter(true); - - assertThat(formatterOutput, equalTo("" + - "Feature: feature name\n" + - " feature description\n" + - " ...\n" + - "\n" + - " Background: background name # path/test.feature:4\n" + - " background description\n" + - " Given first step # path/step_definitions.java:3\n" + - "\n" + - " Scenario: scenario name # path/test.feature:7\n" + - " scenario description\n" + - " Then second step # path/step_definitions.java:7\n" + - "\n" + - " Scenario Outline: scenario outline name # path/test.feature:10\n" + - " scenario outline description\n" + - " Then step\n" + - "\n" + - " Examples: examples name\n" + - " examples description\n" + + assertThat(formatterOutput, containsString("" + "\n" + - " Background: background name # path/test.feature:4\n" + - " background description\n" + - " Given first step # path/step_definitions.java:3\n" + + "Scenario Outline: name 1 # path/test.feature:7\n" + + " Given first step # path/step_definitions.java:3\n" + + " Then second step # path/step_definitions.java:7\n" + "\n" + - " Scenario Outline: scenario outline name # path/test.feature:16\n" + - " scenario outline description\n" + - " Then third step # path/step_definitions.java:11\n")); + "Scenario Outline: name 2 # path/test.feature:8\n" + + " Given first step # path/step_definitions.java:3\n" + + " Then third step # path/step_definitions.java:11\n")); } @Test void should_print_tags() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "@feature_tag\n" + "Feature: feature name\n" + " @scenario_tag\n" + @@ -199,28 +135,19 @@ void should_print_tags() { String formatterOutput = runFeaturesWithFormatter(true); assertThat(formatterOutput, equalTo("" + - "@feature_tag\n" + - "Feature: feature name\n" + "\n" + - " @feature_tag @scenario_tag\n" + - " Scenario: scenario name # path/test.feature:4\n" + - " Then second step # path/step_definitions.java:7\n" + - "\n" + - " @scenario_outline_tag\n" + - " Scenario Outline: scenario outline name # path/test.feature:7\n" + - " Then step\n" + - "\n" + - " @examples_tag\n" + - " Examples: examples name\n" + + "@feature_tag @scenario_tag\n" + + "Scenario: scenario name # path/test.feature:4\n" + + " Then second step # path/step_definitions.java:7\n" + "\n" + - " @feature_tag @scenario_outline_tag @examples_tag\n" + - " Scenario Outline: scenario outline name # path/test.feature:12\n" + - " Then third step # path/step_definitions.java:11\n")); + "@feature_tag @scenario_outline_tag @examples_tag\n" + + "Scenario Outline: scenario outline name # path/test.feature:12\n" + + " Then third step # path/step_definitions.java:11\n")); } @Test void should_print_error_message_for_failed_steps() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -231,13 +158,13 @@ void should_print_error_message_for_failed_steps() { String formatterOutput = runFeaturesWithFormatter(true); assertThat(formatterOutput, containsString("" + - " Given first step # path/step_definitions.java:3\n" + + " Given first step # path/step_definitions.java:3\n" + " the stack trace\n")); } @Test void should_print_error_message_for_before_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -249,14 +176,14 @@ void should_print_error_message_for_before_hooks() { String formatterOutput = runFeaturesWithFormatter(true); assertThat(formatterOutput, containsString("" + - " Scenario: scenario name # path/test.feature:2\n" + + "Scenario: scenario name # path/test.feature:2\n" + " the stack trace\n" + - " Given first step # path/step_definitions.java:3\n")); + " Given first step # path/step_definitions.java:3\n")); } @Test void should_print_error_message_for_after_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -268,13 +195,13 @@ void should_print_error_message_for_after_hooks() { String formatterOutput = runFeaturesWithFormatter(true); assertThat(formatterOutput, containsString("" + - " Given first step # path/step_definitions.java:3\n" + + " Given first step # path/step_definitions.java:3\n" + " the stack trace\n")); } @Test void should_print_output_from_before_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -287,16 +214,16 @@ void should_print_output_from_before_hooks() { String formatterOutput = runFeaturesWithFormatter(true); assertThat(formatterOutput, containsString("" + - " Scenario: scenario name # path/test.feature:2\n" + + "Scenario: scenario name # path/test.feature:2\n" + "\n" + - " printed from hook\n" + + " printed from hook\n" + "\n" + - " Given first step # path/step_definitions.java:3\n")); + " Given first step # path/step_definitions.java:3\n")); } @Test void should_print_output_from_after_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -309,14 +236,14 @@ void should_print_output_from_after_hooks() { String formatterOutput = runFeaturesWithFormatter(true); assertThat(formatterOutput, containsString("" + - " Given first step # path/step_definitions.java:3\n" + + " Given first step # path/step_definitions.java:3\n" + "\n" + - " printed from hook\n")); + " printed from hook\n")); } @Test void should_print_output_from_afterStep_hooks() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -332,19 +259,19 @@ void should_print_output_from_afterStep_hooks() { String formatterOutput = runFeaturesWithFormatter(true); assertThat(formatterOutput, containsString("" + - " Given first step # path/step_definitions.java:3\n" + + " Given first step # path/step_definitions.java:3\n" + "\n" + - " printed from afterstep hook\n" + + " printed from afterstep hook\n" + "\n" + - " When second step # path/step_definitions.java:4\n" + + " When second step # path/step_definitions.java:4\n" + "\n" + - " printed from afterstep hook" + + " printed from afterstep hook" + "\n")); } @Test void should_color_code_steps_according_to_the_result() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -355,12 +282,12 @@ void should_color_code_steps_according_to_the_result() { String formatterOutput = runFeaturesWithFormatter(false); assertThat(formatterOutput, containsString("" + - " " + AnsiEscapes.GREEN + "Given " + AnsiEscapes.RESET + AnsiEscapes.GREEN + "first step" + AnsiEscapes.RESET)); + " " + AnsiEscapes.GREEN + "Given " + AnsiEscapes.RESET + AnsiEscapes.GREEN + "first step" + AnsiEscapes.RESET)); } @Test void should_color_code_locations_as_comments() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); @@ -376,7 +303,7 @@ void should_color_code_locations_as_comments() { @Test void should_color_code_error_message_according_to_the_result() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n"); diff --git a/core/src/test/java/io/cucumber/core/plugin/RerunFormatterTest.java b/core/src/test/java/io/cucumber/core/plugin/RerunFormatterTest.java index 8009c5c69b..3aab99850f 100755 --- a/core/src/test/java/io/cucumber/core/plugin/RerunFormatterTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/RerunFormatterTest.java @@ -1,7 +1,7 @@ package io.cucumber.core.plugin; -import io.cucumber.core.feature.CucumberFeature; import io.cucumber.core.feature.TestFeatureParser; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.runner.TestHelper; import io.cucumber.plugin.event.Result; import org.junit.jupiter.api.Test; @@ -19,13 +19,13 @@ class RerunFormatterTest { - private final List features = new ArrayList<>(); + private final List features = new ArrayList<>(); private final Map stepsToResult = new HashMap<>(); private final List> hooks = new ArrayList<>(); @Test void should_leave_report_empty_when_exit_code_is_zero() { - CucumberFeature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + + Feature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + "Feature: feature name\n" + " Scenario: passed scenario\n" + " Given passed step\n" + @@ -45,7 +45,7 @@ void should_leave_report_empty_when_exit_code_is_zero() { @Test void should_put_data_in_report_when_exit_code_is_non_zero() { - CucumberFeature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + + Feature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + "Feature: feature name\n" + " Scenario: failed scenario\n" + " Given failed step\n" + @@ -65,7 +65,7 @@ void should_put_data_in_report_when_exit_code_is_non_zero() { @Test void should_use_scenario_location_when_scenario_step_fails() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -83,7 +83,7 @@ void should_use_scenario_location_when_scenario_step_fails() { @Test void should_use_scenario_location_when_background_step_fails() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Background: the background\n" + " Given background step\n" + @@ -102,7 +102,7 @@ void should_use_scenario_location_when_background_step_fails() { @Test void should_use_example_row_location_when_scenario_outline_fails() { - CucumberFeature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + + Feature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + "Feature: feature name\n" + " Scenario Outline: scenario name\n" + " When executing row\n" + @@ -123,7 +123,7 @@ void should_use_example_row_location_when_scenario_outline_fails() { @Test void should_use_scenario_location_when_before_hook_fails() { - CucumberFeature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + + Feature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -142,7 +142,7 @@ void should_use_scenario_location_when_before_hook_fails() { @Test void should_use_scenario_location_when_after_hook_fails() { - CucumberFeature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + + Feature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -161,7 +161,7 @@ void should_use_scenario_location_when_after_hook_fails() { @Test void should_one_entry_for_feature_with_many_failing_scenarios() { - CucumberFeature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + + Feature feature = TestFeatureParser.parse("classpath:path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario 1 name\n" + " When first step\n" + @@ -182,12 +182,12 @@ void should_one_entry_for_feature_with_many_failing_scenarios() { @Test void should_one_entry_for_each_failing_feature() { - CucumberFeature feature1 = TestFeatureParser.parse("classpath:path/first.feature", "" + + Feature feature1 = TestFeatureParser.parse("classpath:path/first.feature", "" + "Feature: feature 1 name\n" + " Scenario: scenario 1 name\n" + " When first step\n" + " Then second step\n"); - CucumberFeature feature2 = TestFeatureParser.parse("classpath:path/second.feature", "" + + Feature feature2 = TestFeatureParser.parse("classpath:path/second.feature", "" + "Feature: feature 2 name\n" + " Scenario: scenario 2 name\n" + " When third step\n" + diff --git a/core/src/test/java/io/cucumber/core/plugin/TestNGFormatterTest.java b/core/src/test/java/io/cucumber/core/plugin/TestNGFormatterTest.java index 4cc8c95301..27dfc1905a 100644 --- a/core/src/test/java/io/cucumber/core/plugin/TestNGFormatterTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/TestNGFormatterTest.java @@ -1,7 +1,7 @@ package io.cucumber.core.plugin; import io.cucumber.plugin.event.Result; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.runner.TestHelper; import org.junit.jupiter.api.Test; @@ -27,7 +27,7 @@ final class TestNGFormatterTest { - private final List features = new ArrayList<>(); + private final List features = new ArrayList<>(); private final Map stepsToResult = new HashMap<>(); private final Map stepsToLocation = new HashMap<>(); private final List> hooks = new ArrayList<>(); @@ -37,7 +37,7 @@ final class TestNGFormatterTest { @Test void testScenarioWithUndefinedSteps() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature\n" + " Scenario: scenario\n" + " When step\n" + @@ -61,7 +61,7 @@ void testScenarioWithUndefinedSteps() throws Throwable { @Test void testScenarioWithUndefinedStepsStrict() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature\n" + " Scenario: scenario\n" + " When step\n" + @@ -92,7 +92,7 @@ void testScenarioWithUndefinedStepsStrict() throws Throwable { @Test void testScenarioWithPendingSteps() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature\n" + " Scenario: scenario\n" + " When step1\n" + @@ -117,7 +117,7 @@ void testScenarioWithPendingSteps() throws Throwable { @Test void testScenarioWithFailedSteps() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature\n" + " Scenario: scenario\n" + " When step1\n" + @@ -149,7 +149,7 @@ void testScenarioWithFailedSteps() throws Throwable { @Test void testScenarioWithPassedSteps() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature\n" + " Scenario: scenario\n" + " When step\n" + @@ -173,7 +173,7 @@ void testScenarioWithPassedSteps() throws Throwable { @Test void testScenarioWithBackground() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature\n" + " Background:\n" + " When background\n" + @@ -201,7 +201,7 @@ void testScenarioWithBackground() throws Throwable { @Test void testScenarioOutlineWithExamples() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature\n" + " Scenario Outline: scenario\n" + " When step\n" + @@ -230,7 +230,7 @@ void testScenarioOutlineWithExamples() throws Throwable { @Test void testDurationCalculationOfStepsAndHooks() throws Throwable { - CucumberFeature feature1 = TestFeatureParser.parse("path/feature1.feature", "" + + Feature feature1 = TestFeatureParser.parse("path/feature1.feature", "" + "Feature: feature_1\n" + " Scenario: scenario_1\n" + " When step\n" + @@ -238,7 +238,7 @@ void testDurationCalculationOfStepsAndHooks() throws Throwable { " Scenario: scenario_2\n" + " When step\n" + " Then step\n"); - CucumberFeature feature2 = TestFeatureParser.parse("path/feature2.feature", "" + + Feature feature2 = TestFeatureParser.parse("path/feature2.feature", "" + "Feature: feature_2\n" + " Scenario: scenario_3\n" + " When step\n" + @@ -269,7 +269,7 @@ void testDurationCalculationOfStepsAndHooks() throws Throwable { @Test void testScenarioWithFailedBeforeHook() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature\n" + " Scenario: scenario\n" + " When step\n" + @@ -301,7 +301,7 @@ void testScenarioWithFailedBeforeHook() throws Throwable { @Test void testScenarioWithFailedAfterHook() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature\n" + " Scenario: scenario\n" + " When step\n" + diff --git a/core/src/test/java/io/cucumber/core/plugin/TimelineFormatterTest.java b/core/src/test/java/io/cucumber/core/plugin/TimelineFormatterTest.java index 7f0dce8ee0..1c14302cb4 100644 --- a/core/src/test/java/io/cucumber/core/plugin/TimelineFormatterTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/TimelineFormatterTest.java @@ -4,7 +4,7 @@ import gherkin.deps.com.google.gson.GsonBuilder; import gherkin.deps.com.google.gson.JsonDeserializer; import io.cucumber.plugin.event.Result; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.runner.TestHelper; import org.junit.jupiter.api.BeforeEach; @@ -33,7 +33,7 @@ class TimelineFormatterTest { - private static final Comparator TEST_DATA_COMPARATOR = Comparator.comparing(o -> o.id); + private static final Comparator TEST_DATA_COMPARATOR = Comparator.comparing(o -> o.scenario); private static final String REPORT_TEMPLATE_RESOURCE_DIR = "src/main/resources/io/cucumber/core/plugin/timeline"; private static final String REPORT_JS = "report.js"; @@ -49,7 +49,7 @@ class TimelineFormatterTest { private final Map stepsToResult = new HashMap<>(); private final Map stepsToLocation = new HashMap<>(); - private final CucumberFeature failingFeature = TestFeatureParser.parse("some/path/failing.feature", "" + + private final Feature failingFeature = TestFeatureParser.parse("some/path/failing.feature", "" + "Feature: Failing Feature\n" + " Background:\n" + " Given bg_1\n" + @@ -65,25 +65,25 @@ class TimelineFormatterTest { " When step_02\n" + " Then step_03"); - private final CucumberFeature successfulFeature = TestFeatureParser.parse("some/path/successful.feature", "" + + private final Feature successfulFeature = TestFeatureParser.parse("some/path/successful.feature", "" + "Feature: Successful Feature\n" + " Background:\n" + " Given bg_1\n" + " When bg_2\n" + " Then bg_3\n" + " @TagB @TagC\n" + - " Scenario: Scenario 1\n" + + " Scenario: Scenario 3\n" + " Given step_10\n" + " When step_20\n" + " Then step_30"); - private final CucumberFeature pendingFeature = TestFeatureParser.parse("some/path/pending.feature", "" + + private final Feature pendingFeature = TestFeatureParser.parse("some/path/pending.feature", "" + "Feature: Pending Feature\n" + " Background:\n" + " Given bg_1\n" + " When bg_2\n" + " Then bg_3\n" + - " Scenario: Scenario 1\n" + + " Scenario: Scenario 4\n" + " Given step_10\n" + " When step_20\n" + " Then step_50"); @@ -194,8 +194,8 @@ void shouldWriteItemsAndGroupsCorrectlyToReportJs() throws Throwable { private TimelineFormatter.TestData[] getExpectedTestData(Long groupId) { String expectedJson = ("[\n" + " {\n" + - " \"id\": \"failing-feature;scenario-1\",\n" + " \"feature\": \"Failing Feature\",\n" + + " \"scenario\": \"Scenario 1\",\n" + " \"start\": 0,\n" + " \"end\": 6000,\n" + " \"group\": groupId,\n" + @@ -204,8 +204,8 @@ private TimelineFormatter.TestData[] getExpectedTestData(Long groupId) { " \"className\": \"failed\"\n" + " },\n" + " {\n" + - " \"id\": \"failing-feature;scenario-2\",\n" + " \"feature\": \"Failing Feature\",\n" + + " \"scenario\": \"Scenario 2\",\n" + " \"start\": 6000,\n" + " \"end\": 12000,\n" + " \"group\": groupId,\n" + @@ -214,24 +214,24 @@ private TimelineFormatter.TestData[] getExpectedTestData(Long groupId) { " \"className\": \"failed\"\n" + " },\n" + " {\n" + - " \"id\": \"pending-feature;scenario-1\",\n" + - " \"feature\": \"Pending Feature\",\n" + - " \"start\": 12000,\n" + - " \"end\": 18000,\n" + - " \"group\": groupId,\n" + - " \"content\": \"\",\n" + - " \"tags\": \"\",\n" + - " \"className\": \"undefined\"\n" + - " },\n" + - " {\n" + - " \"id\": \"successful-feature;scenario-1\",\n" + " \"feature\": \"Successful Feature\",\n" + + " \"scenario\": \"Scenario 3\",\n" + " \"start\": 18000,\n" + " \"end\": 24000,\n" + " \"group\": groupId,\n" + " \"content\": \"\",\n" + " \"tags\": \"@tagb,@tagc,\",\n" + " \"className\": \"passed\"\n" + + " },\n" + + " {\n" + + " \"scenario\": \"Scenario 4\",\n" + + " \"feature\": \"Pending Feature\",\n" + + " \"start\": 12000,\n" + + " \"end\": 18000,\n" + + " \"group\": groupId,\n" + + " \"content\": \"\",\n" + + " \"tags\": \"\",\n" + + " \"className\": \"undefined\"\n" + " }\n" + "]").replaceAll("groupId", groupId.toString()); @@ -290,7 +290,6 @@ private void assertTimelineTestDataIsAsExpected(final TimelineFormatter.TestData final int idx = i; assertAll("Checking TimelineFormatter.TestData", - () -> assertThat(String.format("id on item %s, was not as expected", idx), actual.id, is(equalTo(expected.id))), () -> assertThat(String.format("feature on item %s, was not as expected", idx), actual.feature, is(equalTo(expected.feature))), () -> assertThat(String.format("className on item %s, was not as expected", idx), actual.className, is(equalTo(expected.className))), () -> assertThat(String.format("content on item %s, was not as expected", idx), actual.content, is(equalTo(expected.content))), diff --git a/core/src/test/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinterTest.java b/core/src/test/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinterTest.java index 13416d88f7..2d4dfbb031 100644 --- a/core/src/test/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinterTest.java +++ b/core/src/test/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinterTest.java @@ -2,6 +2,7 @@ import java.time.Clock; import java.time.Duration; +import java.util.UUID; import io.cucumber.core.runtime.TimeServiceEventBus; import io.cucumber.plugin.event.Result; @@ -26,7 +27,7 @@ class UnusedStepsSummaryPrinterTest { void verifyUnusedStepsPrinted() { StringBuilder out = new StringBuilder(); UnusedStepsSummaryPrinter summaryPrinter = new UnusedStepsSummaryPrinter(out); - TimeServiceEventBus bus = new TimeServiceEventBus(Clock.systemUTC()); + TimeServiceEventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); summaryPrinter.setEventPublisher(bus); // Register two steps, use one, then finish the test run diff --git a/core/src/test/java/io/cucumber/core/runner/AmbiguousStepDefinitionMatchTest.java b/core/src/test/java/io/cucumber/core/runner/AmbiguousStepDefinitionMatchTest.java index 52f353d8a4..669e6d6aca 100644 --- a/core/src/test/java/io/cucumber/core/runner/AmbiguousStepDefinitionMatchTest.java +++ b/core/src/test/java/io/cucumber/core/runner/AmbiguousStepDefinitionMatchTest.java @@ -1,7 +1,7 @@ package io.cucumber.core.runner; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.feature.TestFeatureParser; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; @@ -17,12 +17,12 @@ class AmbiguousStepDefinitionMatchTest { - private final CucumberFeature feature = TestFeatureParser.parse("file:test.feature", "" + + private final Feature feature = TestFeatureParser.parse("file:test.feature", "" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" ); - private final CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + private final Step step = feature.getPickles().get(0).getSteps().get(0); private final AmbiguousStepDefinitionsException e = new AmbiguousStepDefinitionsException(step, emptyList()); private final AmbiguousPickleStepDefinitionsMatch match = new AmbiguousPickleStepDefinitionsMatch(URI.create("file:path/to.feature"), step, e); diff --git a/core/src/test/java/io/cucumber/core/runner/AmbiguousStepDefinitionsExceptionTest.java b/core/src/test/java/io/cucumber/core/runner/AmbiguousStepDefinitionsExceptionTest.java index a6d6267712..d6487d09fa 100644 --- a/core/src/test/java/io/cucumber/core/runner/AmbiguousStepDefinitionsExceptionTest.java +++ b/core/src/test/java/io/cucumber/core/runner/AmbiguousStepDefinitionsExceptionTest.java @@ -1,7 +1,7 @@ package io.cucumber.core.runner; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.feature.TestFeatureParser; import org.junit.jupiter.api.Test; @@ -20,13 +20,13 @@ class AmbiguousStepDefinitionsExceptionTest { @Test void can_report_ambiguous_step_definitions() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" ); - CucumberStep mockPickleStep = feature.getPickles().get(0).getSteps().get(0); + Step mockPickleStep = feature.getPickles().get(0).getSteps().get(0); PickleStepDefinitionMatch mockPickleStepDefinitionMatchOne = mock(PickleStepDefinitionMatch.class); when(mockPickleStepDefinitionMatchOne.getPattern()).thenReturn("PickleStepDefinitionMatchOne_Pattern"); diff --git a/core/src/test/java/io/cucumber/core/runner/CachingGlueTest.java b/core/src/test/java/io/cucumber/core/runner/CachingGlueTest.java index 6f2f7def75..f49211dae3 100644 --- a/core/src/test/java/io/cucumber/core/runner/CachingGlueTest.java +++ b/core/src/test/java/io/cucumber/core/runner/CachingGlueTest.java @@ -11,8 +11,8 @@ import io.cucumber.core.backend.ScenarioScoped; import io.cucumber.core.backend.StepDefinition; import io.cucumber.core.backend.TestCaseState; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.runtime.TimeServiceEventBus; import io.cucumber.core.stepexpression.StepTypeRegistry; @@ -28,6 +28,7 @@ import java.net.URI; import java.time.Clock; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; import static java.util.Locale.ENGLISH; @@ -45,10 +46,10 @@ class CachingGlueTest { private final StepTypeRegistry stepTypeRegistry = new StepTypeRegistry(ENGLISH); - private CachingGlue glue = new CachingGlue(new TimeServiceEventBus(Clock.systemUTC())); + private CachingGlue glue = new CachingGlue(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); - private static CucumberStep getPickleStep(String text) { - CucumberFeature feature = TestFeatureParser.parse("" + + private static Step getPickleStep(String text) { + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given " + text + "\n" @@ -57,8 +58,8 @@ private static CucumberStep getPickleStep(String text) { return feature.getPickles().get(0).getSteps().get(0); } - private static CucumberStep getPickleStepWithSingleCellTable(String stepText, String cell) { - CucumberFeature feature = TestFeatureParser.parse("" + + private static Step getPickleStepWithSingleCellTable(String stepText, String cell) { + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given " + stepText + "\n" + @@ -68,8 +69,8 @@ private static CucumberStep getPickleStepWithSingleCellTable(String stepText, St return feature.getPickles().get(0).getSteps().get(0); } - private static CucumberStep getPickleStepWithDocString(String stepText, String doc) { - CucumberFeature feature = TestFeatureParser.parse("" + + private static Step getPickleStepWithDocString(String stepText, String doc) { + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given " + stepText + "\n" + @@ -205,7 +206,7 @@ void returns_null_if_no_matching_steps_found() throws AmbiguousStepDefinitionsEx glue.addStepDefinition(stepDefinition); URI uri = URI.create("file:path/to.feature"); - CucumberStep pickleStep = getPickleStep("pattern"); + Step pickleStep = getPickleStep("pattern"); assertThat(glue.stepDefinitionMatch(uri, pickleStep), is(nullValue())); } @@ -220,7 +221,7 @@ void returns_match_from_cache_if_single_found() throws AmbiguousStepDefinitionsE URI uri = URI.create("file:path/to.feature"); String stepText = "pattern1"; - CucumberStep pickleStep1 = getPickleStep(stepText); + Step pickleStep1 = getPickleStep(stepText); PickleStepDefinitionMatch pickleStepDefinitionMatch = glue.stepDefinitionMatch(uri, pickleStep1); assertThat(pickleStepDefinitionMatch.getStepDefinition(), is(equalTo(stepDefinition1))); @@ -231,7 +232,7 @@ void returns_match_from_cache_if_single_found() throws AmbiguousStepDefinitionsE CoreStepDefinition coreStepDefinition = glue.getStepDefinitionsByPattern().get(stepDefinition1.getPattern()); assertThat(coreStepDefinition.getStepDefinition(), is(equalTo(stepDefinition1))); - CucumberStep pickleStep2 = getPickleStep(stepText); + Step pickleStep2 = getPickleStep(stepText); PickleStepDefinitionMatch pickleStepDefinitionMatch2 = glue.stepDefinitionMatch(uri, pickleStep2); assertThat(pickleStepDefinitionMatch2.getStepDefinition(), is(equalTo(stepDefinition1))); } @@ -247,7 +248,7 @@ void returns_match_from_cache_for_step_with_table() throws AmbiguousStepDefiniti URI uri = URI.create("file:path/to.feature"); String stepText = "pattern1"; - CucumberStep pickleStep1 = getPickleStepWithSingleCellTable(stepText, "cell 1"); + Step pickleStep1 = getPickleStepWithSingleCellTable(stepText, "cell 1"); PickleStepDefinitionMatch match1 = glue.stepDefinitionMatch(uri, pickleStep1); assertThat(match1.getStepDefinition(), is(equalTo(stepDefinition1))); @@ -260,7 +261,7 @@ void returns_match_from_cache_for_step_with_table() throws AmbiguousStepDefiniti assertThat(((DataTable) match1.getArguments().get(0).getValue()).cell(0, 0), is(equalTo("cell 1"))); //check second match - CucumberStep pickleStep2 = getPickleStepWithSingleCellTable(stepText, "cell 2"); + Step pickleStep2 = getPickleStepWithSingleCellTable(stepText, "cell 2"); PickleStepDefinitionMatch match2 = glue.stepDefinitionMatch(uri, pickleStep2); //check arguments @@ -278,7 +279,7 @@ void returns_match_from_cache_for_ste_with_doc_string() throws AmbiguousStepDefi URI uri = URI.create("file:path/to.feature"); String stepText = "pattern1"; - CucumberStep pickleStep1 = getPickleStepWithDocString(stepText, "doc string 1"); + Step pickleStep1 = getPickleStepWithDocString(stepText, "doc string 1"); PickleStepDefinitionMatch match1 = glue.stepDefinitionMatch(uri, pickleStep1); assertThat(match1.getStepDefinition(), is(equalTo(stepDefinition1))); @@ -292,7 +293,7 @@ void returns_match_from_cache_for_ste_with_doc_string() throws AmbiguousStepDefi assertThat(match1.getArguments().get(0).getValue(), is(equalTo("doc string 1"))); //check second match - CucumberStep pickleStep2 = getPickleStepWithDocString(stepText, "doc string 2"); + Step pickleStep2 = getPickleStepWithDocString(stepText, "doc string 2"); PickleStepDefinitionMatch match2 = glue.stepDefinitionMatch(uri, pickleStep2); //check arguments assertThat(match2.getArguments().get(0).getValue(), is(equalTo("doc string 2"))); @@ -302,7 +303,7 @@ void returns_match_from_cache_for_ste_with_doc_string() throws AmbiguousStepDefi void returns_fresh_match_from_cache_after_evicting_scenario_scoped() throws AmbiguousStepDefinitionsException { URI uri = URI.create("file:path/to.feature"); String stepText = "pattern1"; - CucumberStep pickleStep1 = getPickleStep(stepText); + Step pickleStep1 = getPickleStep(stepText); StepDefinition stepDefinition1 = new MockedScenarioScopedStepDefinition("^pattern1"); @@ -327,7 +328,7 @@ void returns_fresh_match_from_cache_after_evicting_scenario_scoped() throws Ambi void returns_no_match_after_evicting_scenario_scoped() throws AmbiguousStepDefinitionsException { URI uri = URI.create("file:path/to.feature"); String stepText = "pattern1"; - CucumberStep pickleStep1 = getPickleStep(stepText); + Step pickleStep1 = getPickleStep(stepText); StepDefinition stepDefinition1 = new MockedScenarioScopedStepDefinition("^pattern1"); diff --git a/core/src/test/java/io/cucumber/core/runner/CoreStepDefinitionTest.java b/core/src/test/java/io/cucumber/core/runner/CoreStepDefinitionTest.java index 61dd3315e3..5902c57979 100644 --- a/core/src/test/java/io/cucumber/core/runner/CoreStepDefinitionTest.java +++ b/core/src/test/java/io/cucumber/core/runner/CoreStepDefinitionTest.java @@ -1,7 +1,7 @@ package io.cucumber.core.runner; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.stepexpression.Argument; import io.cucumber.core.stepexpression.StepTypeRegistry; @@ -30,7 +30,7 @@ class CoreStepDefinitionTest { @Test void should_apply_identity_transform_to_doc_string_when_target_type_is_object() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have some step\n" + @@ -40,7 +40,7 @@ void should_apply_identity_transform_to_doc_string_when_target_type_is_object() ); StubStepDefinition stub = new StubStepDefinition("I have some step", Object.class); CoreStepDefinition stepDefinition = new CoreStepDefinition(stub, stepTypeRegistry); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); List arguments = stepDefinition.matchedArguments(step); assertThat(arguments.get(0).getValue(), is(equalTo(DocString.create("content")))); } @@ -48,7 +48,7 @@ void should_apply_identity_transform_to_doc_string_when_target_type_is_object() @Test void should_apply_identity_transform_to_data_table_when_target_type_is_object() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have some step\n" + @@ -62,7 +62,7 @@ void should_apply_identity_transform_to_data_table_when_target_type_is_object() @Test void should_convert_empty_pickle_table_cells_to_null_values() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have some step\n" + @@ -93,7 +93,7 @@ public void transposedMapOfDoubleToListOfDouble(Map> mapOfD @Test void transforms_to_map_of_double_to_double() throws Throwable { Method m = Steps.class.getMethod("mapOfDoubleToDouble", Map.class); - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given some text\n" + @@ -113,7 +113,7 @@ void transforms_to_map_of_double_to_double() throws Throwable { @Test void transforms_transposed_to_map_of_double_to_double() throws Throwable { Method m = Steps.class.getMethod("transposedMapOfDoubleToListOfDouble", Map.class); - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given some text\n" + @@ -128,7 +128,7 @@ void transforms_transposed_to_map_of_double_to_double() throws Throwable { @Test void transforms_to_list_of_single_values() throws Throwable { Method m = Steps.class.getMethod("listOfListOfDoubles", List.class); - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given some text\n" + @@ -143,7 +143,7 @@ void transforms_to_list_of_single_values() throws Throwable { @Test void transforms_to_list_of_single_values_transposed() throws Throwable { Method m = Steps.class.getMethod("listOfListOfDoubles", List.class); - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given some text\n" + @@ -157,7 +157,7 @@ void transforms_to_list_of_single_values_transposed() throws Throwable { @Test void passes_plain_data_table() throws Throwable { Method m = Steps.class.getMethod("plainDataTable", DataTable.class); - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given some text\n" + @@ -175,7 +175,7 @@ void passes_plain_data_table() throws Throwable { @Test void passes_transposed_data_table() throws Throwable { Method m = Steps.class.getMethod("plainDataTable", DataTable.class); - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given some text\n" + @@ -191,10 +191,10 @@ void passes_transposed_data_table() throws Throwable { } @SuppressWarnings("unchecked") - private T runStepDef(Method method, boolean transposed, CucumberFeature feature) throws Throwable { + private T runStepDef(Method method, boolean transposed, Feature feature) { StubStepDefinition stub = new StubStepDefinition("some text", transposed, method.getGenericParameterTypes()); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(stub, stepTypeRegistry); - CucumberStep stepWithTable = feature.getPickles().get(0).getSteps().get(0); + Step stepWithTable = feature.getPickles().get(0).getSteps().get(0); List arguments = coreStepDefinition.matchedArguments(stepWithTable); List result = new ArrayList<>(); diff --git a/core/src/test/java/io/cucumber/core/runner/EventBusTest.java b/core/src/test/java/io/cucumber/core/runner/EventBusTest.java index 0d84aa427b..c87ab10edc 100644 --- a/core/src/test/java/io/cucumber/core/runner/EventBusTest.java +++ b/core/src/test/java/io/cucumber/core/runner/EventBusTest.java @@ -16,6 +16,7 @@ import java.time.Clock; import java.time.Instant; import java.time.ZoneId; +import java.util.UUID; import static java.time.Duration.ZERO; import static java.time.Instant.EPOCH; @@ -34,7 +35,7 @@ void handlers_receive_the_events_they_registered_for() { TestCase testCase = mock(TestCase.class); TestStepFinished event = new TestStepFinished(EPOCH, testCase, testStep, result); - EventBus bus = new TimeServiceEventBus(Clock.fixed(Instant.EPOCH, ZoneId.of("UTC"))); + EventBus bus = new TimeServiceEventBus(Clock.fixed(Instant.EPOCH, ZoneId.of("UTC")), UUID::randomUUID); bus.registerHandlerFor(TestStepFinished.class, handler); bus.send(event); @@ -48,7 +49,7 @@ void handlers_do_not_receive_the_events_they_did_not_registered_for() { TestCase testCase = mock(TestCase.class); TestStepStarted event = new TestStepStarted(EPOCH, testCase, testStep); - EventBus bus = new TimeServiceEventBus(Clock.fixed(Instant.EPOCH, ZoneId.of("UTC"))); + EventBus bus = new TimeServiceEventBus(Clock.fixed(Instant.EPOCH, ZoneId.of("UTC")), UUID::randomUUID); bus.registerHandlerFor(TestStepFinished.class, handler); bus.send(event); diff --git a/core/src/test/java/io/cucumber/core/runner/HookOrderTest.java b/core/src/test/java/io/cucumber/core/runner/HookOrderTest.java index 675fc3687a..aae5edddfe 100644 --- a/core/src/test/java/io/cucumber/core/runner/HookOrderTest.java +++ b/core/src/test/java/io/cucumber/core/runner/HookOrderTest.java @@ -3,8 +3,8 @@ import io.cucumber.core.backend.Glue; import io.cucumber.core.backend.HookDefinition; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.options.RuntimeOptions; import io.cucumber.core.runtime.StubStepDefinition; @@ -17,6 +17,7 @@ import java.time.Clock; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -25,18 +26,18 @@ class HookOrderTest { private final RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); - private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC()); + private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); private final StubStepDefinition stepDefinition = new StubStepDefinition("I have 4 cukes in my belly"); - private final CucumberFeature feature = TestFeatureParser.parse("" + + private final Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" ); - private final CucumberPickle pickle = feature.getPickles().get(0); + private final Pickle pickle = feature.getPickles().get(0); @Test - void before_hooks_execute_in_order() throws Throwable { + void before_hooks_execute_in_order() { final List hooks = mockHooks(3, Integer.MAX_VALUE, 1, -1, 0, 10000, Integer.MIN_VALUE); TestRunnerSupplier runnerSupplier = new TestRunnerSupplier(bus, runtimeOptions) { @@ -63,7 +64,7 @@ public void loadGlue(Glue glue, List gluePaths) { } @Test - void before_step_hooks_execute_in_order() throws Throwable { + void before_step_hooks_execute_in_order() { final List hooks = mockHooks(3, Integer.MAX_VALUE, 1, -1, 0, 10000, Integer.MIN_VALUE); TestRunnerSupplier runnerSupplier = new TestRunnerSupplier(bus, runtimeOptions) { @@ -90,7 +91,7 @@ public void loadGlue(Glue glue, List gluePaths) { } @Test - void after_hooks_execute_in_reverse_order() throws Throwable { + void after_hooks_execute_in_reverse_order() { final List hooks = mockHooks(Integer.MIN_VALUE, 2, Integer.MAX_VALUE, 4, -1, 0, 10000); TestRunnerSupplier runnerSupplier = new TestRunnerSupplier(bus, runtimeOptions) { @@ -117,7 +118,7 @@ public void loadGlue(Glue glue, List gluePaths) { } @Test - void after_step_hooks_execute_in_reverse_order() throws Throwable { + void after_step_hooks_execute_in_reverse_order() { final List hooks = mockHooks(Integer.MIN_VALUE, 2, Integer.MAX_VALUE, 4, -1, 0, 10000); TestRunnerSupplier runnerSupplier = new TestRunnerSupplier(bus, runtimeOptions) { @@ -144,7 +145,7 @@ public void loadGlue(Glue glue, List gluePaths) { } @Test - void hooks_order_across_many_backends() throws Throwable { + void hooks_order_across_many_backends() { final List backend1Hooks = mockHooks(3, Integer.MAX_VALUE, 1); final List backend2Hooks = mockHooks(2, Integer.MAX_VALUE, 4); diff --git a/core/src/test/java/io/cucumber/core/runner/HookTest.java b/core/src/test/java/io/cucumber/core/runner/HookTest.java index fffc3c7fd5..78bb2fcaa4 100644 --- a/core/src/test/java/io/cucumber/core/runner/HookTest.java +++ b/core/src/test/java/io/cucumber/core/runner/HookTest.java @@ -6,8 +6,8 @@ import io.cucumber.core.backend.HookDefinition; import io.cucumber.core.backend.ObjectFactory; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.options.RuntimeOptions; import io.cucumber.core.runtime.TimeServiceEventBus; @@ -18,6 +18,7 @@ import java.time.Clock; import java.util.Collections; +import java.util.UUID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; @@ -27,21 +28,21 @@ class HookTest { - private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC()); + private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); private final RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); - private final CucumberFeature feature = TestFeatureParser.parse("" + + private final Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" ); - private final CucumberPickle pickle = feature.getPickles().get(0); + private final Pickle pickle = feature.getPickles().get(0); /** * Test for #23. */ @Test - void after_hooks_execute_before_objects_are_disposed() throws Throwable { + void after_hooks_execute_before_objects_are_disposed() { Backend backend = mock(Backend.class); when(backend.getSnippet()).thenReturn(new TestSnippet()); ObjectFactory objectFactory = mock(ObjectFactory.class); diff --git a/core/src/test/java/io/cucumber/core/runner/HookTestStepTest.java b/core/src/test/java/io/cucumber/core/runner/HookTestStepTest.java index f562ab8804..3839fa541a 100644 --- a/core/src/test/java/io/cucumber/core/runner/HookTestStepTest.java +++ b/core/src/test/java/io/cucumber/core/runner/HookTestStepTest.java @@ -1,7 +1,7 @@ package io.cucumber.core.runner; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.plugin.event.HookType; import io.cucumber.plugin.event.TestStepFinished; @@ -13,6 +13,7 @@ import java.time.Instant; import java.util.Collections; +import java.util.UUID; import static io.cucumber.core.backend.Status.PASSED; import static io.cucumber.core.backend.Status.SKIPPED; @@ -28,7 +29,7 @@ class HookTestStepTest { - private final CucumberFeature feature = TestFeatureParser.parse("" + + private final Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" @@ -36,6 +37,7 @@ class HookTestStepTest { private final CoreHookDefinition hookDefintion = mock(CoreHookDefinition.class); private final HookDefinitionMatch definitionMatch = new HookDefinitionMatch(hookDefintion); private final TestCase testCase = new TestCase( + UUID.randomUUID(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), @@ -44,7 +46,8 @@ class HookTestStepTest { ); private final EventBus bus = mock(EventBus.class); private final TestCaseState state = new TestCaseState(bus, testCase); - private HookTestStep step = new HookTestStep(HookType.AFTER_STEP, definitionMatch); + private HookTestStep step = new HookTestStep(UUID.randomUUID(), HookType.AFTER_STEP, definitionMatch); + private final UUID testExecutionId = UUID.randomUUID(); @BeforeEach void init() { @@ -52,8 +55,8 @@ void init() { } @Test - void run_does_run() throws Throwable { - step.run(testCase, bus, state, false); + void run_does_run() { + step.run(testCase, bus, state, false, testExecutionId); InOrder order = inOrder(bus, hookDefintion); order.verify(bus).send(isA(TestStepStarted.class)); @@ -62,8 +65,8 @@ void run_does_run() throws Throwable { } @Test - void run_does_dry_run() throws Throwable { - step.run(testCase, bus, state, true); + void run_does_dry_run() { + step.run(testCase, bus, state, true, testExecutionId); InOrder order = inOrder(bus, hookDefintion); order.verify(bus).send(isA(TestStepStarted.class)); @@ -73,14 +76,14 @@ void run_does_dry_run() throws Throwable { @Test void result_is_passed_when_step_definition_does_not_throw_exception() { - boolean skipNextStep = step.run(testCase, bus, state, false); + boolean skipNextStep = step.run(testCase, bus, state, false, testExecutionId); assertFalse(skipNextStep); assertThat(state.getStatus(), is(equalTo(PASSED))); } @Test void result_is_skipped_when_skip_step_is_skip_all_skipable() { - boolean skipNextStep = step.run(testCase, bus, state, true); + boolean skipNextStep = step.run(testCase, bus, state, true, testExecutionId); assertTrue(skipNextStep); assertThat(state.getStatus(), is(equalTo(SKIPPED))); } diff --git a/core/src/test/java/io/cucumber/core/runner/PickleStepTestStepTest.java b/core/src/test/java/io/cucumber/core/runner/PickleStepTestStepTest.java index a8fdca96cb..7d407180c7 100644 --- a/core/src/test/java/io/cucumber/core/runner/PickleStepTestStepTest.java +++ b/core/src/test/java/io/cucumber/core/runner/PickleStepTestStepTest.java @@ -1,8 +1,8 @@ package io.cucumber.core.runner; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.plugin.event.Result; import io.cucumber.plugin.event.Status; @@ -21,6 +21,7 @@ import java.time.Instant; import java.util.Collections; import java.util.List; +import java.util.UUID; import static io.cucumber.core.backend.Status.FAILED; import static io.cucumber.core.backend.Status.PASSED; @@ -51,27 +52,29 @@ class PickleStepTestStepTest { - private final CucumberFeature feature = TestFeatureParser.parse("" + + private final Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" ); - private final CucumberPickle pickle = feature.getPickles().get(0); - private final TestCase testCase = new TestCase(Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), pickle, false); + private final Pickle pickle = feature.getPickles().get(0); + private final TestCase testCase = new TestCase(UUID.randomUUID(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), pickle, false); private final EventBus bus = mock(EventBus.class); private final TestCaseState state = new TestCaseState(bus, testCase); private final PickleStepDefinitionMatch definitionMatch = mock(PickleStepDefinitionMatch.class); private CoreHookDefinition afterHookDefinition = mock(CoreHookDefinition.class); - private final HookTestStep afterHook = new HookTestStep(AFTER_STEP, new HookDefinitionMatch(afterHookDefinition)); + private final HookTestStep afterHook = new HookTestStep(UUID.randomUUID(), AFTER_STEP, new HookDefinitionMatch(afterHookDefinition)); private CoreHookDefinition beforeHookDefinition = mock(CoreHookDefinition.class); - private final HookTestStep beforeHook = new HookTestStep(BEFORE_STEP, new HookDefinitionMatch(beforeHookDefinition)); + private final HookTestStep beforeHook = new HookTestStep(UUID.randomUUID(), BEFORE_STEP, new HookDefinitionMatch(beforeHookDefinition)); private final PickleStepTestStep step = new PickleStepTestStep( + UUID.randomUUID(), URI.create("file:path/to.feature"), pickle.getSteps().get(0), singletonList(beforeHook), singletonList(afterHook), definitionMatch ); + private final UUID testExecutionId = UUID.randomUUID(); private static ArgumentMatcher scenarioDoesNotHave(final Throwable type) { return argument -> !type.equals(argument.getError()); } @@ -83,7 +86,7 @@ void init() { @Test void run_wraps_run_step_in_test_step_started_and_finished_events() throws Throwable { - step.run(testCase, bus, state, false); + step.run(testCase, bus, state, false, testExecutionId); InOrder order = inOrder(bus, definitionMatch); order.verify(bus).send(isA(TestStepStarted.class)); @@ -93,7 +96,7 @@ void run_wraps_run_step_in_test_step_started_and_finished_events() throws Throwa @Test void run_does_dry_run_step_when_skip_steps_is_true() throws Throwable { - step.run(testCase, bus, state, true); + step.run(testCase, bus, state, true, testExecutionId); InOrder order = inOrder(bus, definitionMatch); order.verify(bus).send(isA(TestStepStarted.class)); @@ -103,14 +106,14 @@ void run_does_dry_run_step_when_skip_steps_is_true() throws Throwable { @Test void result_is_passed_when_step_definition_does_not_throw_exception() { - boolean skipNextStep = step.run(testCase, bus, state, false); + boolean skipNextStep = step.run(testCase, bus, state, false, testExecutionId); assertFalse(skipNextStep); assertThat(state.getStatus(), is(equalTo(PASSED))); } @Test void result_is_skipped_when_skip_step_is_not_run_all() { - boolean skipNextStep = step.run(testCase, bus, state, true); + boolean skipNextStep = step.run(testCase, bus, state, true, testExecutionId); assertTrue(skipNextStep); assertThat(state.getStatus(), is(equalTo(SKIPPED))); @@ -119,7 +122,7 @@ void result_is_skipped_when_skip_step_is_not_run_all() { @Test void result_is_skipped_when_before_step_hook_does_not_pass() { doThrow(TestAbortedException.class).when(beforeHookDefinition).execute(any(TestCaseState.class)); - boolean skipNextStep = step.run(testCase, bus, state, false); + boolean skipNextStep = step.run(testCase, bus, state, false, testExecutionId); assertTrue(skipNextStep); assertThat(state.getStatus(), is(equalTo(SKIPPED))); } @@ -127,7 +130,7 @@ void result_is_skipped_when_before_step_hook_does_not_pass() { @Test void step_execution_is_dry_run_when_before_step_hook_does_not_pass() throws Throwable { doThrow(TestAbortedException.class).when(beforeHookDefinition).execute(any(TestCaseState.class)); - step.run(testCase, bus, state, false); + step.run(testCase, bus, state, false, testExecutionId); verify(definitionMatch).dryRunStep(any(TestCaseState.class)); } @@ -136,7 +139,7 @@ void result_is_result_from_hook_when_before_step_hook_does_not_pass() { Exception exception = new RuntimeException(); doThrow(exception).when(beforeHookDefinition).execute(any(TestCaseState.class)); Result failure = new Result(Status.FAILED, ZERO, exception); - boolean skipNextStep = step.run(testCase, bus, state, false); + boolean skipNextStep = step.run(testCase, bus, state, false, testExecutionId); assertTrue(skipNextStep); assertThat(state.getStatus(), is(equalTo(FAILED))); @@ -151,7 +154,7 @@ void result_is_result_from_step_when_step_hook_does_not_pass() throws Throwable RuntimeException runtimeException = new RuntimeException(); Result failure = new Result(Status.FAILED, ZERO, runtimeException); doThrow(runtimeException).when(definitionMatch).runStep(any(TestCaseState.class)); - boolean skipNextStep = step.run(testCase, bus, state, false); + boolean skipNextStep = step.run(testCase, bus, state, false, testExecutionId); assertTrue(skipNextStep); assertThat(state.getStatus(), is(equalTo(FAILED))); @@ -166,7 +169,7 @@ void result_is_result_from_hook_when_after_step_hook_does_not_pass() { Exception exception = new RuntimeException(); Result failure = new Result(Status.FAILED, ZERO, exception); doThrow(exception).when(afterHookDefinition).execute(any(TestCaseState.class)); - boolean skipNextStep = step.run(testCase, bus, state, false); + boolean skipNextStep = step.run(testCase, bus, state, false, testExecutionId); assertTrue(skipNextStep); assertThat(state.getStatus(), is(equalTo(FAILED))); @@ -179,14 +182,14 @@ void result_is_result_from_hook_when_after_step_hook_does_not_pass() { @Test void after_step_hook_is_run_when_before_step_hook_does_not_pass() { doThrow(RuntimeException.class).when(beforeHookDefinition).execute(any(TestCaseState.class)); - step.run(testCase, bus, state, false); + step.run(testCase, bus, state, false, testExecutionId); verify(afterHookDefinition).execute(any(TestCaseState.class)); } @Test void after_step_hook_is_run_when_step_does_not_pass() throws Throwable { doThrow(Exception.class).when(definitionMatch).runStep(any(TestCaseState.class)); - step.run(testCase, bus, state, false); + step.run(testCase, bus, state, false, testExecutionId); verify(afterHookDefinition).execute(any(TestCaseState.class)); } @@ -195,7 +198,7 @@ void after_step_hook_scenario_contains_step_failure_when_step_does_not_pass() th Throwable expectedError = new TestAbortedException("oops"); doThrow(expectedError).when(definitionMatch).runStep(any(TestCaseState.class)); doThrow(new RuntimeException()).when(afterHookDefinition).execute(argThat(scenarioDoesNotHave(expectedError))); - step.run(testCase, bus, state, false); + step.run(testCase, bus, state, false, testExecutionId); assertThat(state.getError(), is(expectedError)); } @@ -204,7 +207,7 @@ void after_step_hook_scenario_contains_before_step_hook_failure_when_before_step Throwable expectedError = new TestAbortedException("oops"); doThrow(expectedError).when(beforeHookDefinition).execute(any(TestCaseState.class)); doThrow(new RuntimeException()).when(afterHookDefinition).execute(argThat(scenarioDoesNotHave(expectedError))); - step.run(testCase, bus, state, false); + step.run(testCase, bus, state, false, testExecutionId); assertThat(state.getError(), is(expectedError)); } @@ -212,7 +215,7 @@ void after_step_hook_scenario_contains_before_step_hook_failure_when_before_step void result_is_skipped_when_step_definition_throws_assumption_violated_exception() throws Throwable { doThrow(TestAbortedException.class).when(definitionMatch).runStep(any()); - boolean skipNextStep = step.run(testCase, bus, state, false); + boolean skipNextStep = step.run(testCase, bus, state, false, testExecutionId); assertTrue(skipNextStep); assertThat(state.getStatus(), is(equalTo(SKIPPED))); @@ -222,7 +225,7 @@ void result_is_skipped_when_step_definition_throws_assumption_violated_exception void result_is_failed_when_step_definition_throws_exception() throws Throwable { doThrow(RuntimeException.class).when(definitionMatch).runStep(any(TestCaseState.class)); - boolean skipNextStep = step.run(testCase, bus, state, false); + boolean skipNextStep = step.run(testCase, bus, state, false, testExecutionId); assertTrue(skipNextStep); assertThat(state.getStatus(), is(equalTo(FAILED))); @@ -232,7 +235,7 @@ void result_is_failed_when_step_definition_throws_exception() throws Throwable { void result_is_pending_when_step_definition_throws_pending_exception() throws Throwable { doThrow(TestPendingException.class).when(definitionMatch).runStep(any(TestCaseState.class)); - boolean skipNextStep = step.run(testCase, bus, state, false); + boolean skipNextStep = step.run(testCase, bus, state, false, testExecutionId); assertTrue(skipNextStep); assertThat(state.getStatus(), is(equalTo(PENDING))); @@ -240,19 +243,20 @@ void result_is_pending_when_step_definition_throws_pending_exception() throws Th @Test void step_execution_time_is_measured() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" ); TestStep step = new PickleStepTestStep( + UUID.randomUUID(), URI.create("file:path/to.feature"), feature.getPickles().get(0).getSteps().get(0), definitionMatch ); when(bus.getInstant()).thenReturn(ofEpochMilli(234L), ofEpochMilli(1234L)); - step.run(testCase, bus, state, false); + step.run(testCase, bus, state, false, testExecutionId); ArgumentCaptor captor = forClass(TestCaseEvent.class); verify(bus, times(2)).send(captor.capture()); 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 eed3cdee26..361d06e4ee 100644 --- a/core/src/test/java/io/cucumber/core/runner/RunnerTest.java +++ b/core/src/test/java/io/cucumber/core/runner/RunnerTest.java @@ -6,8 +6,8 @@ import io.cucumber.core.backend.HookDefinition; import io.cucumber.core.backend.ObjectFactory; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.options.RuntimeOptions; import io.cucumber.core.options.RuntimeOptionsBuilder; @@ -20,6 +20,7 @@ import java.net.URI; import java.time.Clock; import java.util.List; +import java.util.UUID; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -40,7 +41,7 @@ class RunnerTest { private final RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); - private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC()); + private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); private final TypeRegistryConfigurer typeRegistryConfigurer = typeRegistry -> { }; @@ -73,7 +74,7 @@ void hooks_execute_when_world_exist() { @Test void steps_are_skipped_after_failure() { StubStepDefinition stepDefinition = spy(new StubStepDefinition("some step")); - CucumberPickle pickleMatchingStepDefinitions = createPickleMatchingStepDefinitions(stepDefinition); + Pickle pickleMatchingStepDefinitions = createPickleMatchingStepDefinitions(stepDefinition); final HookDefinition failingBeforeHook = addBeforeHook(); doThrow(RuntimeException.class).when(failingBeforeHook).execute(ArgumentMatchers.any()); @@ -103,7 +104,7 @@ public void execute(Object[] args) { } }); - CucumberPickle pickleMatchingStepDefinitions = createPickleMatchingStepDefinitions(stepDefinition); + Pickle pickleMatchingStepDefinitions = createPickleMatchingStepDefinitions(stepDefinition); final HookDefinition afteStepHook = addAfterStepHook(); @@ -125,7 +126,7 @@ public void loadGlue(Glue glue, List gluePaths) { @Test void aftersteps_executed_for_passed_step() { StubStepDefinition stepDefinition = spy(new StubStepDefinition("some step")); - CucumberPickle pickle = createPickleMatchingStepDefinitions(stepDefinition); + Pickle pickle = createPickleMatchingStepDefinitions(stepDefinition); HookDefinition afteStepHook1 = addAfterStepHook(); HookDefinition afteStepHook2 = addAfterStepHook(); @@ -174,7 +175,7 @@ public void loadGlue(Glue glue, List gluePaths) { @Test void steps_are_executed() { StubStepDefinition stepDefinition = new StubStepDefinition("some step"); - CucumberPickle pickleMatchingStepDefinitions = createPickleMatchingStepDefinitions(stepDefinition); + Pickle pickleMatchingStepDefinitions = createPickleMatchingStepDefinitions(stepDefinition); TestRunnerSupplier runnerSupplier = new TestRunnerSupplier(bus, runtimeOptions) { @Override public void loadGlue(Glue glue, List gluePaths) { @@ -188,7 +189,7 @@ public void loadGlue(Glue glue, List gluePaths) { @Test void steps_are_not_executed_on_dry_run() { StubStepDefinition stepDefinition = new StubStepDefinition("some step"); - CucumberPickle pickle = createPickleMatchingStepDefinitions(stepDefinition); + Pickle pickle = createPickleMatchingStepDefinitions(stepDefinition); RuntimeOptions runtimeOptions = new RuntimeOptionsBuilder().setDryRun().build(); TestRunnerSupplier runnerSupplier = new TestRunnerSupplier(bus, runtimeOptions) { @Override @@ -276,17 +277,17 @@ private HookDefinition addHook() { return hook; } - private CucumberPickle createEmptyPickle() { - CucumberFeature feature = TestFeatureParser.parse("" + + private Pickle createEmptyPickle() { + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" ); return feature.getPickles().get(0); } - private CucumberPickle createPickleMatchingStepDefinitions(StubStepDefinition stepDefinition) { + private Pickle createPickleMatchingStepDefinitions(StubStepDefinition stepDefinition) { String pattern = stepDefinition.getPattern(); - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given " + pattern + "\n" @@ -294,8 +295,8 @@ private CucumberPickle createPickleMatchingStepDefinitions(StubStepDefinition st return feature.getPickles().get(0); } - private CucumberPickle createPicklesWithSteps() { - CucumberFeature feature = TestFeatureParser.parse("file:path/to.feature", "" + + private Pickle createPicklesWithSteps() { + Feature feature = TestFeatureParser.parse("file:path/to.feature", "" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given some step\n" diff --git a/core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java b/core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java index 177ef6ff2e..9d59f8c353 100644 --- a/core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java +++ b/core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java @@ -3,9 +3,9 @@ import io.cucumber.core.backend.CucumberBackendException; import io.cucumber.core.backend.StepDefinition; import io.cucumber.core.exception.CucumberException; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberStep; import io.cucumber.core.feature.TestFeatureParser; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.runtime.StubStepDefinition; import io.cucumber.core.stepexpression.Argument; import io.cucumber.core.stepexpression.StepTypeRegistry; @@ -32,12 +32,12 @@ class StepDefinitionMatchTest { @Test void executes_a_step() throws Throwable { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" ); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition("I have {int} cukes in my belly", Integer.class); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(stepDefinition, stepTypeRegistry); @@ -48,17 +48,16 @@ void executes_a_step() throws Throwable { @Test void throws_arity_mismatch_exception_when_there_are_fewer_parameters_than_arguments() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" ); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition("I have {int} cukes in my belly"); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(stepDefinition, stepTypeRegistry); List arguments = coreStepDefinition.matchedArguments(step); - StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch(arguments, stepDefinition, null, step); Executable testMethod = () -> stepDefinitionMatch.runStep(null); @@ -73,14 +72,14 @@ void throws_arity_mismatch_exception_when_there_are_fewer_parameters_than_argume @Test void throws_arity_mismatch_exception_when_there_are_fewer_parameters_than_arguments_with_data_table() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" + " | A | B | \n" + " | C | D | \n" ); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition("I have {int} cukes in my belly"); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(stepDefinition, stepTypeRegistry); @@ -103,14 +102,14 @@ void throws_arity_mismatch_exception_when_there_are_fewer_parameters_than_argume @Test void throws_arity_mismatch_exception_when_there_are_more_parameters_than_arguments() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" + " | A | B | \n" + " | C | D | \n" ); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition("I have {int} cukes in my belly", Integer.TYPE, Short.TYPE, List.class); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(stepDefinition, stepTypeRegistry); @@ -133,12 +132,12 @@ void throws_arity_mismatch_exception_when_there_are_more_parameters_than_argumen @Test void throws_arity_mismatch_exception_when_there_are_more_parameters_and_no_arguments() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have cukes in my belly\n" ); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition("I have cukes in my belly", Integer.TYPE, Short.TYPE, List.class); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(stepDefinition, stepTypeRegistry); List arguments = coreStepDefinition.matchedArguments(step); @@ -155,13 +154,13 @@ void throws_arity_mismatch_exception_when_there_are_more_parameters_and_no_argum @Test void throws_register_type_in_configuration_exception_when_there_is_no_data_table_type_defined() { - CucumberFeature feature = TestFeatureParser.parse("file:test.feature", "" + + Feature feature = TestFeatureParser.parse("file:test.feature", "" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have a data table\n" + " | A | \n" ); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition( "I have a data table", @@ -169,7 +168,6 @@ void throws_register_type_in_configuration_exception_when_there_is_no_data_table ); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(stepDefinition, stepTypeRegistry); List arguments = coreStepDefinition.matchedArguments(step); - StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch( arguments, stepDefinition, @@ -194,16 +192,15 @@ void throws_could_not_convert_exception_for_transfomer_and_capture_group_mismatc (String s) -> null )); - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have some cukes in my belly\n" ); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition("I have {itemQuantity} in my belly", ItemQuantity.class); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(stepDefinition, stepTypeRegistry); List arguments = coreStepDefinition.matchedArguments(step); - StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch(arguments, stepDefinition, null, step); Executable testMethod = () -> stepDefinitionMatch.runStep(null); @@ -216,7 +213,7 @@ void throws_could_not_convert_exception_for_transfomer_and_capture_group_mismatc @Test void throws_could_not_convert_exception_for_singleton_table_dimension_mismatch() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have some cukes in my belly\n" + @@ -227,11 +224,10 @@ void throws_could_not_convert_exception_for_singleton_table_dimension_mismatch() stepTypeRegistry.defineDataTableType(new DataTableType(ItemQuantity.class, ItemQuantity::new)); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition("I have some cukes in my belly", ItemQuantity.class); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(stepDefinition, stepTypeRegistry); List arguments = coreStepDefinition.matchedArguments(step); - StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch(arguments, stepDefinition, null, step); Executable testMethod = () -> stepDefinitionMatch.runStep(null); @@ -244,7 +240,7 @@ void throws_could_not_convert_exception_for_singleton_table_dimension_mismatch() @Test void throws_could_not_convert_exception_for_docstring() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have some cukes in my belly\n" + @@ -257,11 +253,10 @@ void throws_could_not_convert_exception_for_docstring() { throw new IllegalArgumentException(content); })); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition("I have some cukes in my belly", ItemQuantity.class); CoreStepDefinition coreStepDefinition = new CoreStepDefinition(stepDefinition, stepTypeRegistry); List arguments = coreStepDefinition.matchedArguments(step); - StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch(arguments, stepDefinition, null, step); Executable testMethod = () -> stepDefinitionMatch.runStep(null); @@ -274,13 +269,13 @@ void throws_could_not_convert_exception_for_docstring() { @Test void throws_could_not_invoke_argument_conversion_when_argument_could_not_be_got() { - CucumberFeature feature = TestFeatureParser.parse("file:test.feature", "" + + Feature feature = TestFeatureParser.parse("file:test.feature", "" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have a data table\n" + " | A | \n" ); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition( "I have a data table", @@ -289,7 +284,6 @@ void throws_could_not_invoke_argument_conversion_when_argument_could_not_be_got( List arguments = Collections.singletonList(() -> { throw new CucumberBackendException("This exception is expected", new IllegalAccessException()); }); - StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch( arguments, stepDefinition, @@ -307,14 +301,14 @@ void throws_could_not_invoke_argument_conversion_when_argument_could_not_be_got( @Test void throws_could_not_invoke_step_when_execution_failed_due_to_bad_methods() { - CucumberFeature feature = TestFeatureParser.parse("file:test.feature", "" + + Feature feature = TestFeatureParser.parse("file:test.feature", "" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have a data table\n" + " | A | \n" + " | B | \n" ); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition( "I have a data table", @@ -327,7 +321,6 @@ void throws_could_not_invoke_step_when_execution_failed_due_to_bad_methods() { () -> "mocked table cell", () -> "mocked table cell" ); - StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch( arguments, stepDefinition, diff --git a/core/src/test/java/io/cucumber/core/runner/TestCaseStateResultTest.java b/core/src/test/java/io/cucumber/core/runner/TestCaseStateResultTest.java index fe75cc9493..55c9c53cab 100644 --- a/core/src/test/java/io/cucumber/core/runner/TestCaseStateResultTest.java +++ b/core/src/test/java/io/cucumber/core/runner/TestCaseStateResultTest.java @@ -1,12 +1,12 @@ package io.cucumber.core.runner; +import io.cucumber.core.eventbus.EventBus; +import io.cucumber.core.feature.TestFeatureParser; +import io.cucumber.core.gherkin.Feature; import io.cucumber.plugin.event.EmbedEvent; import io.cucumber.plugin.event.Result; import io.cucumber.plugin.event.Status; import io.cucumber.plugin.event.WriteEvent; -import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.TestFeatureParser; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatcher; @@ -14,6 +14,7 @@ import java.time.Instant; import java.util.Arrays; import java.util.Collections; +import java.util.UUID; import static io.cucumber.core.backend.Status.FAILED; import static io.cucumber.core.backend.Status.PASSED; @@ -34,7 +35,7 @@ class TestCaseStateResultTest { - private final CucumberFeature feature = TestFeatureParser.parse("file:path/file.feature", "" + + private final Feature feature = TestFeatureParser.parse("file:path/file.feature", "" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" @@ -43,6 +44,7 @@ class TestCaseStateResultTest { private final TestCaseState s = new TestCaseState( bus, new TestCase( + UUID.randomUUID(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), diff --git a/core/src/test/java/io/cucumber/core/runner/TestCaseStateTest.java b/core/src/test/java/io/cucumber/core/runner/TestCaseStateTest.java index 247170e70a..f74e61ff18 100644 --- a/core/src/test/java/io/cucumber/core/runner/TestCaseStateTest.java +++ b/core/src/test/java/io/cucumber/core/runner/TestCaseStateTest.java @@ -1,12 +1,13 @@ package io.cucumber.core.runner; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; import io.cucumber.core.feature.TestFeatureParser; +import io.cucumber.core.gherkin.Feature; import org.junit.jupiter.api.Test; import java.io.File; import java.util.Collections; +import java.util.UUID; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; @@ -16,7 +17,7 @@ class TestCaseStateTest { @Test void provides_the_uri_of_the_feature_file() { - CucumberFeature feature = TestFeatureParser.parse("file:path/file.feature", "" + + Feature feature = TestFeatureParser.parse("file:path/file.feature", "" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" @@ -27,7 +28,7 @@ void provides_the_uri_of_the_feature_file() { @Test void provides_the_scenario_line() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" @@ -39,7 +40,7 @@ void provides_the_scenario_line() { @Test void provides_both_the_example_row_line_and_scenario_outline_line_for_scenarios_from_scenario_outlines() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario Outline: Test scenario\n" + " Given I have 4 in my belly\n" + @@ -54,7 +55,7 @@ void provides_both_the_example_row_line_and_scenario_outline_line_for_scenarios_ @Test void provides_the_uri_and_scenario_line_as_unique_id() { - CucumberFeature feature = TestFeatureParser.parse("file:path/file.feature", "" + + Feature feature = TestFeatureParser.parse("file:path/file.feature", "" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" @@ -67,7 +68,7 @@ void provides_the_uri_and_scenario_line_as_unique_id() { @Test void provides_the_uri_and_example_row_line_as_unique_id_for_scenarios_from_scenario_outlines() { - CucumberFeature feature = TestFeatureParser.parse("file:path/file.feature", "" + + Feature feature = TestFeatureParser.parse("file:path/file.feature", "" + "Feature: Test feature\n" + " Scenario Outline: Test scenario\n" + " Given I have 4 in my belly\n" + @@ -80,8 +81,9 @@ void provides_the_uri_and_example_row_line_as_unique_id_for_scenarios_from_scena assertThat(state.getId(), is(new File("path/file.feature:6").toURI().toString())); } - private TestCaseState createTestCaseState(CucumberFeature feature) { + private TestCaseState createTestCaseState(Feature feature) { return new TestCaseState(mock(EventBus.class), new TestCase( + UUID.randomUUID(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), diff --git a/core/src/test/java/io/cucumber/core/runner/TestCaseTest.java b/core/src/test/java/io/cucumber/core/runner/TestCaseTest.java index 8e65d6393d..4ebadf68f4 100644 --- a/core/src/test/java/io/cucumber/core/runner/TestCaseTest.java +++ b/core/src/test/java/io/cucumber/core/runner/TestCaseTest.java @@ -1,19 +1,19 @@ package io.cucumber.core.runner; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.plugin.event.TestCaseFinished; import io.cucumber.plugin.event.TestCaseStarted; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InOrder; -import org.mockito.Mockito; import java.net.URI; import java.time.Instant; import java.util.Collections; +import java.util.UUID; import static io.cucumber.plugin.event.HookType.AFTER_STEP; import static io.cucumber.plugin.event.HookType.BEFORE_STEP; @@ -24,10 +24,11 @@ import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.when; class TestCaseTest { - private final CucumberFeature feature = TestFeatureParser.parse("" + + private final Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" + @@ -41,10 +42,11 @@ class TestCaseTest { private CoreHookDefinition afterStep1HookDefinition1 = mock(CoreHookDefinition.class); private final PickleStepTestStep testStep1 = new PickleStepTestStep( + UUID.randomUUID(), URI.create("file:path/to.feature"), feature.getPickles().get(0).getSteps().get(0), - singletonList(new HookTestStep(BEFORE_STEP, new HookDefinitionMatch(beforeStep1HookDefinition1))), - singletonList(new HookTestStep(AFTER_STEP, new HookDefinitionMatch(afterStep1HookDefinition1))), + singletonList(new HookTestStep(UUID.randomUUID(), BEFORE_STEP, new HookDefinitionMatch(beforeStep1HookDefinition1))), + singletonList(new HookTestStep(UUID.randomUUID(), AFTER_STEP, new HookDefinitionMatch(afterStep1HookDefinition1))), definitionMatch1 ); @@ -52,16 +54,18 @@ class TestCaseTest { private CoreHookDefinition beforeStep1HookDefinition2 = mock(CoreHookDefinition.class); private CoreHookDefinition afterStep1HookDefinition2 = mock(CoreHookDefinition.class); private final PickleStepTestStep testStep2 = new PickleStepTestStep( + UUID.randomUUID(), URI.create("file:path/to.feature"), feature.getPickles().get(0).getSteps().get(1), - singletonList(new HookTestStep(BEFORE_STEP, new HookDefinitionMatch(beforeStep1HookDefinition2))), - singletonList(new HookTestStep(AFTER_STEP, new HookDefinitionMatch(afterStep1HookDefinition2))), + singletonList(new HookTestStep(UUID.randomUUID(), BEFORE_STEP, new HookDefinitionMatch(beforeStep1HookDefinition2))), + singletonList(new HookTestStep(UUID.randomUUID(), AFTER_STEP, new HookDefinitionMatch(afterStep1HookDefinition2))), definitionMatch2 ); @BeforeEach void init() { - Mockito.when(bus.getInstant()).thenReturn(Instant.now()); + when(bus.getInstant()).thenReturn(Instant.now()); + when(bus.generateId()).thenReturn(UUID.randomUUID()); } @Test @@ -126,11 +130,11 @@ void skip_steps_at_first_gherkin_step_after_non_passed_result() throws Throwable } private TestCase createTestCase(PickleStepTestStep... steps) { - return new TestCase(asList(steps), Collections.emptyList(), Collections.emptyList(), pickle(), false); + return new TestCase(UUID.randomUUID(), asList(steps), Collections.emptyList(), Collections.emptyList(), pickle(), false); } - private CucumberPickle pickle() { - CucumberFeature feature = TestFeatureParser.parse("" + + private Pickle pickle() { + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" diff --git a/core/src/test/java/io/cucumber/core/runner/TestHelper.java b/core/src/test/java/io/cucumber/core/runner/TestHelper.java index dee7b20004..7df9a83cbc 100644 --- a/core/src/test/java/io/cucumber/core/runner/TestHelper.java +++ b/core/src/test/java/io/cucumber/core/runner/TestHelper.java @@ -6,12 +6,11 @@ import io.cucumber.core.backend.Located; import io.cucumber.core.backend.StepDefinition; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.Argument; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; -import io.cucumber.core.feature.CucumberStep; -import io.cucumber.core.feature.DataTableArgument; -import io.cucumber.core.feature.DocStringArgument; +import io.cucumber.core.gherkin.Argument; +import io.cucumber.core.gherkin.DocStringArgument; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.options.CommandlineOptionsParser; import io.cucumber.core.runtime.BackendSupplier; import io.cucumber.core.runtime.FeatureSupplier; @@ -22,6 +21,7 @@ import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.EventListener; import io.cucumber.plugin.Plugin; +import io.cucumber.plugin.event.DataTableArgument; import io.cucumber.plugin.event.Event; import io.cucumber.plugin.event.Result; import io.cucumber.plugin.event.Status; @@ -43,6 +43,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.function.Supplier; import static io.cucumber.plugin.event.Status.FAILED; @@ -61,7 +62,7 @@ public class TestHelper { - private List features = Collections.emptyList(); + private List features = Collections.emptyList(); private Map stepsToResult = Collections.emptyMap(); private Map stepsToLocation = Collections.emptyMap(); private List> hooks = Collections.emptyList(); @@ -71,6 +72,7 @@ public class TestHelper { private Duration timeServiceIncrement = Duration.ZERO; private Object formatterUnderTest = null; private List runtimeArgs = Collections.emptyList(); + private TestHelper() { } @@ -212,13 +214,13 @@ private EventBus createEventBus() { EventBus bus = null; if (TimeServiceType.REAL_TIME.equals(this.timeServiceType)) { - bus = new TimeServiceEventBus(Clock.systemUTC()); + bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); } else if (TimeServiceType.FIXED_INCREMENT_ON_STEP_START.equals(this.timeServiceType)) { final StepDurationTimeService timeService = new StepDurationTimeService(this.timeServiceIncrement); - bus = new TimeServiceEventBus(timeService); + bus = new TimeServiceEventBus(timeService, UUID::randomUUID); timeService.setEventPublisher(bus); } else if (TimeServiceType.FIXED_INCREMENT.equals(this.timeServiceType)) { - bus = new TimeServiceEventBus(Clock.fixed(Instant.EPOCH, ZoneId.of("UTC"))); + bus = new TimeServiceEventBus(Clock.fixed(Instant.EPOCH, ZoneId.of("UTC")), UUID::randomUUID); } return bus; } @@ -229,14 +231,14 @@ public enum TimeServiceType { public static final class TestHelperBackendSupplier extends TestBackendSupplier { - private final List features; + private final List features; private final Map stepsToResult; private final Map stepsToLocation; private final List> hooks; private final List hookLocations; private final List> hookActions; - TestHelperBackendSupplier(List features, Map stepsToResult, Map stepsToLocation, List> hooks, List hookLocations, List> hookActions) { + TestHelperBackendSupplier(List features, Map stepsToResult, Map stepsToLocation, List> hooks, List hookLocations, List> hookActions) { this.features = features; this.stepsToResult = stepsToResult; this.stepsToLocation = stepsToLocation; @@ -245,7 +247,7 @@ public static final class TestHelperBackendSupplier extends TestBackendSupplier this.hookActions = hookActions; } - public TestHelperBackendSupplier(List features) { + public TestHelperBackendSupplier(List features) { this( features, Collections.emptyMap(), @@ -256,13 +258,13 @@ public TestHelperBackendSupplier(List features) { ); } - private static void mockSteps(Glue glue, List features, + private static void mockSteps(Glue glue, List features, Map stepsToResult, final Map stepsToLocation) { - List steps = new ArrayList<>(); - for (CucumberFeature feature : features) { - for (CucumberPickle pickle : feature.getPickles()) { - for (CucumberStep step : pickle.getSteps()) { + List steps = new ArrayList<>(); + for (Feature feature : features) { + for (Pickle pickle : feature.getPickles()) { + for (Step step : pickle.getSteps()) { if (!containsStep(steps, step)) { steps.add(step); } @@ -270,7 +272,7 @@ private static void mockSteps(Glue glue, List features, } } - for (final CucumberStep step : steps) { + for (final Step step : steps) { final Result stepResult = getResultWithDefaultPassed(stepsToResult, step.getText()); if (stepResult.getStatus().is(UNDEFINED)) { continue; @@ -307,7 +309,7 @@ public void execute(Object[] args) { @Override public String getLocation() { - return stepsToLocation.get(step.getText()); + return stepsToLocation.getOrDefault(step.getText(), "mocked location"); } }; @@ -319,8 +321,8 @@ private static Result getResultWithDefaultPassed(Map stepsToResu return stepsToResult.containsKey(step) ? stepsToResult.get(step) : new Result(PASSED, ZERO, null); } - private static boolean containsStep(List steps, CucumberStep step) { - for (CucumberStep definedSteps : steps) { + private static boolean containsStep(List steps, Step step) { + for (Step definedSteps : steps) { if (definedSteps.getText().equals(step.getText()) && (definedSteps.getArgument() == null) == (step.getArgument() == null)) { return true; @@ -330,7 +332,7 @@ private static boolean containsStep(List steps, CucumberStep step) return false; } - private static Type[] mapArgumentToTypes(CucumberStep step) { + private static Type[] mapArgumentToTypes(Step step) { Type[] types = new Type[0]; Argument argument = step.getArgument(); if (argument == null) { @@ -345,7 +347,7 @@ private static Type[] mapArgumentToTypes(CucumberStep step) { private static void mockHooks(Glue glue, final List> hooks, final List hookLocations, - final List> hookActions) throws Throwable { + final List> hookActions) { List beforeHooks = new ArrayList<>(); List afterHooks = new ArrayList<>(); List beforeStepHooks = new ArrayList<>(); @@ -419,12 +421,8 @@ public String getLocation() { @Override public void loadGlue(Glue glue, List gluePaths) { - try { - mockSteps(glue, features, stepsToResult, stepsToLocation); - mockHooks(glue, hooks, hookLocations, hookActions); - } catch (Throwable throwable) { - throw new RuntimeException(throwable); - } + mockSteps(glue, features, stepsToResult, stepsToLocation); + mockHooks(glue, hooks, hookLocations, hookActions); } } @@ -435,11 +433,11 @@ public static final class Builder { private Builder() { } - public Builder withFeatures(CucumberFeature... features) { + public Builder withFeatures(Feature... features) { return withFeatures(Arrays.asList(features)); } - public Builder withFeatures(List features) { + public Builder withFeatures(List features) { this.instance.features = features; return this; } diff --git a/core/src/test/java/io/cucumber/core/runner/UndefinedStepDefinitionMatchTest.java b/core/src/test/java/io/cucumber/core/runner/UndefinedStepDefinitionMatchTest.java index 491981dcfe..22aef7c517 100644 --- a/core/src/test/java/io/cucumber/core/runner/UndefinedStepDefinitionMatchTest.java +++ b/core/src/test/java/io/cucumber/core/runner/UndefinedStepDefinitionMatchTest.java @@ -1,6 +1,6 @@ package io.cucumber.core.runner; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.feature.TestFeatureParser; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; @@ -15,7 +15,7 @@ class UndefinedStepDefinitionMatchTest { - private final CucumberFeature feature = TestFeatureParser.parse("" + + private final Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my belly\n" diff --git a/core/src/test/java/io/cucumber/core/runtime/ExitStatusTest.java b/core/src/test/java/io/cucumber/core/runtime/ExitStatusTest.java index 1947b177af..e72c95203d 100644 --- a/core/src/test/java/io/cucumber/core/runtime/ExitStatusTest.java +++ b/core/src/test/java/io/cucumber/core/runtime/ExitStatusTest.java @@ -11,6 +11,7 @@ import java.time.Clock; import java.time.Instant; +import java.util.UUID; import static java.time.Duration.ZERO; import static org.hamcrest.MatcherAssert.assertThat; @@ -45,7 +46,7 @@ private void createExitStatus(String... runtimeArgs) { RuntimeOptions runtimeOptions = new CommandlineOptionsParser() .parse(runtimeArgs) .build(); - this.bus = new TimeServiceEventBus(Clock.systemUTC()); + this.bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); exitStatus = new Runtime.ExitStatus(runtimeOptions); exitStatus.setEventPublisher(bus); } diff --git a/core/src/test/java/io/cucumber/core/runtime/FeatureBuilderTest.java b/core/src/test/java/io/cucumber/core/runtime/FeatureBuilderTest.java index 0f31de66db..99760d01ac 100644 --- a/core/src/test/java/io/cucumber/core/runtime/FeatureBuilderTest.java +++ b/core/src/test/java/io/cucumber/core/runtime/FeatureBuilderTest.java @@ -1,7 +1,7 @@ package io.cucumber.core.runtime; -import io.cucumber.core.feature.CucumberFeature; import io.cucumber.core.feature.FeatureParser; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.resource.Resource; import io.cucumber.core.runtime.FeaturePathFeatureSupplier.FeatureBuilder; import org.junit.jupiter.api.Test; @@ -10,6 +10,7 @@ import java.io.InputStream; import java.net.URI; import java.util.List; +import java.util.UUID; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; @@ -18,6 +19,7 @@ class FeatureBuilderTest { + private final FeatureParser parser = new FeatureParser(UUID::randomUUID); private final FeatureBuilder builder = new FeatureBuilder(); @Test @@ -25,13 +27,13 @@ void ignores_identical_features_in_different_directories() { URI featurePath1 = URI.create("src/example.feature"); URI featurePath2 = URI.create("build/example.feature"); - CucumberFeature resource1 = createResourceMock(featurePath1); - CucumberFeature resource2 = createResourceMock(featurePath2); + Feature resource1 = createResourceMock(featurePath1); + Feature resource2 = createResourceMock(featurePath2); builder.addUnique(resource1); builder.addUnique(resource2); - List features = builder.build(); + List features = builder.build(); assertThat(features.size(), equalTo(1)); } @@ -41,13 +43,13 @@ void duplicate_content_with_different_file_names_are_intentionally_duplicated() URI featurePath1 = URI.create("src/feature1/example-first.feature"); URI featurePath2 = URI.create("src/feature1/example-second.feature"); - CucumberFeature resource1 = createResourceMock(featurePath1); - CucumberFeature resource2 = createResourceMock(featurePath2); + Feature resource1 = createResourceMock(featurePath1); + Feature resource2 = createResourceMock(featurePath2); builder.addUnique(resource1); builder.addUnique(resource2); - List features = builder.build(); + List features = builder.build(); assertAll( () -> assertThat(features.size(), equalTo(2)), @@ -63,15 +65,15 @@ void features_are_sorted_by_uri() { URI featurePath2 = URI.create("b.feature"); URI featurePath3 = URI.create("a.feature"); - CucumberFeature resource1 = createResourceMock(featurePath1); - CucumberFeature resource2 = createResourceMock(featurePath2); - CucumberFeature resource3 = createResourceMock(featurePath3); + Feature resource1 = createResourceMock(featurePath1); + Feature resource2 = createResourceMock(featurePath2); + Feature resource3 = createResourceMock(featurePath3); builder.addUnique(resource1); builder.addUnique(resource2); builder.addUnique(resource3); - List features = builder.build(); + List features = builder.build(); assertAll( () -> assertThat(features.get(0).getUri(), equalTo(featurePath3)), @@ -80,8 +82,8 @@ void features_are_sorted_by_uri() { ); } - private CucumberFeature createResourceMock(URI featurePath) { - return FeatureParser.parseResource(new Resource() { + private Feature createResourceMock(URI featurePath) { + return parser.parseResource(new Resource() { @Override public URI getUri() { return featurePath; diff --git a/core/src/test/java/io/cucumber/core/runtime/FeaturePathFeatureSupplierTest.java b/core/src/test/java/io/cucumber/core/runtime/FeaturePathFeatureSupplierTest.java index c2a861655c..9f62943155 100644 --- a/core/src/test/java/io/cucumber/core/runtime/FeaturePathFeatureSupplierTest.java +++ b/core/src/test/java/io/cucumber/core/runtime/FeaturePathFeatureSupplierTest.java @@ -1,5 +1,6 @@ package io.cucumber.core.runtime; +import io.cucumber.core.feature.FeatureParser; import io.cucumber.core.feature.FeaturePath; import io.cucumber.core.feature.Options; import io.cucumber.core.logging.LogRecordListener; @@ -9,6 +10,7 @@ import org.junit.jupiter.api.Test; import java.util.Collections; +import java.util.UUID; import java.util.function.Supplier; import static java.util.Collections.singletonList; @@ -21,6 +23,7 @@ class FeaturePathFeatureSupplierTest { private LogRecordListener logRecordListener; private final Supplier classLoader = FeaturePathFeatureSupplierTest.class::getClassLoader; + private final FeatureParser parser = new FeatureParser(UUID::randomUUID); @BeforeEach void setup() { @@ -37,7 +40,7 @@ void tearDown() { void logs_message_if_no_features_are_found() { Options featureOptions = () -> singletonList(FeaturePath.parse("src/test/resources/io/cucumber/core/options")); - FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(classLoader, featureOptions); + FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(classLoader, featureOptions, parser); supplier.get(); assertAll( () -> assertThat(logRecordListener.getLogRecords().get(1).getMessage(), containsString("No features found at file:")), @@ -49,7 +52,7 @@ void logs_message_if_no_features_are_found() { void logs_message_if_no_feature_paths_are_given() { Options featureOptions = Collections::emptyList; - FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(classLoader, featureOptions); + FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(classLoader, featureOptions, parser); supplier.get(); assertThat(logRecordListener.getLogRecords().get(1).getMessage(), containsString("Got no path to feature directory or feature file")); } @@ -57,7 +60,7 @@ void logs_message_if_no_feature_paths_are_given() { @Test void throws_if_path_does_not_exist() { Options featureOptions = () -> singletonList(FeaturePath.parse("file:does/not/exist")); - FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(classLoader, featureOptions); + FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(classLoader, featureOptions, parser); IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, supplier::get @@ -68,7 +71,7 @@ void throws_if_path_does_not_exist() { @Test void throws_if_feature_does_not_exist() { Options featureOptions = () -> singletonList(FeaturePath.parse("classpath:no-such.feature")); - FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(classLoader, featureOptions); + FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(classLoader, featureOptions, parser); IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, supplier::get diff --git a/core/src/test/java/io/cucumber/core/runtime/RuntimeTest.java b/core/src/test/java/io/cucumber/core/runtime/RuntimeTest.java index e0eee7f532..d8aab3e609 100644 --- a/core/src/test/java/io/cucumber/core/runtime/RuntimeTest.java +++ b/core/src/test/java/io/cucumber/core/runtime/RuntimeTest.java @@ -7,9 +7,9 @@ import io.cucumber.core.backend.TestCaseState; import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.exception.CompositeCucumberException; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.options.CommandlineOptionsParser; import io.cucumber.core.options.RuntimeOptionsBuilder; @@ -42,6 +42,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import static io.cucumber.core.runner.TestHelper.result; @@ -63,11 +64,11 @@ class RuntimeTest { private final static Instant ANY_INSTANT = Instant.ofEpochMilli(1234567890); - private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC()); + private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); @Test void runs_feature_with_json_formatter() { - final CucumberFeature feature = TestFeatureParser.parse("test.feature", "" + + final Feature feature = TestFeatureParser.parse("test.feature", "" + "Feature: feature name\n" + " Background: background name\n" + " Given b\n" + @@ -80,7 +81,7 @@ void runs_feature_with_json_formatter() { FeatureSupplier featureSupplier = new TestFeatureSupplier(bus, feature); Runtime.builder() .withAdditionalPlugins(jsonFormatter) - .withEventBus(new TimeServiceEventBus(Clock.fixed(Instant.EPOCH, ZoneId.of("UTC")))) + .withEventBus(new TimeServiceEventBus(Clock.fixed(Instant.EPOCH, ZoneId.of("UTC")), UUID::randomUUID)) .withFeatureSupplier(featureSupplier) .build() .run(); @@ -248,8 +249,8 @@ void should_pass_if_no_features_are_found() { } @Test - void should_make_scenario_name_available_to_hooks() throws Throwable { - final CucumberFeature feature = TestFeatureParser.parse("path/test.feature", + void should_make_scenario_name_available_to_hooks() { + final Feature feature = TestFeatureParser.parse("path/test.feature", "Feature: feature name\n" + " Scenario: scenario name\n" + " Given first step\n" + @@ -274,12 +275,12 @@ void should_make_scenario_name_available_to_hooks() throws Throwable { assertThat(capturedScenario.getValue().getName(), is(equalTo("scenario name"))); } - private TestBackendSupplier createTestBackendSupplier(final CucumberFeature feature, final HookDefinition beforeHook) { + private TestBackendSupplier createTestBackendSupplier(final Feature feature, final HookDefinition beforeHook) { return new TestBackendSupplier() { @Override public void loadGlue(Glue glue, List gluePaths) { - for (CucumberPickle child : feature.getPickles()) { - for (CucumberStep step : child.getSteps()) { + for (Pickle child : feature.getPickles()) { + for (Step step : child.getSteps()) { mockMatch(glue, step.getText()); } } @@ -290,7 +291,7 @@ public void loadGlue(Glue glue, List gluePaths) { @Test void should_call_formatter_for_two_scenarios_with_background() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Background: background\n" + " Given first step\n" + @@ -327,7 +328,7 @@ void should_call_formatter_for_two_scenarios_with_background() { @Test void should_call_formatter_for_scenario_outline_with_two_examples_table_and_background() { - CucumberFeature feature = TestFeatureParser.parse("path/test.feature", "" + + Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Background: background\n" + " Given first step\n" + @@ -379,25 +380,25 @@ void should_call_formatter_for_scenario_outline_with_two_examples_table_and_back @Test void should_call_formatter_with_correct_sequence_of_events_when_running_in_parallel() { - CucumberFeature feature1 = TestFeatureParser.parse("path/test.feature", "" + + Feature feature1 = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name 1\n" + " Scenario: scenario_1 name\n" + " Given first step\n" + " Scenario: scenario_2 name\n" + " Given first step\n"); - CucumberFeature feature2 = TestFeatureParser.parse("path/test2.feature", "" + + Feature feature2 = TestFeatureParser.parse("path/test2.feature", "" + "Feature: feature name 2\n" + " Scenario: scenario_2 name\n" + " Given first step\n"); - CucumberFeature feature3 = TestFeatureParser.parse("path/test3.feature", "" + + Feature feature3 = TestFeatureParser.parse("path/test3.feature", "" + "Feature: feature name 3\n" + " Scenario: scenario_3 name\n" + " Given first step\n"); FormatterSpy formatterSpy = new FormatterSpy(); - final List features = Arrays.asList(feature1, feature2, feature3); + final List features = Arrays.asList(feature1, feature2, feature3); Runtime.builder() .withFeatureSupplier(new TestFeatureSupplier(bus, features)) @@ -437,14 +438,14 @@ void should_call_formatter_with_correct_sequence_of_events_when_running_in_paral @Test void should_fail_on_event_listener_exception_when_running_in_parallel() { - CucumberFeature feature1 = TestFeatureParser.parse("path/test.feature", "" + + Feature feature1 = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name 1\n" + " Scenario: scenario_1 name\n" + " Given first step\n" + " Scenario: scenario_2 name\n" + " Given first step\n"); - CucumberFeature feature2 = TestFeatureParser.parse("path/test2.feature", "" + + Feature feature2 = TestFeatureParser.parse("path/test2.feature", "" + "Feature: feature name 2\n" + " Scenario: scenario_2 name\n" + " Given first step\n"); @@ -471,14 +472,14 @@ void should_fail_on_event_listener_exception_when_running_in_parallel() { @Test void should_interrupt_waiting_plugins() throws InterruptedException { - final CucumberFeature feature1 = TestFeatureParser.parse("path/test.feature", "" + + final Feature feature1 = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name 1\n" + " Scenario: scenario_1 name\n" + " Given first step\n" + " Scenario: scenario_2 name\n" + " Given first step\n"); - final CucumberFeature feature2 = TestFeatureParser.parse("path/test2.feature", "" + + final Feature feature2 = TestFeatureParser.parse("path/test2.feature", "" + "Feature: feature name 2\n" + " Scenario: scenario_2 name\n" + " Given first step\n"); @@ -512,7 +513,7 @@ void should_interrupt_waiting_plugins() throws InterruptedException { @Test void generates_events_for_glue_and_scenario_scoped_glue() { - final CucumberFeature feature = TestFeatureParser.parse("test.feature", "" + + final Feature feature = TestFeatureParser.parse("test.feature", "" + "Feature: feature name\n" + " Scenario: Run a scenario once\n" + " Given global scoped\n" + @@ -554,7 +555,7 @@ public void buildWorld() { Runtime.builder() .withBackendSupplier(backendSupplier) .withAdditionalPlugins(eventListener) - .withEventBus(new TimeServiceEventBus(new StepDurationTimeService(ZERO))) + .withEventBus(new TimeServiceEventBus(new StepDurationTimeService(ZERO), UUID::randomUUID)) .withFeatureSupplier(featureSupplier) .build() .run(); @@ -567,7 +568,7 @@ public void buildWorld() { assertThat(stepDefinedEvents.size(), is(4)); } - private String runFeatureWithFormatterSpy(CucumberFeature feature, Map stepsToResult) { + private String runFeatureWithFormatterSpy(Feature feature, Map stepsToResult) { FormatterSpy formatterSpy = new FormatterSpy(); TestHelper.builder() diff --git a/core/src/test/java/io/cucumber/core/runtime/SingletonRunnerSupplierTest.java b/core/src/test/java/io/cucumber/core/runtime/SingletonRunnerSupplierTest.java index 4beab961e1..4502942c6c 100644 --- a/core/src/test/java/io/cucumber/core/runtime/SingletonRunnerSupplierTest.java +++ b/core/src/test/java/io/cucumber/core/runtime/SingletonRunnerSupplierTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test; import java.time.Clock; +import java.util.UUID; import java.util.function.Supplier; import static org.hamcrest.MatcherAssert.assertThat; @@ -24,7 +25,7 @@ void before() { ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions); ObjectFactorySupplier objectFactory = new SingletonObjectFactorySupplier(objectFactoryServiceLoader); BackendServiceLoader backendSupplier = new BackendServiceLoader(getClass()::getClassLoader, objectFactory); - EventBus eventBus = new TimeServiceEventBus(Clock.systemUTC()); + EventBus eventBus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier = new ScanningTypeRegistryConfigurerSupplier(classLoader, runtimeOptions); runnerSupplier = new SingletonRunnerSupplier(runtimeOptions, eventBus, backendSupplier, objectFactory, typeRegistryConfigurerSupplier); } diff --git a/core/src/test/java/io/cucumber/core/runtime/TestFeatureSupplier.java b/core/src/test/java/io/cucumber/core/runtime/TestFeatureSupplier.java index f1a1215534..67035e6afe 100644 --- a/core/src/test/java/io/cucumber/core/runtime/TestFeatureSupplier.java +++ b/core/src/test/java/io/cucumber/core/runtime/TestFeatureSupplier.java @@ -1,7 +1,7 @@ package io.cucumber.core.runtime; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.plugin.event.TestSourceRead; import java.util.Arrays; @@ -9,20 +9,20 @@ public class TestFeatureSupplier implements FeatureSupplier { private final EventBus bus ; - private final List features; + private final List features; - public TestFeatureSupplier(EventBus bus, CucumberFeature... features) { + public TestFeatureSupplier(EventBus bus, Feature... features) { this(bus, Arrays.asList(features)); } - public TestFeatureSupplier(EventBus bus, List features) { + public TestFeatureSupplier(EventBus bus, List features) { this.bus = bus; this.features = features; } @Override - public List get() { - for (CucumberFeature feature : features) { + public List get() { + for (Feature feature : features) { bus.send(new TestSourceRead(bus.getInstant(), feature.getUri(), feature.getSource())); } return features; diff --git a/core/src/test/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplierTest.java b/core/src/test/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplierTest.java index 41dc32a691..9dcb64ef4c 100644 --- a/core/src/test/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplierTest.java +++ b/core/src/test/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplierTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test; import java.time.Clock; +import java.util.UUID; import java.util.function.Supplier; import static java.time.Instant.EPOCH; @@ -33,7 +34,7 @@ void before() { ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions); ObjectFactorySupplier objectFactory = new SingletonObjectFactorySupplier(objectFactoryServiceLoader); BackendServiceLoader backendSupplier = new BackendServiceLoader(classLoader, objectFactory); - eventBus = new TimeServiceEventBus(Clock.systemUTC()); + eventBus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier = new ScanningTypeRegistryConfigurerSupplier(classLoader, runtimeOptions); runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, eventBus, backendSupplier, objectFactory, typeRegistryConfigurerSupplier); } diff --git a/core/src/test/resources/io/cucumber/core/plugin/JSONPrettyFormatterTest.json b/core/src/test/resources/io/cucumber/core/plugin/JSONPrettyFormatterTest.json index a272c77fe7..9db6012f44 100644 --- a/core/src/test/resources/io/cucumber/core/plugin/JSONPrettyFormatterTest.json +++ b/core/src/test/resources/io/cucumber/core/plugin/JSONPrettyFormatterTest.json @@ -1,49 +1,44 @@ [ { - "id": "feature-3", - "description": "", - "name": "Feature_3", - "keyword": "Feature", "line": 1, "elements": [ { - "description": "", + "line": 3, "name": "", + "description": "", + "type": "background", "keyword": "Background", - "line": 3, "steps": [ { "result": { "status": "undefined" }, - "name": "bg_1", - "keyword": "Given ", "line": 4, - "match": {} + "name": "bg_1", + "match": {}, + "keyword": "Given " }, { "result": { "status": "undefined" }, - "name": "bg_2", - "keyword": "When ", "line": 5, - "match": {} + "name": "bg_2", + "match": {}, + "keyword": "When " }, { "result": { "status": "undefined" }, - "name": "bg_3", - "keyword": "Then ", "line": 6, - "match": {} + "name": "bg_3", + "match": {}, + "keyword": "Then " } - ], - "type": "background" + ] }, { - "id": "feature-3;scenario-1", "start_timestamp": "1970-01-01T00:00:11.106Z", "before": [ { @@ -54,88 +49,88 @@ "match": {} } ], - "description": "", + "line": 8, "name": "Scenario_1", + "description": "", + "id": "feature-3;scenario-1", + "type": "scenario", "keyword": "Scenario", - "line": 8, "steps": [ { "result": { "status": "undefined" }, - "name": "step_1", - "keyword": "Given ", "line": 9, - "match": {} + "name": "step_1", + "match": {}, + "keyword": "Given " }, { "result": { "status": "undefined" }, - "name": "step_2", - "keyword": "When ", "line": 10, - "match": {} + "name": "step_2", + "match": {}, + "keyword": "When " }, { "result": { "status": "undefined" }, - "name": "step_3", - "keyword": "Then ", "line": 11, - "match": {} + "name": "step_3", + "match": {}, + "keyword": "Then " }, { "result": { "status": "undefined" }, - "name": "cliché", - "keyword": "Then ", "line": 12, - "match": {} + "name": "cliché", + "match": {}, + "keyword": "Then " } - ], - "type": "scenario" + ] }, { - "description": "", + "line": 3, "name": "", + "description": "", + "type": "background", "keyword": "Background", - "line": 3, "steps": [ { "result": { "status": "undefined" }, - "name": "bg_1", - "keyword": "Given ", "line": 4, - "match": {} + "name": "bg_1", + "match": {}, + "keyword": "Given " }, { "result": { "status": "undefined" }, - "name": "bg_2", - "keyword": "When ", "line": 5, - "match": {} + "name": "bg_2", + "match": {}, + "keyword": "When " }, { "result": { "status": "undefined" }, - "name": "bg_3", - "keyword": "Then ", "line": 6, - "match": {} + "name": "bg_3", + "match": {}, + "keyword": "Then " } - ], - "type": "background" + ] }, { - "id": "feature-3;scenariooutline-1;;2", "start_timestamp": "1970-01-01T00:00:40.722Z", "before": [ { @@ -146,79 +141,79 @@ "match": {} } ], - "description": "", + "line": 21, "name": "ScenarioOutline_1", + "description": "", + "id": "feature-3;scenariooutline-1;;2", + "type": "scenario", "keyword": "Scenario Outline", - "line": 21, "steps": [ { "result": { "status": "undefined" }, - "name": "so_1 12", - "keyword": "Given ", "line": 15, - "match": {} + "name": "so_1 12", + "match": {}, + "keyword": "Given " }, { "result": { "status": "undefined" }, - "name": "so_2 7 cucumbers", - "keyword": "When ", "line": 16, - "match": {} + "name": "so_2 7 cucumbers", + "match": {}, + "keyword": "When " }, { "result": { "status": "undefined" }, - "name": "5 so_3", - "keyword": "Then ", "line": 17, - "match": {} + "name": "5 so_3", + "match": {}, + "keyword": "Then " } - ], - "type": "scenario" + ] }, { - "description": "", + "line": 3, "name": "", + "description": "", + "type": "background", "keyword": "Background", - "line": 3, "steps": [ { "result": { "status": "undefined" }, - "name": "bg_1", - "keyword": "Given ", "line": 4, - "match": {} + "name": "bg_1", + "match": {}, + "keyword": "Given " }, { "result": { "status": "undefined" }, - "name": "bg_2", - "keyword": "When ", "line": 5, - "match": {} + "name": "bg_2", + "match": {}, + "keyword": "When " }, { "result": { "status": "undefined" }, - "name": "bg_3", - "keyword": "Then ", "line": 6, - "match": {} + "name": "bg_3", + "match": {}, + "keyword": "Then " } - ], - "type": "background" + ] }, { - "id": "feature-3;scenariooutline-1;;3", "start_timestamp": "1970-01-01T00:01:07.870Z", "before": [ { @@ -229,79 +224,79 @@ "match": {} } ], - "description": "", + "line": 22, "name": "ScenarioOutline_1", + "description": "", + "id": "feature-3;scenariooutline-1;;3", + "type": "scenario", "keyword": "Scenario Outline", - "line": 22, "steps": [ { "result": { "status": "undefined" }, - "name": "so_1 20", - "keyword": "Given ", "line": 15, - "match": {} + "name": "so_1 20", + "match": {}, + "keyword": "Given " }, { "result": { "status": "undefined" }, - "name": "so_2 15 cucumbers", - "keyword": "When ", "line": 16, - "match": {} + "name": "so_2 15 cucumbers", + "match": {}, + "keyword": "When " }, { "result": { "status": "undefined" }, - "name": "5 so_3", - "keyword": "Then ", "line": 17, - "match": {} + "name": "5 so_3", + "match": {}, + "keyword": "Then " } - ], - "type": "scenario" + ] }, { - "description": "", + "line": 3, "name": "", + "description": "", + "type": "background", "keyword": "Background", - "line": 3, "steps": [ { "result": { "status": "undefined" }, - "name": "bg_1", - "keyword": "Given ", "line": 4, - "match": {} + "name": "bg_1", + "match": {}, + "keyword": "Given " }, { "result": { "status": "undefined" }, - "name": "bg_2", - "keyword": "When ", "line": 5, - "match": {} + "name": "bg_2", + "match": {}, + "keyword": "When " }, { "result": { "status": "undefined" }, - "name": "bg_3", - "keyword": "Then ", "line": 6, - "match": {} + "name": "bg_3", + "match": {}, + "keyword": "Then " } - ], - "type": "background" + ] }, { - "id": "feature-3;scenario-2", "start_timestamp": "1970-01-01T00:01:35.018Z", "before": [ { @@ -312,43 +307,48 @@ "match": {} } ], - "description": "", + "line": 24, "name": "Scenario_2", + "description": "", + "id": "feature-3;scenario-2", + "type": "scenario", "keyword": "Scenario", - "line": 24, "steps": [ { "result": { "status": "undefined" }, - "name": "a", - "keyword": "Given ", "line": 25, - "match": {} + "name": "a", + "match": {}, + "keyword": "Given " }, { "result": { "status": "undefined" }, - "name": "b", - "keyword": "Then ", "line": 26, - "match": {} + "name": "b", + "match": {}, + "keyword": "Then " }, { "result": { "status": "undefined" }, - "name": "c", - "keyword": "When ", "line": 27, - "match": {} + "name": "c", + "match": {}, + "keyword": "When " } - ], - "type": "scenario" + ] } ], + "name": "Feature_3", + "description": "", + "id": "feature-3", + "keyword": "Feature", "uri": "classpath:io/cucumber/core/plugin/JSONPrettyFormatterTest.feature", "tags": [] } -] \ No newline at end of file +] diff --git a/gherkin-vintage/pom.xml b/gherkin-vintage/pom.xml new file mode 100644 index 0000000000..cbdfc384d4 --- /dev/null +++ b/gherkin-vintage/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + io.cucumber + cucumber-jvm + 5.0.0-RC3-SNAPSHOT + + + + io.cucumber.core.gherkin.vintage + + + cucumber-gherkin-vintage + jar + Cucumber-JVM: Gherkin Vintage + + + + io.cucumber + gherkin + ${gherkin-vintage.version} + + + + io.cucumber + gherkin-jvm-deps + 1.0.6 + + + + io.cucumber + cucumber-gherkin + + + + org.junit.jupiter + junit-jupiter + test + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.0 + + + package + + shade + + + + + io.cucumber:gherkin + io.cucumber:gherkin-jvm-deps + + + + + gherkin + io.cucumber.core.gherkin.vintage.internal.gherkin + + + + + + + + + + diff --git a/core/src/main/java/io/cucumber/core/feature/DataTableArgument.java b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageDataTableArgument.java similarity index 83% rename from core/src/main/java/io/cucumber/core/feature/DataTableArgument.java rename to gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageDataTableArgument.java index 1459a1518f..562c0f9a9c 100644 --- a/core/src/main/java/io/cucumber/core/feature/DataTableArgument.java +++ b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageDataTableArgument.java @@ -1,16 +1,17 @@ -package io.cucumber.core.feature; +package io.cucumber.core.gherkin.vintage; import gherkin.pickles.PickleTable; +import io.cucumber.core.gherkin.DataTableArgument; import java.util.AbstractList; import java.util.List; -public final class DataTableArgument implements Argument, io.cucumber.plugin.event.DataTableArgument { +final class GherkinVintageDataTableArgument implements DataTableArgument { private final CellView cells; private final int line; - DataTableArgument(PickleTable table) { + GherkinVintageDataTableArgument(PickleTable table) { this.cells = new CellView(table); this.line = table.getLocation().getLine(); } diff --git a/core/src/main/java/io/cucumber/core/feature/DocStringArgument.java b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageDocStringArgument.java similarity index 64% rename from core/src/main/java/io/cucumber/core/feature/DocStringArgument.java rename to gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageDocStringArgument.java index d2f9bf4a44..8bfb49b8d0 100644 --- a/core/src/main/java/io/cucumber/core/feature/DocStringArgument.java +++ b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageDocStringArgument.java @@ -1,12 +1,13 @@ -package io.cucumber.core.feature; +package io.cucumber.core.gherkin.vintage; import gherkin.pickles.PickleString; +import io.cucumber.core.gherkin.DocStringArgument; -public final class DocStringArgument implements Argument, io.cucumber.plugin.event.DocStringArgument { +final class GherkinVintageDocStringArgument implements DocStringArgument { private final PickleString docString; - DocStringArgument(PickleString docString) { + GherkinVintageDocStringArgument(PickleString docString) { this.docString = docString; } diff --git a/core/src/main/java/io/cucumber/core/feature/CucumberExample.java b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageExample.java similarity index 50% rename from core/src/main/java/io/cucumber/core/feature/CucumberExample.java rename to gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageExample.java index 83112a6985..ec9b841280 100644 --- a/core/src/main/java/io/cucumber/core/feature/CucumberExample.java +++ b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageExample.java @@ -1,13 +1,15 @@ -package io.cucumber.core.feature; +package io.cucumber.core.gherkin.vintage; import gherkin.ast.TableRow; +import io.cucumber.core.gherkin.Example; +import io.cucumber.core.gherkin.Location; -public final class CucumberExample implements Located, Named { +final class GherkinVintageExample implements Example { private final TableRow tableRow; private final int rowIndex; - CucumberExample(TableRow tableRow, int rowIndex) { + GherkinVintageExample(TableRow tableRow, int rowIndex) { this.tableRow = tableRow; this.rowIndex = rowIndex; } @@ -22,7 +24,7 @@ public String getName() { } @Override - public CucumberLocation getLocation() { - return CucumberLocation.from(tableRow.getLocation()); + public Location getLocation() { + return GherkinVintageLocation.from(tableRow.getLocation()); } } diff --git a/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageExamples.java b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageExamples.java new file mode 100644 index 0000000000..c3555b88b6 --- /dev/null +++ b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageExamples.java @@ -0,0 +1,43 @@ +package io.cucumber.core.gherkin.vintage; + +import io.cucumber.core.gherkin.Example; +import io.cucumber.core.gherkin.Examples; +import io.cucumber.core.gherkin.Location; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; + +final class GherkinVintageExamples implements Examples { + + private final gherkin.ast.Examples examples; + + GherkinVintageExamples(gherkin.ast.Examples examples) { + this.examples = examples; + } + + @Override + public Stream children() { + if (examples.getTableBody() == null) { + return Stream.empty(); + } + + AtomicInteger rowCounter = new AtomicInteger(1); + return examples.getTableBody().stream() + .map(tableRow -> new GherkinVintageExample(tableRow, rowCounter.getAndIncrement())); + } + + @Override + public Location getLocation() { + return GherkinVintageLocation.from(examples.getLocation()); + } + + @Override + public String getKeyWord() { + return examples.getKeyword(); + } + + @Override + public String getName() { + return examples.getName(); + } +} diff --git a/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageFeature.java b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageFeature.java new file mode 100644 index 0000000000..c02a9dfd93 --- /dev/null +++ b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageFeature.java @@ -0,0 +1,98 @@ +package io.cucumber.core.gherkin.vintage; + +import gherkin.ast.GherkinDocument; +import gherkin.ast.ScenarioOutline; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Located; +import io.cucumber.core.gherkin.Location; +import io.cucumber.core.gherkin.Node; +import io.cucumber.core.gherkin.Pickle; + +import java.net.URI; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +final class GherkinVintageFeature implements Feature { + private final URI uri; + private final List pickles; + private final GherkinDocument gherkinDocument; + private final String gherkinSource; + + GherkinVintageFeature(GherkinDocument gherkinDocument, URI uri, String gherkinSource, List pickles) { + this.gherkinDocument = gherkinDocument; + this.uri = uri; + this.gherkinSource = gherkinSource; + this.pickles = pickles; + } + + @Override + public Stream children() { + return gherkinDocument.getFeature().getChildren().stream() + .map(scenarioDefinition -> { + if (scenarioDefinition instanceof ScenarioOutline) { + ScenarioOutline outline = (ScenarioOutline) scenarioDefinition; + return new GherkinVintageScenarioOutline(outline); + } + return new GherkinVintageScenario(scenarioDefinition); + }).map(Node.class::cast); + } + + @Override + public String getKeyword() { + return gherkinDocument.getFeature().getKeyword(); + } + + @Override + public Optional getPickleAt(Located located) { + Location location = located.getLocation(); + return pickles.stream() + .filter(cucumberPickle -> cucumberPickle.getLocation().equals(location)) + .findFirst(); + } + + @Override + public Location getLocation() { + return GherkinVintageLocation.from(gherkinDocument.getFeature().getLocation()); + } + + @Override + public List getPickles() { + return pickles; + } + + @Override + public String getKeyWord() { + return null; + } + + @Override + public String getName() { + return gherkinDocument.getFeature().getName(); + } + + @Override + public URI getUri() { + return uri; + } + + @Override + public String getSource() { + return gherkinSource; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GherkinVintageFeature that = (GherkinVintageFeature) o; + return uri.equals(that.uri); + } + + @Override + public int hashCode() { + return Objects.hash(uri); + } + +} diff --git a/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageFeatureParser.java b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageFeatureParser.java new file mode 100644 index 0000000000..12cb745397 --- /dev/null +++ b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageFeatureParser.java @@ -0,0 +1,58 @@ +package io.cucumber.core.gherkin.vintage; + +import gherkin.AstBuilder; +import gherkin.GherkinDialect; +import gherkin.GherkinDialectProvider; +import gherkin.Parser; +import gherkin.ParserException; +import gherkin.TokenMatcher; +import gherkin.ast.GherkinDocument; +import gherkin.pickles.Compiler; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.FeatureParser; +import io.cucumber.core.gherkin.FeatureParserException; +import io.cucumber.core.gherkin.Pickle; + +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public final class GherkinVintageFeatureParser implements FeatureParser { + private static Feature parseGherkin5(URI path, String source) { + try { + Parser parser = new Parser<>(new AstBuilder()); + TokenMatcher matcher = new TokenMatcher(); + GherkinDocument gherkinDocument = parser.parse(source, matcher); + GherkinDialectProvider dialectProvider = new GherkinDialectProvider(); + List pickles = compilePickles(gherkinDocument, dialectProvider, path); + return new GherkinVintageFeature(gherkinDocument, path, source, pickles); + } catch (ParserException e) { + throw new FeatureParserException("Failed to parse resource at: " + path.toString(), e); + } + } + + private static List compilePickles(GherkinDocument document, GherkinDialectProvider dialectProvider, URI path) { + if (document.getFeature() == null) { + return Collections.emptyList(); + } + String language = document.getFeature().getLanguage(); + GherkinDialect dialect = dialectProvider.getDialect(language, null); + return new Compiler().compile(document) + .stream() + .map(pickle -> new GherkinVintagePickle(pickle, path, document, dialect)) + .collect(Collectors.toList()); + } + + @Override + public Feature parse(URI path, String source, Supplier idGenerator) { + return parseGherkin5(path, source); + } + + @Override + public String version() { + return "5"; + } +} diff --git a/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageLocation.java b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageLocation.java new file mode 100644 index 0000000000..3f7467ae83 --- /dev/null +++ b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageLocation.java @@ -0,0 +1,49 @@ +package io.cucumber.core.gherkin.vintage; + +import gherkin.pickles.PickleLocation; +import io.cucumber.core.gherkin.Location; + +import java.util.Objects; + +final class GherkinVintageLocation implements Location { + + private final int line; + private final int column; + + private GherkinVintageLocation(int line, int column) { + this.line = line; + this.column = column; + } + + static Location from(PickleLocation location) { + return new GherkinVintageLocation(location.getLine(), location.getColumn()); + } + + public static Location from(gherkin.ast.Location location) { + return new GherkinVintageLocation(location.getLine(), location.getColumn()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GherkinVintageLocation that = (GherkinVintageLocation) o; + return line == that.line && + column == that.column; + } + + @Override + public int getLine() { + return line; + } + + @Override + public int getColumn() { + return column; + } + + @Override + public int hashCode() { + return Objects.hash(line, column); + } +} diff --git a/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintagePickle.java b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintagePickle.java new file mode 100644 index 0000000000..ef901b0d0e --- /dev/null +++ b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintagePickle.java @@ -0,0 +1,114 @@ +package io.cucumber.core.gherkin.vintage; + +import gherkin.GherkinDialect; +import gherkin.ast.GherkinDocument; +import gherkin.ast.ScenarioDefinition; +import gherkin.pickles.PickleStep; +import gherkin.pickles.PickleTag; +import io.cucumber.core.gherkin.Location; +import io.cucumber.core.gherkin.Pickle; +import io.cucumber.core.gherkin.Step; +import io.cucumber.core.gherkin.StepType; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static io.cucumber.core.gherkin.vintage.GherkinVintageLocation.from; +import static java.util.stream.Collectors.toList; + +/** + * Wraps {@link gherkin.pickles.Pickle} to avoid exposing the gherkin library to all of + * Cucumber. + */ +final class GherkinVintagePickle implements Pickle { + + private final gherkin.pickles.Pickle pickle; + private final List steps; + private final URI uri; + private final String keyWord; + + GherkinVintagePickle(gherkin.pickles.Pickle pickle, URI uri, GherkinDocument document, GherkinDialect dialect) { + this.pickle = pickle; + this.uri = uri; + this.steps = createCucumberSteps(pickle, document, dialect, uri.toString()); + this.keyWord = document.getFeature().getChildren().stream() + .filter(scenarioDefinition -> scenarioDefinition.getLocation().getLine() == getScenarioLocation().getLine()) + .map(ScenarioDefinition::getKeyword) + .findFirst() + .orElse("Scenario"); + } + + private static List createCucumberSteps(gherkin.pickles.Pickle pickle, GherkinDocument document, GherkinDialect dialect, String uri) { + List list = new ArrayList<>(); + String previousGivenWhenThen = dialect.getGivenKeywords() + .stream() + .filter(s -> !StepType.isAstrix(s)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("No Given keyword for dialect: " + dialect.getName())); + + for (PickleStep step : pickle.getSteps()) { + Step cucumberStep = new GherkinVintageStep(step, document, dialect, previousGivenWhenThen, uri); + if (cucumberStep.getType().isGivenWhenThen()) { + previousGivenWhenThen = cucumberStep.getKeyWord(); + } + list.add(cucumberStep); + } + return list; + } + + @Override + public String getKeyword() { + return keyWord; + } + + @Override + public String getLanguage() { + return pickle.getLanguage(); + } + + @Override + public String getName() { + return pickle.getName(); + } + + + @Override + public Location getLocation() { + return from(pickle.getLocations().get(0)); + } + + @Override + public Location getScenarioLocation() { + int last = pickle.getLocations().size() - 1; + return from(pickle.getLocations().get(last)); + } + + @Override + public List getSteps() { + return steps; + } + + @Override + public List getTags() { + return pickle.getTags().stream() + .map(PickleTag::getName) + .collect(toList()); + } + + @Override + public URI getUri() { + return uri; + } + + @Override + public String getId() { + return pickle.getName() + ":" + pickle.getLocations() + .stream() + .map(l -> String.valueOf(l.getLine())) + .collect(Collectors.joining(":")); + } + + +} diff --git a/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageScenario.java b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageScenario.java new file mode 100644 index 0000000000..b6f17c0820 --- /dev/null +++ b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageScenario.java @@ -0,0 +1,31 @@ +package io.cucumber.core.gherkin.vintage; + +import gherkin.ast.ScenarioDefinition; +import io.cucumber.core.gherkin.Location; +import io.cucumber.core.gherkin.Scenario; + +import static io.cucumber.core.gherkin.vintage.GherkinVintageLocation.from; + +final class GherkinVintageScenario implements Scenario { + + private final ScenarioDefinition scenarioDefinition; + + GherkinVintageScenario(ScenarioDefinition scenarioDefinition) { + this.scenarioDefinition = scenarioDefinition; + } + + @Override + public Location getLocation() { + return from(scenarioDefinition.getLocation()); + } + + @Override + public String getKeyWord() { + return scenarioDefinition.getKeyword(); + } + + @Override + public String getName() { + return scenarioDefinition.getName(); + } +} diff --git a/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageScenarioOutline.java b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageScenarioOutline.java new file mode 100644 index 0000000000..8fa8bb5ff7 --- /dev/null +++ b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageScenarioOutline.java @@ -0,0 +1,39 @@ +package io.cucumber.core.gherkin.vintage; + +import io.cucumber.core.gherkin.Examples; +import io.cucumber.core.gherkin.Location; +import io.cucumber.core.gherkin.ScenarioOutline; + +import java.util.stream.Stream; + +import static io.cucumber.core.gherkin.vintage.GherkinVintageLocation.from; + +final class GherkinVintageScenarioOutline implements ScenarioOutline { + + private final gherkin.ast.ScenarioOutline scenarioOutline; + + GherkinVintageScenarioOutline(gherkin.ast.ScenarioOutline scenarioOutline) { + this.scenarioOutline = scenarioOutline; + } + + @Override + public Stream children() { + return scenarioOutline.getExamples().stream() + .map(GherkinVintageExamples::new); + } + + @Override + public Location getLocation() { + return from(scenarioOutline.getLocation()); + } + + @Override + public String getKeyWord() { + return scenarioOutline.getKeyword(); + } + + @Override + public String getName() { + return scenarioOutline.getName(); + } +} diff --git a/core/src/main/java/io/cucumber/core/feature/CucumberStep.java b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageStep.java similarity index 72% rename from core/src/main/java/io/cucumber/core/feature/CucumberStep.java rename to gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageStep.java index d462f92355..081cbd5395 100644 --- a/core/src/main/java/io/cucumber/core/feature/CucumberStep.java +++ b/gherkin-vintage/src/main/java/io/cucumber/core/gherkin/vintage/GherkinVintageStep.java @@ -1,34 +1,40 @@ -package io.cucumber.core.feature; +package io.cucumber.core.gherkin.vintage; import gherkin.GherkinDialect; import gherkin.ast.GherkinDocument; -import gherkin.ast.Step; import gherkin.pickles.PickleStep; import gherkin.pickles.PickleString; import gherkin.pickles.PickleTable; +import io.cucumber.core.gherkin.Argument; +import io.cucumber.core.gherkin.Step; +import io.cucumber.core.gherkin.StepType; -public final class CucumberStep implements io.cucumber.plugin.event.CucumberStep { +import java.util.stream.Collectors; + +final class GherkinVintageStep implements Step { private final PickleStep step; private final Argument argument; private final String keyWord; private final StepType stepType; private final String previousGwtKeyWord; + private final String uri; - CucumberStep(PickleStep step, GherkinDocument document, GherkinDialect dialect, String previousGwtKeyWord) { + GherkinVintageStep(PickleStep step, GherkinDocument document, GherkinDialect dialect, String previousGwtKeyWord, String uri) { this.step = step; this.argument = extractArgument(step); this.keyWord = extractKeyWord(document); this.stepType = extractKeyWordType(keyWord, dialect); this.previousGwtKeyWord = previousGwtKeyWord; + this.uri = uri; } private String extractKeyWord(GherkinDocument document) { return document.getFeature().getChildren().stream() .flatMap(scenarioDefinition -> scenarioDefinition.getSteps().stream()) - .filter(step -> step.getLocation().getLine() == getStepLine()) + .filter(step -> step.getLocation().getLine() == getLine()) .findFirst() - .map(Step::getKeyword) + .map(gherkin.ast.Step::getKeyword) .orElseThrow(() -> new IllegalStateException("GherkinDocument did not contain PickleStep")); } @@ -61,17 +67,17 @@ private Argument extractArgument(PickleStep pickleStep) { gherkin.pickles.Argument argument = pickleStep.getArgument().get(0); if (argument instanceof PickleString) { PickleString docString = (PickleString) argument; - return new DocStringArgument(docString); + return new GherkinVintageDocStringArgument(docString); } if (argument instanceof PickleTable) { PickleTable table = (PickleTable) argument; - return new DataTableArgument(table); + return new GherkinVintageDataTableArgument(table); } return null; } @Override - public int getStepLine() { + public int getLine() { int last = step.getLocations().size() - 1; return step.getLocations().get(last).getLine(); } @@ -86,10 +92,12 @@ public String getKeyWord() { return keyWord; } - public StepType getStepType() { + @Override + public StepType getType() { return stepType; } + @Override public String getPreviousGivenWhenThenKeyWord() { return previousGwtKeyWord; } @@ -98,4 +106,12 @@ public String getPreviousGivenWhenThenKeyWord() { public String getText() { return step.getText(); } + + @Override + public String getId() { + String lineNumbers = this.step.getLocations().stream() + .map(s -> String.valueOf(s.getLine())) + .collect(Collectors.joining(":")); + return uri + ":" + lineNumbers; + } } diff --git a/gherkin-vintage/src/main/resources/META-INF/services/io.cucumber.core.gherkin.FeatureParser b/gherkin-vintage/src/main/resources/META-INF/services/io.cucumber.core.gherkin.FeatureParser new file mode 100644 index 0000000000..00e08a804b --- /dev/null +++ b/gherkin-vintage/src/main/resources/META-INF/services/io.cucumber.core.gherkin.FeatureParser @@ -0,0 +1 @@ +io.cucumber.core.gherkin.vintage.GherkinVintageFeatureParser diff --git a/gherkin/pom.xml b/gherkin/pom.xml new file mode 100644 index 0000000000..96ff8f8040 --- /dev/null +++ b/gherkin/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + io.cucumber + cucumber-jvm + 5.0.0-RC3-SNAPSHOT + + + + io.cucumber.core.gherkin + + + cucumber-gherkin + jar + Cucumber-JVM: Gherkin + + + + io.cucumber + cucumber-plugin + + + + diff --git a/core/src/main/java/io/cucumber/core/feature/Argument.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Argument.java similarity index 74% rename from core/src/main/java/io/cucumber/core/feature/Argument.java rename to gherkin/src/main/java/io/cucumber/core/gherkin/Argument.java index d61c9b864b..776570972b 100644 --- a/core/src/main/java/io/cucumber/core/feature/Argument.java +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Argument.java @@ -1,4 +1,4 @@ -package io.cucumber.core.feature; +package io.cucumber.core.gherkin; import io.cucumber.plugin.event.StepArgument; diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/Container.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Container.java new file mode 100644 index 0000000000..147cd0f57e --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Container.java @@ -0,0 +1,8 @@ +package io.cucumber.core.gherkin; + +import java.util.stream.Stream; + +public interface Container { + + Stream children(); +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/DataTableArgument.java b/gherkin/src/main/java/io/cucumber/core/gherkin/DataTableArgument.java new file mode 100644 index 0000000000..3c239fdbe1 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/DataTableArgument.java @@ -0,0 +1,9 @@ +package io.cucumber.core.gherkin; + +import java.util.List; + +public interface DataTableArgument extends Argument, io.cucumber.plugin.event.DataTableArgument { + List> cells(); + + int getLine(); +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/DocStringArgument.java b/gherkin/src/main/java/io/cucumber/core/gherkin/DocStringArgument.java new file mode 100644 index 0000000000..6a4ecfaa98 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/DocStringArgument.java @@ -0,0 +1,9 @@ +package io.cucumber.core.gherkin; + +public interface DocStringArgument extends Argument, io.cucumber.plugin.event.DocStringArgument { + String getContent(); + + String getContentType(); + + int getLine(); +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/Example.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Example.java new file mode 100644 index 0000000000..51d26146b8 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Example.java @@ -0,0 +1,4 @@ +package io.cucumber.core.gherkin; + +public interface Example extends Node { +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/Examples.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Examples.java new file mode 100644 index 0000000000..6725a53174 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Examples.java @@ -0,0 +1,4 @@ +package io.cucumber.core.gherkin; + +public interface Examples extends Node, Container { +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/Feature.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Feature.java new file mode 100644 index 0000000000..4aee75b7e2 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Feature.java @@ -0,0 +1,19 @@ +package io.cucumber.core.gherkin; + +import java.net.URI; +import java.util.List; +import java.util.Optional; + +public interface Feature extends Node, Container { + + String getKeyword(); + + Optional getPickleAt(Located located); + + List getPickles(); + + URI getUri(); + + String getSource(); + +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParser.java b/gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParser.java new file mode 100644 index 0000000000..f4d6b9b625 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParser.java @@ -0,0 +1,13 @@ +package io.cucumber.core.gherkin; + +import java.net.URI; +import java.util.UUID; +import java.util.function.Supplier; + +public interface FeatureParser { + + Feature parse(URI path, String source, Supplier idGenerator); + + String version(); + +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParserException.java b/gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParserException.java new file mode 100644 index 0000000000..8b598e8d23 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParserException.java @@ -0,0 +1,16 @@ +package io.cucumber.core.gherkin; + +public final class FeatureParserException extends RuntimeException { + + public FeatureParserException(String message) { + super(message); + } + + public FeatureParserException(String message, Throwable cause) { + super(message, cause); + } + + public FeatureParserException(Throwable cause) { + super(cause); + } +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/Located.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Located.java new file mode 100644 index 0000000000..9010f17976 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Located.java @@ -0,0 +1,6 @@ +package io.cucumber.core.gherkin; + +public interface Located { + + Location getLocation(); +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/Location.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Location.java new file mode 100644 index 0000000000..a1dea8b1bd --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Location.java @@ -0,0 +1,8 @@ +package io.cucumber.core.gherkin; + +public interface Location { + + int getLine(); + + int getColumn(); +} diff --git a/core/src/main/java/io/cucumber/core/feature/Named.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Named.java similarity index 69% rename from core/src/main/java/io/cucumber/core/feature/Named.java rename to gherkin/src/main/java/io/cucumber/core/gherkin/Named.java index b7b26c6295..21d7e6e4ad 100644 --- a/core/src/main/java/io/cucumber/core/feature/Named.java +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Named.java @@ -1,4 +1,4 @@ -package io.cucumber.core.feature; +package io.cucumber.core.gherkin; public interface Named { diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/Node.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Node.java new file mode 100644 index 0000000000..a8c3bb6ee3 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Node.java @@ -0,0 +1,4 @@ +package io.cucumber.core.gherkin; + +public interface Node extends Located, Named { +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/Pickle.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Pickle.java new file mode 100644 index 0000000000..a76a69c4ae --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Pickle.java @@ -0,0 +1,40 @@ +package io.cucumber.core.gherkin; + +import java.net.URI; +import java.util.List; + +public interface Pickle { + + String getKeyword(); + + String getLanguage(); + + String getName(); + + /** + * Returns the location in feature file of the Scenario this pickle was created + * from. If this pickle was created from a Scenario Outline this location is the + * location in the Example section used to fill in the place holders. + * + * @return location in the feature file + */ + Location getLocation(); + + + /** + * Returns the location in feature file of the Scenario this pickle was created + * from. If this pickle was created from a Scenario Outline this location is + * that of the Scenario + * + * @return location in the feature file + */ + Location getScenarioLocation(); + + List getSteps(); + + List getTags(); + + URI getUri(); + + String getId(); +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/Rule.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Rule.java new file mode 100644 index 0000000000..885438ee3a --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Rule.java @@ -0,0 +1,4 @@ +package io.cucumber.core.gherkin; + +public interface Rule extends Node, Container { +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/Scenario.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Scenario.java new file mode 100644 index 0000000000..cbd2a88eb7 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Scenario.java @@ -0,0 +1,4 @@ +package io.cucumber.core.gherkin; + +public interface Scenario extends ScenarioDefinition { +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/ScenarioDefinition.java b/gherkin/src/main/java/io/cucumber/core/gherkin/ScenarioDefinition.java new file mode 100644 index 0000000000..a4fc7a42e5 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/ScenarioDefinition.java @@ -0,0 +1,4 @@ +package io.cucumber.core.gherkin; + +public interface ScenarioDefinition extends Node { +} diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/ScenarioOutline.java b/gherkin/src/main/java/io/cucumber/core/gherkin/ScenarioOutline.java new file mode 100644 index 0000000000..77dbb907e2 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/ScenarioOutline.java @@ -0,0 +1,5 @@ +package io.cucumber.core.gherkin; + +public interface ScenarioOutline extends Node, Container { + +} \ No newline at end of file diff --git a/gherkin/src/main/java/io/cucumber/core/gherkin/Step.java b/gherkin/src/main/java/io/cucumber/core/gherkin/Step.java new file mode 100644 index 0000000000..665b0972f3 --- /dev/null +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/Step.java @@ -0,0 +1,18 @@ +package io.cucumber.core.gherkin; + +public interface Step extends io.cucumber.plugin.event.Step { + + int getLine(); + + Argument getArgument(); + + String getKeyWord(); + + StepType getType(); + + String getPreviousGivenWhenThenKeyWord(); + + String getText(); + + String getId(); +} diff --git a/core/src/main/java/io/cucumber/core/feature/StepType.java b/gherkin/src/main/java/io/cucumber/core/gherkin/StepType.java similarity index 76% rename from core/src/main/java/io/cucumber/core/feature/StepType.java rename to gherkin/src/main/java/io/cucumber/core/gherkin/StepType.java index 703681f713..b667f8a01f 100644 --- a/core/src/main/java/io/cucumber/core/feature/StepType.java +++ b/gherkin/src/main/java/io/cucumber/core/gherkin/StepType.java @@ -1,4 +1,4 @@ -package io.cucumber.core.feature; +package io.cucumber.core.gherkin; public enum StepType { GIVEN, WHEN, THEN, AND, BUT, OTHER; @@ -9,7 +9,7 @@ public boolean isGivenWhenThen() { return this == GIVEN || this == WHEN || this == THEN; } - static boolean isAstrix(String stepType) { + public static boolean isAstrix(String stepType) { return ASTRIX_KEY_WORD.equals(stepType); } } diff --git a/java/src/main/groovy/generate-annotations.groovy b/java/src/main/groovy/generate-annotations.groovy index 202534b101..269a55d866 100644 --- a/java/src/main/groovy/generate-annotations.groovy +++ b/java/src/main/groovy/generate-annotations.groovy @@ -11,7 +11,7 @@ static def normalize(s) { if (System.getProperty("java.version").startsWith("1.6")) { return s } else { - return Normalizer.normalize(s, Normalizer.Form.NFC) + return Normalizer.normalize(s, Normalizer.Form.NFC) } } @@ -42,4 +42,4 @@ GherkinDialectProvider.DIALECTS.keySet().each { language -> def file = new File(project.baseDir, "target/generated-sources/i18n/java/io/cucumber/java/${normalized_language}/package-info.java") file.write(html, "UTF-8") } -} \ No newline at end of file +} diff --git a/java/src/main/java/io/cucumber/java/GlueAdaptor.java b/java/src/main/java/io/cucumber/java/GlueAdaptor.java index fdf1ae9dc7..e9084cd704 100644 --- a/java/src/main/java/io/cucumber/java/GlueAdaptor.java +++ b/java/src/main/java/io/cucumber/java/GlueAdaptor.java @@ -65,7 +65,8 @@ private static String expression(Annotation annotation) { try { Method expressionMethod = annotation.getClass().getMethod("value"); return (String) Invoker.invoke(annotation, expressionMethod); - } catch (Throwable e) { + } catch (NoSuchMethodException e) { + // Should never happen. throw new IllegalStateException(e); } } diff --git a/java/src/main/java/io/cucumber/java/MethodScanner.java b/java/src/main/java/io/cucumber/java/MethodScanner.java index 8ddd26ced2..abbe7c558c 100644 --- a/java/src/main/java/io/cucumber/java/MethodScanner.java +++ b/java/src/main/java/io/cucumber/java/MethodScanner.java @@ -86,7 +86,8 @@ private static Annotation[] repeatedAnnotations(Annotation annotation) { try { Method expressionMethod = annotation.getClass().getMethod("value"); return (Annotation[]) Invoker.invoke(annotation, expressionMethod); - } catch (Throwable e) { + } catch (NoSuchMethodException e) { + // Should never happen. throw new IllegalStateException(e); } } diff --git a/java/src/test/java/io/cucumber/java/JavaSnippetTest.java b/java/src/test/java/io/cucumber/java/JavaSnippetTest.java index 1bc9b14846..53071f518e 100644 --- a/java/src/test/java/io/cucumber/java/JavaSnippetTest.java +++ b/java/src/test/java/io/cucumber/java/JavaSnippetTest.java @@ -1,7 +1,7 @@ package io.cucumber.java; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.snippets.SnippetGenerator; import io.cucumber.core.snippets.SnippetType; import io.cucumber.cucumberexpressions.ParameterType; @@ -289,7 +289,7 @@ void generatesSnippetDefaultsToGiven() { } private String snippetFor(String stepText) { - CucumberStep step = createStep(stepText); + Step step = createStep(stepText); List snippet = new SnippetGenerator(new JavaSnippet(), new ParameterTypeRegistry(Locale.ENGLISH)).getSnippet(step, snippetType); return String.join("\n", snippet); } @@ -301,8 +301,8 @@ private String snippetForWhenAnd(String stepText) { " When some other step\n" + " And " + stepText + "\n"; - CucumberFeature feature = TestFeatureParser.parse(source); - CucumberStep step = feature.getPickles().get(0).getSteps().get(1); + Feature feature = TestFeatureParser.parse(source); + Step step = feature.getPickles().get(0).getSteps().get(1); List snippet = new SnippetGenerator(new JavaSnippet(), new ParameterTypeRegistry(Locale.ENGLISH)).getSnippet(step, snippetType); return String.join("\n", snippet); } @@ -312,15 +312,15 @@ private String snippetForWildCard(String stepText) { "Feature: Test feature\n" + " Scenario: Test Scenario\n" + " * " + stepText + "\n"; - CucumberFeature feature = TestFeatureParser.parse(source); - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + Feature feature = TestFeatureParser.parse(source); + Step step = feature.getPickles().get(0).getSteps().get(0); List snippet = new SnippetGenerator(new JavaSnippet(), new ParameterTypeRegistry(Locale.ENGLISH)).getSnippet(step, snippetType); return String.join("\n", snippet); } private String snippetFor(String stepText, ParameterType parameterType) { - CucumberStep step = createStep(stepText); + Step step = createStep(stepText); ParameterTypeRegistry parameterTypeRegistry = new ParameterTypeRegistry(Locale.ENGLISH); parameterTypeRegistry.defineParameterType(parameterType); List snippet = new SnippetGenerator(new JavaSnippet(), parameterTypeRegistry).getSnippet(step, snippetType); @@ -329,14 +329,14 @@ private String snippetFor(String stepText, ParameterType parameterType) { private String snippetForDocString(String stepText, String docString) { - CucumberStep step = createStepWithDocString(stepText, docString); + Step step = createStepWithDocString(stepText, docString); List snippet = new SnippetGenerator(new JavaSnippet(), new ParameterTypeRegistry(Locale.ENGLISH)).getSnippet(step, snippetType); return String.join("\n", snippet); } private String snippetForDocString(String stepText, String docString, ParameterType parameterType) { - CucumberStep step = createStepWithDocString(stepText, docString); + Step step = createStepWithDocString(stepText, docString); ParameterTypeRegistry parameterTypeRegistry = new ParameterTypeRegistry(Locale.ENGLISH); parameterTypeRegistry.defineParameterType(parameterType); List snippet = new SnippetGenerator(new JavaSnippet(), parameterTypeRegistry).getSnippet(step, snippetType); @@ -345,31 +345,31 @@ private String snippetForDocString(String stepText, String docString, ParameterT private String snippetForDataTable(String stepText) { - CucumberStep step = createStepWithDataTable(stepText); + Step step = createStepWithDataTable(stepText); List snippet = new SnippetGenerator(new JavaSnippet(), new ParameterTypeRegistry(Locale.ENGLISH)).getSnippet(step, snippetType); return String.join("\n", snippet); } private String snippetForDataTable(String stepText, ParameterType parameterType) { - CucumberStep step = createStepWithDataTable(stepText); + Step step = createStepWithDataTable(stepText); ParameterTypeRegistry parameterTypeRegistry = new ParameterTypeRegistry(Locale.ENGLISH); parameterTypeRegistry.defineParameterType(parameterType); List snippet = new SnippetGenerator(new JavaSnippet(), parameterTypeRegistry).getSnippet(step, snippetType); return String.join("\n", snippet); } - private CucumberStep createStep(String stepText) { + private Step createStep(String stepText) { String source = "" + "Feature: Test feature\n" + " Scenario: Test Scenario\n" + " Given " + stepText + "\n"; - CucumberFeature feature = TestFeatureParser.parse(source); + Feature feature = TestFeatureParser.parse(source); return feature.getPickles().get(0).getSteps().get(0); } - private CucumberStep createStepWithDocString(String stepText, String docString) { + private Step createStepWithDocString(String stepText, String docString) { String source = "" + "Feature: Test feature\n" + " Scenario: Test Scenario\n" + @@ -378,11 +378,11 @@ private CucumberStep createStepWithDocString(String stepText, String docString) " " + docString + "\n" + " \"\"\""; - CucumberFeature feature = TestFeatureParser.parse(source); + Feature feature = TestFeatureParser.parse(source); return feature.getPickles().get(0).getSteps().get(0); } - private CucumberStep createStepWithDataTable(String stepText) { + private Step createStepWithDataTable(String stepText) { String source = "" + "Feature: Test feature\n" + " Scenario: Test Scenario\n" + @@ -390,7 +390,7 @@ private CucumberStep createStepWithDataTable(String stepText) { " | key | \n" + " | value | \n"; - CucumberFeature feature = TestFeatureParser.parse(source); + Feature feature = TestFeatureParser.parse(source); return feature.getPickles().get(0).getSteps().get(0); } diff --git a/java/src/test/java/io/cucumber/java/TestFeatureParser.java b/java/src/test/java/io/cucumber/java/TestFeatureParser.java index e649be11a0..a4285dcc25 100644 --- a/java/src/test/java/io/cucumber/java/TestFeatureParser.java +++ b/java/src/test/java/io/cucumber/java/TestFeatureParser.java @@ -1,6 +1,6 @@ package io.cucumber.java; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.feature.FeatureIdentifier; import io.cucumber.core.feature.FeatureParser; import io.cucumber.core.resource.Resource; @@ -9,18 +9,19 @@ import java.io.InputStream; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.util.UUID; class TestFeatureParser { - static CucumberFeature parse(final String source) { + static Feature parse(final String source) { return parse("file:test.feature", source); } - private static CucumberFeature parse(final String uri, final String source) { + private static Feature parse(final String uri, final String source) { return parse(FeatureIdentifier.parse(uri), source); } - private static CucumberFeature parse(final URI uri, final String source) { - return FeatureParser.parseResource(new Resource() { + private static Feature parse(final URI uri, final String source) { + return new FeatureParser(UUID::randomUUID).parseResource(new Resource() { @Override public URI getUri() { return uri; diff --git a/java8/src/test/java/io/cucumber/java8/Java8SnippetTest.java b/java8/src/test/java/io/cucumber/java8/Java8SnippetTest.java index 725648d363..75c930a622 100644 --- a/java8/src/test/java/io/cucumber/java8/Java8SnippetTest.java +++ b/java8/src/test/java/io/cucumber/java8/Java8SnippetTest.java @@ -1,7 +1,7 @@ package io.cucumber.java8; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.snippets.SnippetGenerator; import io.cucumber.core.snippets.SnippetType; import io.cucumber.cucumberexpressions.ParameterTypeRegistry; @@ -22,7 +22,7 @@ class Java8SnippetTest { @Test void generatesPlainSnippet() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my \"big\" belly\n" @@ -37,7 +37,7 @@ void generatesPlainSnippet() { @Test void generatesDataTableSnippet() { - CucumberFeature feature = TestFeatureParser.parse("" + + Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given I have 4 cukes in my \"big\" belly\n" + @@ -58,8 +58,8 @@ void generatesDataTableSnippet() { assertThat(getSnippet(feature), is(equalTo(expected))); } - private String getSnippet(CucumberFeature feature) { - CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + private String getSnippet(Feature feature) { + Step step = feature.getPickles().get(0).getSteps().get(0); return String.join( "\n", snippetGenerator.getSnippet(step, SnippetType.UNDERSCORE) diff --git a/java8/src/test/java/io/cucumber/java8/TestFeatureParser.java b/java8/src/test/java/io/cucumber/java8/TestFeatureParser.java index 2de0bcd6a4..3de4d41cef 100644 --- a/java8/src/test/java/io/cucumber/java8/TestFeatureParser.java +++ b/java8/src/test/java/io/cucumber/java8/TestFeatureParser.java @@ -1,26 +1,27 @@ package io.cucumber.java8; -import io.cucumber.core.feature.CucumberFeature; import io.cucumber.core.feature.FeatureIdentifier; import io.cucumber.core.feature.FeatureParser; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.resource.Resource; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.util.UUID; class TestFeatureParser { - static CucumberFeature parse(final String source) { + static Feature parse(final String source) { return parse("file:test.feature", source); } - private static CucumberFeature parse(final String uri, final String source) { + private static Feature parse(final String uri, final String source) { return parse(FeatureIdentifier.parse(uri), source); } - private static CucumberFeature parse(final URI uri, final String source) { - return FeatureParser.parseResource(new Resource() { + private static Feature parse(final URI uri, final String source) { + return new FeatureParser(UUID::randomUUID).parseResource(new Resource() { @Override public URI getUri() { return uri; diff --git a/junit-platform-engine/pom.xml b/junit-platform-engine/pom.xml index 32b08a27dc..e26622a94d 100644 --- a/junit-platform-engine/pom.xml +++ b/junit-platform-engine/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 @@ -73,14 +74,16 @@ - - - - - - - - + + + + + + + + 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 2a4f8552a1..f081c9bb1f 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 @@ -1,8 +1,8 @@ package io.cucumber.junit.platform.engine; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.plugin.PluginFactory; import io.cucumber.core.plugin.Plugins; import io.cucumber.core.runner.Runner; @@ -24,6 +24,7 @@ import org.junit.platform.engine.support.hierarchical.EngineExecutionContext; import java.time.Clock; +import java.util.UUID; import java.util.function.Supplier; class CucumberEngineExecutionContext implements EngineExecutionContext { @@ -40,7 +41,7 @@ class CucumberEngineExecutionContext implements EngineExecutionContext { ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(options); ObjectFactorySupplier objectFactorySupplier = new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader); BackendSupplier backendSupplier = new BackendServiceLoader(classLoader, objectFactorySupplier); - this.bus = new TimeServiceEventBus(Clock.systemUTC()); + this.bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier = new ScanningTypeRegistryConfigurerSupplier(classLoader, options); Plugins plugins = new Plugins(new PluginFactory(), options); if (options.isParallelExecutionEnabled()) { @@ -56,12 +57,12 @@ void startTestRun() { bus.send(new TestRunStarted(bus.getInstant())); } - void beforeFeature(CucumberFeature feature) { + void beforeFeature(Feature feature) { logger.debug(() -> "Sending test source read event for " + feature.getUri()); bus.send(new TestSourceRead(bus.getInstant(), feature.getUri(), feature.getSource())); } - void runTestCase(CucumberPickle pickle) { + void runTestCase(Pickle pickle) { Runner runner = getRunner(); try (TestCaseResultObserver observer = TestCaseResultObserver.observe(runner.getBus())) { logger.debug(() -> "Executing test case " + pickle.getName()); diff --git a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureDescriptor.java b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureDescriptor.java index 63344d4628..db45bb352f 100644 --- a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureDescriptor.java +++ b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureDescriptor.java @@ -1,12 +1,13 @@ package io.cucumber.junit.platform.engine; -import io.cucumber.core.feature.CucumberExample; -import io.cucumber.core.feature.CucumberExamples; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberScenario; -import io.cucumber.core.feature.CucumberScenarioOutline; -import io.cucumber.core.feature.Located; -import io.cucumber.core.feature.Named; +import io.cucumber.core.gherkin.Example; +import io.cucumber.core.gherkin.Examples; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Rule; +import io.cucumber.core.gherkin.Scenario; +import io.cucumber.core.gherkin.ScenarioOutline; +import io.cucumber.core.gherkin.Located; +import io.cucumber.core.gherkin.Named; import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.TestSource; import org.junit.platform.engine.UniqueId; @@ -15,29 +16,29 @@ class FeatureDescriptor extends AbstractTestDescriptor implements Node { - private final CucumberFeature feature; + private final Feature feature; - private FeatureDescriptor(UniqueId uniqueId, String name, TestSource source, CucumberFeature feature) { + private FeatureDescriptor(UniqueId uniqueId, String name, TestSource source, Feature feature) { super(uniqueId, name, source); this.feature = feature; } - static TestDescriptor create(CucumberFeature cucumberFeature, TestDescriptor parent) { - FeatureOrigin source = FeatureOrigin.fromUri(cucumberFeature.getUri()); + static TestDescriptor create(Feature feature, TestDescriptor parent) { + FeatureOrigin source = FeatureOrigin.fromUri(feature.getUri()); TestDescriptor descriptor = new FeatureDescriptor( - source.featureSegment(parent.getUniqueId(), cucumberFeature), - getNameOrKeyWord(cucumberFeature), + source.featureSegment(parent.getUniqueId(), feature), + getNameOrKeyWord(feature), source.featureSource(), - cucumberFeature + feature ); parent.addChild(descriptor); - cucumberFeature.children().forEach(scenarioDefinition -> visit(cucumberFeature, descriptor, source, scenarioDefinition)); + feature.children().forEach(scenarioDefinition -> visit(feature, descriptor, source, scenarioDefinition)); return descriptor; } - private static void visit(CucumberFeature feature, TestDescriptor parent, FeatureOrigin source, T node) { - if (node instanceof CucumberScenario) { - feature.getPickleAt(node.getLocation()) + private static void visit(Feature feature, TestDescriptor parent, FeatureOrigin source, T node) { + if (node instanceof Scenario) { + feature.getPickleAt(node) .ifPresent(pickle -> { PickleDescriptor descriptor = new PickleDescriptor( source.scenarioSegment(parent.getUniqueId(), node), @@ -49,30 +50,41 @@ private static void visit(CucumberFeature feature, T }); } - if (node instanceof CucumberScenarioOutline) { + if (node instanceof Rule) { + NodeDescriptor descriptor = new NodeDescriptor( + source.ruleSegment(parent.getUniqueId(), node), + getNameOrKeyWord(node), + source.nodeSource(node) + ); + parent.addChild(descriptor); + Rule rule = (Rule) node; + rule.children().forEach(section -> visit(feature, descriptor, source, section)); + } + + if (node instanceof ScenarioOutline) { NodeDescriptor descriptor = new NodeDescriptor( source.scenarioSegment(parent.getUniqueId(), node), getNameOrKeyWord(node), source.nodeSource(node) ); parent.addChild(descriptor); - CucumberScenarioOutline scenarioOutline = (CucumberScenarioOutline) node; + ScenarioOutline scenarioOutline = (ScenarioOutline) node; scenarioOutline.children().forEach(section -> visit(feature, descriptor, source, section)); } - if (node instanceof CucumberExamples) { + if (node instanceof Examples) { NodeDescriptor descriptor = new NodeDescriptor( source.examplesSegment(parent.getUniqueId(), node), getNameOrKeyWord(node), source.nodeSource(node) ); parent.addChild(descriptor); - CucumberExamples examples = (CucumberExamples) node; + Examples examples = (Examples) node; examples.children().forEach(example -> visit(feature, descriptor, source, example)); } - if (node instanceof CucumberExample) { - feature.getPickleAt(node.getLocation()) + if (node instanceof Example) { + feature.getPickleAt(node) .ifPresent(pickle -> { PickleDescriptor descriptor = new PickleDescriptor( source.exampleSegment(parent.getUniqueId(), node), @@ -86,7 +98,7 @@ private static void visit(CucumberFeature feature, T } - private static String getNameOrKeyWord(T node) { + private static String getNameOrKeyWord(T node) { String name = node.getName(); return name.isEmpty() ? node.getKeyWord() : name; } diff --git a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureOrigin.java b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureOrigin.java index 7deaf6fd07..c4a3c7705d 100644 --- a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureOrigin.java +++ b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureOrigin.java @@ -1,8 +1,8 @@ package io.cucumber.junit.platform.engine; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberLocation; -import io.cucumber.core.feature.Located; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Location; +import io.cucumber.core.gherkin.Located; import org.junit.platform.engine.TestSource; import org.junit.platform.engine.UniqueId; import org.junit.platform.engine.support.descriptor.ClasspathResourceSource; @@ -16,12 +16,13 @@ abstract class FeatureOrigin { + private static final String RULE_SEGMENT_TYPE = "rule"; private static final String FEATURE_SEGMENT_TYPE = "feature"; private static final String SCENARIO_SEGMENT_TYPE = "scenario"; private static final String EXAMPLES_SEGMENT_TYPE = "examples"; private static final String EXAMPLE_SEGMENT_TYPE = "example"; - private static FilePosition createFilePosition(CucumberLocation location) { + private static FilePosition createFilePosition(Location location) { return FilePosition.from(location.getLine(), location.getColumn()); } @@ -52,7 +53,11 @@ static boolean isFeatureSegment(UniqueId.Segment segment) { abstract TestSource nodeSource(Located node); - abstract UniqueId featureSegment(UniqueId parent, CucumberFeature feature); + abstract UniqueId featureSegment(UniqueId parent, Feature feature); + + UniqueId ruleSegment(UniqueId parent, Located rule){ + return parent.append(RULE_SEGMENT_TYPE, String.valueOf(rule.getLocation().getLine())); + } UniqueId scenarioSegment(UniqueId parent, Located scenarioDefinition) { return parent.append(SCENARIO_SEGMENT_TYPE, String.valueOf(scenarioDefinition.getLocation().getLine())); @@ -85,7 +90,7 @@ TestSource nodeSource(Located node) { } @Override - UniqueId featureSegment(UniqueId parent, CucumberFeature feature) { + UniqueId featureSegment(UniqueId parent, Feature feature) { return parent.append(FEATURE_SEGMENT_TYPE, source.getUri().toString()); } } @@ -109,7 +114,7 @@ TestSource nodeSource(Located node) { } @Override - UniqueId featureSegment(UniqueId parent, CucumberFeature feature) { + UniqueId featureSegment(UniqueId parent, Feature feature) { return parent.append(FEATURE_SEGMENT_TYPE, source.getUri().toString()); } } @@ -133,7 +138,7 @@ TestSource nodeSource(Located node) { } @Override - UniqueId featureSegment(UniqueId parent, CucumberFeature feature) { + UniqueId featureSegment(UniqueId parent, Feature feature) { return parent.append(FEATURE_SEGMENT_TYPE, feature.getUri().toString()); } } diff --git a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureResolver.java b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureResolver.java index 962cb3e07f..ea19151377 100644 --- a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureResolver.java +++ b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureResolver.java @@ -1,11 +1,10 @@ package io.cucumber.junit.platform.engine; -import io.cucumber.core.feature.CucumberFeature; import io.cucumber.core.feature.FeatureIdentifier; +import io.cucumber.core.feature.FeatureParser; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.resource.ClassLoaders; import io.cucumber.core.resource.ResourceScanner; -import org.junit.platform.commons.logging.Logger; -import org.junit.platform.commons.logging.LoggerFactory; import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.UniqueId; import org.junit.platform.engine.discovery.ClassSelector; @@ -22,21 +21,19 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.function.Predicate; import java.util.stream.Stream; -import static io.cucumber.core.feature.FeatureParser.parseResource; -import static java.lang.String.format; import static java.util.Optional.of; -import static org.junit.platform.commons.util.BlacklistedExceptions.rethrowIfBlacklisted; final class FeatureResolver { - private static final Logger logger = LoggerFactory.getLogger(FeatureResolver.class); - private final ResourceScanner featureScanner = new ResourceScanner<>( + private final FeatureParser featureParser = new FeatureParser(UUID::randomUUID); + private final ResourceScanner featureScanner = new ResourceScanner<>( ClassLoaders::getDefaultClassLoader, FeatureIdentifier::isFeature, - resource -> of(parseResource(resource)) + resource -> of(featureParser.parseResource(resource)) ); private final TestDescriptor engineDescriptor; @@ -123,14 +120,8 @@ void resolveClasspathRoot(ClasspathRootSelector selector) { void resolveUri(UriSelector selector) { URI uri = selector.getUri(); - - try { - resolveUri(uri) - .forEach(this::merge); - } catch (Throwable e) { - rethrowIfBlacklisted(e); - logger.debug(e, () -> format("Failed to resolve features for uri '%s'.", uri)); - } + resolveUri(uri) + .forEach(this::merge); } private Stream resolveUri(URI uri) { @@ -153,7 +144,7 @@ void resolveUniqueId(UniqueIdSelector uniqueIdSelector) { .map(UniqueId.Segment::getValue) .map(URI::create) .flatMap(this::resolveUri) - .map(descriptor -> pruneDescription(descriptor, uniqueIdSelector.getUniqueId())) + .map(descriptor -> pruneDescription(descriptor, uniqueId)) .forEach(this::merge); } @@ -172,7 +163,7 @@ private void pruneDescriptionRecursively(TestDescriptor descriptor, UniqueId toK children.forEach(child -> pruneDescriptionRecursively(child, toKeep)); } - private TestDescriptor resolveFeature(CucumberFeature feature) { + private TestDescriptor resolveFeature(Feature feature) { return FeatureDescriptor.create(feature, engineDescriptor); } diff --git a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/PickleDescriptor.java b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/PickleDescriptor.java index 45548e9135..947225c083 100644 --- a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/PickleDescriptor.java +++ b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/PickleDescriptor.java @@ -1,6 +1,6 @@ package io.cucumber.junit.platform.engine; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.resource.ClasspathSupport; import org.junit.platform.engine.TestSource; import org.junit.platform.engine.TestTag; @@ -13,16 +13,15 @@ import java.util.LinkedHashSet; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.toCollection; class PickleDescriptor extends AbstractTestDescriptor implements Node { - private final CucumberPickle pickleEvent; + private final Pickle pickleEvent; - PickleDescriptor(UniqueId uniqueId, String name, TestSource source, CucumberPickle pickleEvent) { + PickleDescriptor(UniqueId uniqueId, String name, TestSource source, Pickle pickleEvent) { super(uniqueId, name, source); this.pickleEvent = pickleEvent; } diff --git a/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/FeatureResolverTest.java b/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/FeatureResolverTest.java index 3eda01822d..8b29e9b55c 100644 --- a/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/FeatureResolverTest.java +++ b/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/FeatureResolverTest.java @@ -138,4 +138,4 @@ private TestDescriptor getOutline() { private TestDescriptor getExample() { return getOutline().getChildren().iterator().next().getChildren().iterator().next(); } -} \ No newline at end of file +} diff --git a/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/StubBackendProviderService.java b/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/StubBackendProviderService.java index ada632374f..3bd2d33abb 100644 --- a/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/StubBackendProviderService.java +++ b/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/StubBackendProviderService.java @@ -63,7 +63,7 @@ public boolean isDefinedAt(StackTraceElement stackTraceElement) { @Override public String getLocation() { - return null; + return "stubbed location"; } @Override diff --git a/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/TestCaseResultObserverTest.java b/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/TestCaseResultObserverTest.java index c47b6babd7..30316b80ff 100644 --- a/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/TestCaseResultObserverTest.java +++ b/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/TestCaseResultObserverTest.java @@ -3,7 +3,7 @@ import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.runtime.TimeServiceEventBus; import io.cucumber.plugin.event.Argument; -import io.cucumber.plugin.event.CucumberStep; +import io.cucumber.plugin.event.Step; import io.cucumber.plugin.event.PickleStepTestStep; import io.cucumber.plugin.event.Result; import io.cucumber.plugin.event.SnippetsSuggestedEvent; @@ -18,13 +18,13 @@ import org.junit.jupiter.api.Test; import org.opentest4j.AssertionFailedError; import org.opentest4j.TestAbortedException; -import org.opentest4j.TestSkippedException; import java.net.URI; import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.util.List; +import java.util.UUID; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -35,7 +35,7 @@ class TestCaseResultObserverTest { private final URI uri = URI.create("classpath:io/cucumber/junit/platform/engine.feature"); - private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC()); + private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); private final TestCaseResultObserver observer = TestCaseResultObserver.observe(bus); private final TestCase testCase = new TestCase() { @@ -44,6 +44,11 @@ public Integer getLine() { return 12; } + @Override + public String getKeyword() { + return "Scenario"; + } + @Override public String getName() { return "Mocked test case"; @@ -68,9 +73,14 @@ public List getTestSteps() { public URI getUri() { return uri; } + + @Override + public UUID getId() { + return UUID.randomUUID(); + } }; private PickleStepTestStep testStep = new PickleStepTestStep() { - CucumberStep cucumberStep = new CucumberStep() { + Step step = new Step() { @Override public StepArgument getArgument() { return null; @@ -87,7 +97,7 @@ public String getText() { } @Override - public int getStepLine() { + public int getLine() { return 15; } }; @@ -98,8 +108,8 @@ public String getPattern() { } @Override - public CucumberStep getStep() { - return cucumberStep; + public Step getStep() { + return step; } @Override @@ -109,12 +119,12 @@ public List getDefinitionArgument() { @Override public StepArgument getStepArgument() { - return cucumberStep.getArgument(); + return step.getArgument(); } @Override public int getStepLine() { - return cucumberStep.getStepLine(); + return step.getLine(); } @Override @@ -124,7 +134,7 @@ public URI getUri() { @Override public String getStepText() { - return cucumberStep.getText(); + return step.getText(); } @Override diff --git a/junit-platform-engine/src/test/resources/junit-platform.properties b/junit-platform-engine/src/test/resources/junit-platform.properties new file mode 100644 index 0000000000..280cc91282 --- /dev/null +++ b/junit-platform-engine/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +cucumber.glue=io.cucumber.junit.platform.engine \ No newline at end of file diff --git a/junit/src/main/java/io/cucumber/junit/Cucumber.java b/junit/src/main/java/io/cucumber/junit/Cucumber.java index 606bc4ccff..0606ac87ee 100644 --- a/junit/src/main/java/io/cucumber/junit/Cucumber.java +++ b/junit/src/main/java/io/cucumber/junit/Cucumber.java @@ -1,10 +1,10 @@ package io.cucumber.junit; -import io.cucumber.core.cli.Main; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.feature.FeatureParser; import io.cucumber.core.filter.Filters; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.logging.Logger; import io.cucumber.core.logging.LoggerFactory; import io.cucumber.core.options.Constants; @@ -41,6 +41,7 @@ import java.time.Clock; import java.util.List; +import java.util.UUID; import java.util.function.Predicate; import java.util.function.Supplier; @@ -86,7 +87,7 @@ public final class Cucumber extends ParentRunner> { private final List> children; private final EventBus bus; - private final List features; + private final List features; private final Plugins plugins; private boolean multiThreadingAssumed = false; @@ -146,21 +147,23 @@ public Cucumber(Class clazz) throws InitializationError { .setStrict(runtimeOptions.isStrict()) .build(junitEnvironmentOptions); + this.bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); + // Parse the features early. Don't proceed when there are lexer errors + FeatureParser parser = new FeatureParser(bus::generateId); Supplier classLoader = ClassLoaders::getDefaultClassLoader; - FeaturePathFeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(classLoader, runtimeOptions); + FeaturePathFeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(classLoader, runtimeOptions, parser); this.features = featureSupplier.get(); // Create plugins after feature parsing to avoid the creation of empty files on lexer errors. this.plugins = new Plugins(new PluginFactory(), runtimeOptions); - this.bus = new TimeServiceEventBus(Clock.systemUTC()); ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions); 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); - Predicate filters = new Filters(runtimeOptions); + Predicate filters = new Filters(runtimeOptions); this.children = features.stream() .map(feature -> FeatureRunner.create(feature, filters, runnerSupplier, junitOptions)) .filter(runner -> !runner.isEmpty()) @@ -210,11 +213,12 @@ public void evaluate() throws Throwable { } bus.send(new TestRunStarted(bus.getInstant())); - for (CucumberFeature feature : features) { + for (Feature feature : features) { bus.send(new TestSourceRead(bus.getInstant(), feature.getUri(), feature.getSource())); } runFeatures.evaluate(); bus.send(new TestRunFinished(bus.getInstant())); } + } } diff --git a/junit/src/main/java/io/cucumber/junit/FeatureRunner.java b/junit/src/main/java/io/cucumber/junit/FeatureRunner.java index 76747c6931..d3fb4eb8a7 100644 --- a/junit/src/main/java/io/cucumber/junit/FeatureRunner.java +++ b/junit/src/main/java/io/cucumber/junit/FeatureRunner.java @@ -1,8 +1,8 @@ package io.cucumber.junit; import io.cucumber.core.exception.CucumberException; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.runtime.RunnerSupplier; import io.cucumber.junit.PickleRunners.PickleRunner; import org.junit.runner.Description; @@ -24,11 +24,11 @@ final class FeatureRunner extends ParentRunner { private final List children; - private final CucumberFeature cucumberFeature; + private final Feature feature; private final JUnitOptions options; private Description description; - static FeatureRunner create(CucumberFeature feature, Predicate filter, RunnerSupplier runners, JUnitOptions options) { + static FeatureRunner create(Feature feature, Predicate filter, RunnerSupplier runners, JUnitOptions options) { try { return new FeatureRunner(feature, filter, runners, options); } catch (InitializationError e) { @@ -36,9 +36,9 @@ static FeatureRunner create(CucumberFeature feature, Predicate f } } - private FeatureRunner(CucumberFeature feature, Predicate filter, RunnerSupplier runners, JUnitOptions options) throws InitializationError { + private FeatureRunner(Feature feature, Predicate filter, RunnerSupplier runners, JUnitOptions options) throws InitializationError { super(null); - this.cucumberFeature = feature; + this.feature = feature; this.options = options; this.children = feature.getPickles().stream() .filter(filter). @@ -50,13 +50,13 @@ private FeatureRunner(CucumberFeature feature, Predicate filter, @Override protected String getName() { - return createName(cucumberFeature.getName(), options.filenameCompatibleNames()); + return createName(feature.getName(), options.filenameCompatibleNames()); } @Override public Description getDescription() { if (description == null) { - description = Description.createSuiteDescription(getName(), new FeatureId(cucumberFeature)); + description = Description.createSuiteDescription(getName(), new FeatureId(feature)); getChildren().forEach(child -> description.addChild(describeChild(child))); } return description; @@ -93,7 +93,7 @@ private static final class FeatureId implements Serializable { private static final long serialVersionUID = 1L; private final URI uri; - FeatureId(CucumberFeature feature) { + FeatureId(Feature feature) { this.uri = feature.getUri(); } diff --git a/junit/src/main/java/io/cucumber/junit/PickleRunners.java b/junit/src/main/java/io/cucumber/junit/PickleRunners.java index 6b0babf5c6..502e45e78f 100644 --- a/junit/src/main/java/io/cucumber/junit/PickleRunners.java +++ b/junit/src/main/java/io/cucumber/junit/PickleRunners.java @@ -1,10 +1,10 @@ package io.cucumber.junit; import io.cucumber.core.exception.CucumberException; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.runner.Runner; import io.cucumber.core.runtime.RunnerSupplier; -import io.cucumber.plugin.event.CucumberStep; +import io.cucumber.plugin.event.Step; import org.junit.runner.Description; import org.junit.runner.notification.RunNotifier; import org.junit.runners.ParentRunner; @@ -27,11 +27,11 @@ interface PickleRunner { Description getDescription(); - Description describeChild(CucumberStep step); + Description describeChild(Step step); } - static PickleRunner withStepDescriptions(RunnerSupplier runnerSupplier, CucumberPickle pickle, JUnitOptions options) { + static PickleRunner withStepDescriptions(RunnerSupplier runnerSupplier, Pickle pickle, JUnitOptions options) { try { return new WithStepDescriptions(runnerSupplier, pickle, options); } catch (InitializationError e) { @@ -40,19 +40,19 @@ static PickleRunner withStepDescriptions(RunnerSupplier runnerSupplier, Cucumber } - static PickleRunner withNoStepDescriptions(String featureName, RunnerSupplier runnerSupplier, CucumberPickle pickle, JUnitOptions jUnitOptions) { + static PickleRunner withNoStepDescriptions(String featureName, RunnerSupplier runnerSupplier, Pickle pickle, JUnitOptions jUnitOptions) { return new NoStepDescriptions(featureName, runnerSupplier, pickle, jUnitOptions); } - static class WithStepDescriptions extends ParentRunner implements PickleRunner { + static class WithStepDescriptions extends ParentRunner implements PickleRunner { private final RunnerSupplier runnerSupplier; - private final CucumberPickle pickle; + private final Pickle pickle; private final JUnitOptions jUnitOptions; - private final Map stepDescriptions = new HashMap<>(); + private final Map stepDescriptions = new HashMap<>(); private Description description; - WithStepDescriptions(RunnerSupplier runnerSupplier, CucumberPickle pickle, JUnitOptions jUnitOptions) throws InitializationError { + WithStepDescriptions(RunnerSupplier runnerSupplier, Pickle pickle, JUnitOptions jUnitOptions) throws InitializationError { super(null); this.runnerSupplier = runnerSupplier; this.pickle = pickle; @@ -60,8 +60,8 @@ static class WithStepDescriptions extends ParentRunner implements } @Override - protected List getChildren() { - // Casts io.cucumber.core.feature.CucumberStep + protected List getChildren() { + // Casts io.cucumber.core.gherkin.Step // to io.cucumber.core.event.CucumberStep return new ArrayList<>(pickle.getSteps()); } @@ -81,7 +81,7 @@ public Description getDescription() { } @Override - public Description describeChild(CucumberStep step) { + public Description describeChild(Step step) { Description description = stepDescriptions.get(step); if (description == null) { String testName = createName(step.getText(), jUnitOptions.filenameCompatibleNames()); @@ -102,7 +102,7 @@ public void run(final RunNotifier notifier) { } @Override - protected void runChild(CucumberStep step, RunNotifier notifier) { + protected void runChild(Step step, RunNotifier notifier) { // The way we override run(RunNotifier) causes this method to never be called. // Instead it happens via cucumberScenario.run(jUnitReporter, jUnitReporter, runtime); throw new UnsupportedOperationException(); @@ -114,11 +114,11 @@ protected void runChild(CucumberStep step, RunNotifier notifier) { static final class NoStepDescriptions implements PickleRunner { private final String featureName; private final RunnerSupplier runnerSupplier; - private final CucumberPickle pickle; + private final Pickle pickle; private final JUnitOptions jUnitOptions; private Description description; - NoStepDescriptions(String featureName, RunnerSupplier runnerSupplier, CucumberPickle pickle, JUnitOptions jUnitOptions) { + NoStepDescriptions(String featureName, RunnerSupplier runnerSupplier, Pickle pickle, JUnitOptions jUnitOptions) { this.featureName = featureName; this.runnerSupplier = runnerSupplier; this.pickle = pickle; @@ -136,7 +136,7 @@ public Description getDescription() { } @Override - public Description describeChild(CucumberStep step) { + public Description describeChild(Step step) { throw new UnsupportedOperationException("This pickle runner does not wish to describe its children"); } @@ -161,7 +161,7 @@ static final class PickleId implements Serializable { this.pickleLine = pickleLine; } - PickleId(CucumberPickle pickle) { + PickleId(Pickle pickle) { this(pickle.getUri(), pickle.getLocation().getLine()); } @@ -192,10 +192,10 @@ private static final class PickleStepId implements Serializable { private final int pickleLine; private int pickleStepLine; - PickleStepId(CucumberPickle pickle, CucumberStep step) { + PickleStepId(Pickle pickle, Step step) { this.uri = pickle.getUri(); this.pickleLine = pickle.getLocation().getLine(); - this.pickleStepLine = step.getStepLine(); + this.pickleStepLine = step.getLine(); } @Override diff --git a/junit/src/test/java/io/cucumber/junit/CucumberTest.java b/junit/src/test/java/io/cucumber/junit/CucumberTest.java index d0ba402099..e8bd6d9597 100644 --- a/junit/src/test/java/io/cucumber/junit/CucumberTest.java +++ b/junit/src/test/java/io/cucumber/junit/CucumberTest.java @@ -1,6 +1,7 @@ package io.cucumber.junit; import io.cucumber.core.exception.CucumberException; +import io.cucumber.core.gherkin.FeatureParserException; import org.junit.experimental.ParallelComputer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -67,22 +68,18 @@ void finds_features_based_on_explicit_root_package() throws InitializationError @Test void testThatParsingErrorsIsNicelyReported() { Executable testMethod = () -> new Cucumber(LexerErrorFeature.class); - CucumberException actualThrown = assertThrows(CucumberException.class, testMethod); + FeatureParserException actualThrown = assertThrows(FeatureParserException.class, testMethod); assertAll("Checking Exception including cause", () -> assertThat( actualThrown.getMessage(), is(equalTo("Failed to parse resource at: classpath:io/cucumber/error/lexer_error.feature")) - ), - () -> assertThat( - actualThrown.getCause().getClass().getName(), - is("gherkin.ParserException$CompositeParserException") ) ); } @Test void testThatFileIsNotCreatedOnParsingError() { - assertThrows(CucumberException.class, + assertThrows(FeatureParserException.class, () -> new Cucumber(FormatterWithLexerErrorFeature.class) ); assertFalse( diff --git a/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java b/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java index 23a6097c12..a219a4b01c 100644 --- a/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java +++ b/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java @@ -1,7 +1,7 @@ package io.cucumber.junit; import io.cucumber.core.eventbus.EventBus; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.filter.Filters; import io.cucumber.core.options.RuntimeOptions; import io.cucumber.core.runtime.BackendSupplier; @@ -24,6 +24,7 @@ import java.time.ZoneId; import java.util.HashSet; import java.util.Set; +import java.util.UUID; import java.util.function.Supplier; import static java.util.Collections.singleton; @@ -55,7 +56,7 @@ private static void assertDescriptionIsUnique(Description description, Set singleton(new StubBackendProviderService.StubBackend()); - EventBus bus = new TimeServiceEventBus(clockStub); + EventBus bus = new TimeServiceEventBus(clockStub, UUID::randomUUID); Filters filters = new Filters(runtimeOptions); Supplier classLoader = FeatureRunnerTest.class::getClassLoader; ScanningTypeRegistryConfigurerSupplier typeRegistrySupplier = new ScanningTypeRegistryConfigurerSupplier(classLoader, runtimeOptions); ThreadLocalRunnerSupplier runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, bus, backendSupplier, objectFactory, typeRegistrySupplier); - return FeatureRunner.create(cucumberFeature, filters, runnerSupplier, junitOption); + return FeatureRunner.create(feature, filters, runnerSupplier, junitOption); } @Test void should_populate_descriptions_with_stable_unique_ids() { - CucumberFeature cucumberFeature = TestPickleBuilder.parseFeature("path/test.feature", "" + + Feature feature = TestPickleBuilder.parseFeature("path/test.feature", "" + "Feature: feature name\n" + " Background:\n" + " Given background step\n" + @@ -202,8 +203,8 @@ void should_populate_descriptions_with_stable_unique_ids() { ); - FeatureRunner runner = createFeatureRunner(cucumberFeature, new JUnitOptions()); - FeatureRunner rerunner = createFeatureRunner(cucumberFeature, new JUnitOptions()); + FeatureRunner runner = createFeatureRunner(feature, new JUnitOptions()); + FeatureRunner rerunner = createFeatureRunner(feature, new JUnitOptions()); Set descriptions = new HashSet<>(); assertDescriptionIsUnique(runner.getDescription(), descriptions); @@ -214,7 +215,7 @@ void should_populate_descriptions_with_stable_unique_ids() { @Test void step_descriptions_can_be_turned_on() { - CucumberFeature cucumberFeature = TestPickleBuilder.parseFeature("path/test.feature", "" + + Feature cucumberFeature = TestPickleBuilder.parseFeature("path/test.feature", "" + "Feature: feature name\n" + " Background:\n" + " Given background step\n" + @@ -250,7 +251,7 @@ void step_descriptions_can_be_turned_on() { @Test void step_notification_can_be_turned_on_scenario_outline_with_two_examples_table_and_background() { - CucumberFeature feature = TestPickleBuilder.parseFeature("path/test.feature", "" + + Feature feature = TestPickleBuilder.parseFeature("path/test.feature", "" + "Feature: feature name\n" + " Background: background\n" + " Given first step\n" + @@ -307,7 +308,7 @@ void step_notification_can_be_turned_on_scenario_outline_with_two_examples_table @Test void step_notification_can_be_turned_on_two_scenarios_with_background() { - CucumberFeature feature = TestPickleBuilder.parseFeature("path/test.feature", "" + + Feature feature = TestPickleBuilder.parseFeature("path/test.feature", "" + "Feature: feature name\n" + " Background: background\n" + " Given first step\n" + @@ -345,7 +346,7 @@ void step_notification_can_be_turned_on_two_scenarios_with_background() { @Test void should_notify_of_failure_to_create_runners_and_request_test_execution_to_stop() { - CucumberFeature feature = TestPickleBuilder.parseFeature("path/test.feature", "" + + Feature feature = TestPickleBuilder.parseFeature("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario_1 name\n" + " Given first step\n" diff --git a/junit/src/test/java/io/cucumber/junit/JUnitReporterWithStepNotificationsTest.java b/junit/src/test/java/io/cucumber/junit/JUnitReporterWithStepNotificationsTest.java index 3927a3fd8a..85f6d711cd 100644 --- a/junit/src/test/java/io/cucumber/junit/JUnitReporterWithStepNotificationsTest.java +++ b/junit/src/test/java/io/cucumber/junit/JUnitReporterWithStepNotificationsTest.java @@ -2,8 +2,8 @@ import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.exception.CucumberException; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberStep; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Step; import io.cucumber.core.runtime.TimeServiceEventBus; import io.cucumber.junit.PickleRunners.PickleRunner; import io.cucumber.plugin.event.HookTestStep; @@ -32,6 +32,7 @@ import java.net.URI; import java.time.Clock; import java.util.List; +import java.util.UUID; import static java.time.Duration.ZERO; import static java.time.Instant.now; @@ -54,14 +55,14 @@ class JUnitReporterWithStepNotificationsTest { private static final int scenarioLine = 0; private static final URI featureUri = URI.create("file:example.feature"); - private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC()); + private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); private final JUnitReporter jUnitReporter = new JUnitReporter(bus, new JUnitOptionsBuilder().setStepNotifications(true).build()); - private final CucumberFeature feature = TestFeatureParser.parse("" + + private final Feature feature = TestFeatureParser.parse("" + "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given step name\n" ); - private final CucumberStep step = feature.getPickles().get(0).getSteps().get(0); + private final Step step = feature.getPickles().get(0).getSteps().get(0); @Mock private TestCase testCase; @Mock @@ -75,7 +76,7 @@ class JUnitReporterWithStepNotificationsTest { @Captor private ArgumentCaptor failureArgumentCaptor; - private static PickleStepTestStep mockTestStep(CucumberStep step) { + private static PickleStepTestStep mockTestStep(Step step) { PickleStepTestStep testStep = mock(PickleStepTestStep.class); lenient().when(testStep.getStepText()).thenReturn(step.getText()); lenient().when(testStep.getStepLine()).thenReturn(scenarioLine); @@ -109,7 +110,7 @@ void disconnects_from_bus_once_execution_unit_finished() { @Test void ignores_steps_when_step_notification_are_disabled() { - EventBus bus = new TimeServiceEventBus(Clock.systemUTC()); + EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); JUnitReporter jUnitReporter = new JUnitReporter(bus, new JUnitOptionsBuilder() .setStepNotifications(false) .build()); @@ -257,7 +258,7 @@ void test_step_undefined_fires_assumption_failed_and_test_finished_for_undefined @Test void test_step_undefined_fires_test_failure_and_test_finished_for_undefined_step_in_strict_mode() { - EventBus bus = new TimeServiceEventBus(Clock.systemUTC()); + EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); JUnitReporter jUnitReporter = new JUnitReporter(bus, new JUnitOptionsBuilder() .setStepNotifications(true) .setStrict(true) diff --git a/junit/src/test/java/io/cucumber/junit/PickleRunnerWithNoStepDescriptionsTest.java b/junit/src/test/java/io/cucumber/junit/PickleRunnerWithNoStepDescriptionsTest.java index 03a0228225..940afab5da 100644 --- a/junit/src/test/java/io/cucumber/junit/PickleRunnerWithNoStepDescriptionsTest.java +++ b/junit/src/test/java/io/cucumber/junit/PickleRunnerWithNoStepDescriptionsTest.java @@ -1,6 +1,6 @@ package io.cucumber.junit; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.runtime.RunnerSupplier; import io.cucumber.junit.PickleRunners.PickleRunner; import org.junit.jupiter.api.Test; @@ -16,7 +16,7 @@ class PickleRunnerWithNoStepDescriptionsTest { @Test void shouldUseScenarioNameWithFeatureNameAsClassNameForDisplayName() { - List pickles = TestPickleBuilder.picklesFromFeature("featurePath", "" + + List pickles = TestPickleBuilder.picklesFromFeature("featurePath", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Then it works\n"); @@ -33,7 +33,7 @@ void shouldUseScenarioNameWithFeatureNameAsClassNameForDisplayName() { @Test void shouldConvertTextFromFeatureFileForNamesWithFilenameCompatibleNameOption() { - List pickles = TestPickleBuilder.picklesFromFeature("featurePath", "" + + List pickles = TestPickleBuilder.picklesFromFeature("featurePath", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Then it works\n"); @@ -50,7 +50,7 @@ void shouldConvertTextFromFeatureFileForNamesWithFilenameCompatibleNameOption() @Test void shouldConvertTextFromFeatureFileWithRussianLanguage() { - List pickles = TestPickleBuilder.picklesFromFeature("featurePath", "" + + List pickles = TestPickleBuilder.picklesFromFeature("featurePath", "" + "#language:ru\n" + "Функция: имя функции\n" + " Сценарий: имя сценария\n" + diff --git a/junit/src/test/java/io/cucumber/junit/PickleRunnerWithStepDescriptionsTest.java b/junit/src/test/java/io/cucumber/junit/PickleRunnerWithStepDescriptionsTest.java index b50b730566..e539d67512 100644 --- a/junit/src/test/java/io/cucumber/junit/PickleRunnerWithStepDescriptionsTest.java +++ b/junit/src/test/java/io/cucumber/junit/PickleRunnerWithStepDescriptionsTest.java @@ -1,11 +1,11 @@ package io.cucumber.junit; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.runtime.RunnerSupplier; import io.cucumber.junit.PickleRunners.PickleRunner; import io.cucumber.junit.PickleRunners.WithStepDescriptions; -import io.cucumber.plugin.event.CucumberStep; +import io.cucumber.plugin.event.Step; import org.junit.jupiter.api.Test; import org.junit.runner.Description; @@ -20,7 +20,7 @@ class PickleRunnerWithStepDescriptionsTest { @Test void shouldAssignUnequalDescriptionsToDifferentOccurrencesOfSameStepInAScenario() { - List pickles = picklesFromFeature("path/test.feature", "" + + List pickles = picklesFromFeature("path/test.feature", "" + "Feature: FB\n" + "# Scenario with same step occurring twice\n" + "\n" + @@ -39,8 +39,8 @@ void shouldAssignUnequalDescriptionsToDifferentOccurrencesOfSameStepInAScenario( ); // fish out the two occurrences of the same step and check whether we really got them - CucumberStep stepOccurrence1 = runner.getChildren().get(0); - CucumberStep stepOccurrence2 = runner.getChildren().get(2); + Step stepOccurrence1 = runner.getChildren().get(0); + Step stepOccurrence2 = runner.getChildren().get(2); assertEquals(stepOccurrence1.getText(), stepOccurrence2.getText()); // then check that the descriptions are unequal @@ -54,7 +54,7 @@ void shouldAssignUnequalDescriptionsToDifferentOccurrencesOfSameStepInAScenario( @Test void shouldAssignUnequalDescriptionsToDifferentStepsInAScenarioOutline() { - CucumberFeature features = TestPickleBuilder.parseFeature("path/test.feature", "" + + Feature features = TestPickleBuilder.parseFeature("path/test.feature", "" + "Feature: FB\n" + " Scenario Outline: SO\n" + " When \n" + @@ -79,7 +79,7 @@ void shouldAssignUnequalDescriptionsToDifferentStepsInAScenarioOutline() { @Test void shouldIncludeScenarioNameAsClassNameInStepDescriptions() { - CucumberFeature features = TestPickleBuilder.parseFeature("path/test.feature", "" + + Feature features = TestPickleBuilder.parseFeature("path/test.feature", "" + "Feature: In cucumber.junit\n" + " Scenario: first\n" + " When step\n" + @@ -108,7 +108,7 @@ void shouldIncludeScenarioNameAsClassNameInStepDescriptions() { @Test void shouldUseScenarioNameForDisplayName() { - List pickles = picklesFromFeature("featurePath", "" + + List pickles = picklesFromFeature("featurePath", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Then it works\n"); @@ -124,7 +124,7 @@ void shouldUseScenarioNameForDisplayName() { @Test void shouldUseStepKeyworkAndNameForChildName() { - List pickles = picklesFromFeature("featurePath", "" + + List pickles = picklesFromFeature("featurePath", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Then it works\n"); @@ -140,7 +140,7 @@ void shouldUseStepKeyworkAndNameForChildName() { @Test void shouldConvertTextFromFeatureFileForNamesWithFilenameCompatibleNameOption() { - List pickles = picklesFromFeature("featurePath", "" + + List pickles = picklesFromFeature("featurePath", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + " Then it works\n"); diff --git a/junit/src/test/java/io/cucumber/junit/TestFeatureParser.java b/junit/src/test/java/io/cucumber/junit/TestFeatureParser.java index 1eeb57c874..9faed93542 100644 --- a/junit/src/test/java/io/cucumber/junit/TestFeatureParser.java +++ b/junit/src/test/java/io/cucumber/junit/TestFeatureParser.java @@ -1,6 +1,6 @@ package io.cucumber.junit; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; import io.cucumber.core.feature.FeatureIdentifier; import io.cucumber.core.feature.FeatureParser; import io.cucumber.core.resource.Resource; @@ -9,18 +9,19 @@ import java.io.InputStream; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.util.UUID; class TestFeatureParser { - static CucumberFeature parse(final String source) { + static Feature parse(final String source) { return parse("file:test.feature", source); } - private static CucumberFeature parse(final String uri, final String source) { + private static Feature parse(final String uri, final String source) { return parse(FeatureIdentifier.parse(uri), source); } - private static CucumberFeature parse(final URI uri, final String source) { - return FeatureParser.parseResource(new Resource() { + private static Feature parse(final URI uri, final String source) { + return new FeatureParser(UUID::randomUUID).parseResource(new Resource() { @Override public URI getUri() { return uri; diff --git a/junit/src/test/java/io/cucumber/junit/TestPickleBuilder.java b/junit/src/test/java/io/cucumber/junit/TestPickleBuilder.java index 3cd2adadb8..9260bfc9c9 100644 --- a/junit/src/test/java/io/cucumber/junit/TestPickleBuilder.java +++ b/junit/src/test/java/io/cucumber/junit/TestPickleBuilder.java @@ -1,8 +1,8 @@ package io.cucumber.junit; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; import io.cucumber.core.feature.FeatureParser; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.resource.Resource; import java.io.ByteArrayInputStream; @@ -10,22 +10,23 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.UUID; class TestPickleBuilder { private TestPickleBuilder() { } - static List picklesFromFeature(final String path, final String source) { + static List picklesFromFeature(final String path, final String source) { return parseFeature(path, source).getPickles(); } - static CucumberFeature parseFeature(final String path, final String source) { + static Feature parseFeature(final String path, final String source) { return parseFeature(URI.create(path), source); } - private static CucumberFeature parseFeature(final URI path, final String source) { - return FeatureParser.parseResource(new Resource() { + private static Feature parseFeature(final URI path, final String source) { + return new FeatureParser(UUID::randomUUID).parseResource(new Resource() { @Override public URI getUri() { return path; diff --git a/plugin/pom.xml b/plugin/pom.xml index 480fbab88c..c539013e14 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -1,14 +1,15 @@ - + + 4.0.0 - cucumber-jvm io.cucumber + cucumber-jvm 5.0.0-RC3-SNAPSHOT - 4.0.0 cucumber-plugin - Cucumber-JVM Plugin + Cucumber-JVM: Plugin Plugin interface for Cucumber-JVM @@ -21,4 +22,4 @@ apiguardian-api - \ No newline at end of file + diff --git a/plugin/src/main/java/io/cucumber/plugin/event/PickleStepTestStep.java b/plugin/src/main/java/io/cucumber/plugin/event/PickleStepTestStep.java index f651c1230b..6de512d94d 100644 --- a/plugin/src/main/java/io/cucumber/plugin/event/PickleStepTestStep.java +++ b/plugin/src/main/java/io/cucumber/plugin/event/PickleStepTestStep.java @@ -23,7 +23,7 @@ public interface PickleStepTestStep extends TestStep { * * @return the matched step */ - CucumberStep getStep(); + Step getStep(); /** * Returns the arguments provided to the step definition. @@ -41,15 +41,18 @@ public interface PickleStepTestStep extends TestStep { * a data table or doc string. * * @return arguments provided to the gherkin step. + * @deprecated use {@link #getStep()} */ - + @Deprecated StepArgument getStepArgument(); /** * The line in the feature file defining this step. * * @return a line number + * @deprecated use {@link #getStep()} */ + @Deprecated int getStepLine(); /** @@ -63,6 +66,8 @@ public interface PickleStepTestStep extends TestStep { * The full text of the Gherkin step. * * @return the step text + * @deprecated use {@code #getStep()} */ + @Deprecated String getStepText(); } diff --git a/plugin/src/main/java/io/cucumber/plugin/event/CucumberStep.java b/plugin/src/main/java/io/cucumber/plugin/event/Step.java similarity index 92% rename from plugin/src/main/java/io/cucumber/plugin/event/CucumberStep.java rename to plugin/src/main/java/io/cucumber/plugin/event/Step.java index e3be0a0a2d..d51a61aab7 100644 --- a/plugin/src/main/java/io/cucumber/plugin/event/CucumberStep.java +++ b/plugin/src/main/java/io/cucumber/plugin/event/Step.java @@ -6,7 +6,7 @@ * Represents a step in a scenario. */ @API(status = API.Status.STABLE) -public interface CucumberStep { +public interface Step { /** * Returns this Gherkin step argument. Can be either a data table or doc * string. @@ -34,5 +34,5 @@ public interface CucumberStep { * * @return step line number */ - int getStepLine(); + int getLine(); } diff --git a/plugin/src/main/java/io/cucumber/plugin/event/TestCase.java b/plugin/src/main/java/io/cucumber/plugin/event/TestCase.java index 12ae4f031d..9275d251fd 100644 --- a/plugin/src/main/java/io/cucumber/plugin/event/TestCase.java +++ b/plugin/src/main/java/io/cucumber/plugin/event/TestCase.java @@ -4,6 +4,7 @@ import java.net.URI; import java.util.List; +import java.util.UUID; @API(status = API.Status.STABLE) public interface TestCase { @@ -15,8 +16,14 @@ public interface TestCase { */ Integer getLine(); + String getKeyword(); + String getName(); + /** + * @deprecated use other accessor to reconstruct the scenario designation + */ + @Deprecated String getScenarioDesignation(); List getTags(); @@ -24,4 +31,6 @@ public interface TestCase { List getTestSteps(); URI getUri(); + + UUID getId(); } diff --git a/pom.xml b/pom.xml index 8dfb23e260..b893119068 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 io.cucumber @@ -41,10 +42,10 @@ 1.1.0 - 5.2.0 - 8.0.0 + 8.2.1 3.0.0 2.0.2 + 5.2.0 4.12 @@ -74,13 +75,13 @@ io.cucumber - gherkin - ${gherkin.version} + tag-expressions + ${tag-expressions.version} io.cucumber - tag-expressions - ${tag-expressions.version} + messages + ${messages.version} io.cucumber @@ -147,6 +148,21 @@ cucumber-kotlin-java8 ${project.version} + + io.cucumber + cucumber-gherkin + ${project.version} + + + io.cucumber + cucumber-gherkin-vintage + ${project.version} + + + io.cucumber + cucumber-gherkin-messages + ${project.version} + io.cucumber cucumber-junit-platform-engine @@ -196,6 +212,8 @@ core deltaspike docstring + gherkin + gherkin-vintage guice java8 java @@ -266,7 +284,7 @@ io.cucumber gherkin - ${gherkin.version} + ${gherkin-vintage.version} @@ -285,7 +303,7 @@ API Packages - cucumber.api.*:io.cucumber.core.api:io.cucumber.core.api.*:io.cucumber.junit:io.cucumber.testng.api + cucumber.api.*:io.cucumber.core.api:io.cucumber.core.api.*:io.cucumber.junit:io.cucumber.testng @@ -328,6 +346,7 @@ + 4.8.0 true @@ -340,28 +359,7 @@ - - class org\.springframework\..* - .* io\.cucumber\.core\.runner\.AmbiguousStepDefinitionsException.* - .* io\.cucumber\.core\.feature\.CucumberFeature\.CucumberFeatureUriComparator.* - .* io\.cucumber\.junit\..* - .* io\.cucumber\.junit\..* - .* io.cucumber.core.runtime.* - .* io.cucumber.core.feature.* - .* io.cucumber.core.logging.* - .* io.cucumber.core.io.* - .* io.cucumber.core.reflection.* - .* io.cucumber.core.resource.* - .* io.cucumber.plugin.event.EmbedEvent::getMediaType().* - .* io.cucumber.java8.DefaultDataTableEntryTransformerBody.* - .* io.cucumber.java8.LambdaGlue::DefaultDataTableCellTransformer.* - .* io.cucumber.java8.DefaultDataTableCellTransformerBody.* - .* io.cucumber.java8.LambdaGlue::DefaultDataTableCellTransformer.* - .* io.cucumber.java8.DefaultParameterTransformerBody.* - .* io.cucumber.java8.LambdaGlue::DefaultParameterTransformer.* - .* io.cucumber.java8.LambdaGlue::DefaultDataTableEntryTransformer.* - .* io.cucumber.testng.TestNGCucumberRunner.* + .* io.cucumber.core.gherkin.* diff --git a/testng/src/main/java/io/cucumber/testng/FeatureWrapperImpl.java b/testng/src/main/java/io/cucumber/testng/FeatureWrapperImpl.java index d485fa25da..4252c1d3e4 100644 --- a/testng/src/main/java/io/cucumber/testng/FeatureWrapperImpl.java +++ b/testng/src/main/java/io/cucumber/testng/FeatureWrapperImpl.java @@ -1,16 +1,16 @@ package io.cucumber.testng; -import io.cucumber.core.feature.CucumberFeature; +import io.cucumber.core.gherkin.Feature; final class FeatureWrapperImpl implements FeatureWrapper { - private final CucumberFeature cucumberFeature; + private final Feature feature; - FeatureWrapperImpl(CucumberFeature cucumberFeature) { - this.cucumberFeature = cucumberFeature; + FeatureWrapperImpl(Feature feature) { + this.feature = feature; } @Override public String toString() { - return "\"" + cucumberFeature.getName() + "\""; + return "\"" + feature.getName() + "\""; } } diff --git a/testng/src/main/java/io/cucumber/testng/Pickle.java b/testng/src/main/java/io/cucumber/testng/Pickle.java index 20ad6c6ef2..3fb26e8b22 100644 --- a/testng/src/main/java/io/cucumber/testng/Pickle.java +++ b/testng/src/main/java/io/cucumber/testng/Pickle.java @@ -1,6 +1,5 @@ package io.cucumber.testng; -import io.cucumber.core.feature.CucumberPickle; import org.apiguardian.api.API; /** @@ -9,13 +8,13 @@ @API(status = API.Status.STABLE) public final class Pickle { - private final CucumberPickle cucumberPickle; + private final io.cucumber.core.gherkin.Pickle pickle; - Pickle(CucumberPickle cucumberPickle) { - this.cucumberPickle = cucumberPickle; + Pickle(io.cucumber.core.gherkin.Pickle pickle) { + this.pickle = pickle; } - CucumberPickle getCucumberPickle() { - return cucumberPickle; + io.cucumber.core.gherkin.Pickle getPickle() { + return pickle; } } diff --git a/testng/src/main/java/io/cucumber/testng/PickleWrapperImpl.java b/testng/src/main/java/io/cucumber/testng/PickleWrapperImpl.java index 14f29b2abd..82bb4e95e3 100644 --- a/testng/src/main/java/io/cucumber/testng/PickleWrapperImpl.java +++ b/testng/src/main/java/io/cucumber/testng/PickleWrapperImpl.java @@ -14,6 +14,6 @@ public Pickle getPickle() { @Override public String toString() { - return "\"" + pickle.getCucumberPickle().getName() + "\""; + return "\"" + pickle.getPickle().getName() + "\""; } } diff --git a/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java b/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java index 361dc59cd1..315c10ffd9 100644 --- a/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java +++ b/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java @@ -2,9 +2,10 @@ import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.exception.CucumberException; -import io.cucumber.core.feature.CucumberFeature; -import io.cucumber.core.feature.CucumberPickle; +import io.cucumber.core.feature.FeatureParser; import io.cucumber.core.filter.Filters; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.logging.Logger; import io.cucumber.core.logging.LoggerFactory; import io.cucumber.core.options.Constants; @@ -32,6 +33,7 @@ import java.time.Clock; import java.util.List; +import java.util.UUID; import java.util.function.Predicate; import java.util.function.Supplier; @@ -55,7 +57,7 @@ public final class TestNGCucumberRunner { private static final Logger log = LoggerFactory.getLogger(TestNGCucumberRunner.class); private final EventBus bus; - private final Predicate filters; + private final Predicate filters; private final ThreadLocalRunnerSupplier runnerSupplier; private final RuntimeOptions runtimeOptions; private final Plugins plugins; @@ -82,11 +84,12 @@ public TestNGCucumberRunner(Class clazz) { .parse(CucumberProperties.fromEnvironment()) .build(annotationOptions); - runtimeOptions = new CucumberPropertiesParser() + this.runtimeOptions = new CucumberPropertiesParser() .parse(CucumberProperties.fromSystemProperties()) .addDefaultSummaryPrinterIfAbsent() .build(environmentOptions); + this.bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); if (!runtimeOptions.isStrict()) { log.warn(() -> "By default Cucumber is running in --non-strict mode.\n" + @@ -96,9 +99,9 @@ public TestNGCucumberRunner(Class clazz) { } Supplier classLoader = ClassLoaders::getDefaultClassLoader; - featureSupplier = new FeaturePathFeatureSupplier(classLoader, runtimeOptions); + FeatureParser parser = new FeatureParser(bus::generateId); + this.featureSupplier = new FeaturePathFeatureSupplier(classLoader, runtimeOptions, parser); - this.bus = new TimeServiceEventBus(Clock.systemUTC()); this.plugins = new Plugins(new PluginFactory(), runtimeOptions); ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions); ObjectFactorySupplier objectFactorySupplier = new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader); @@ -108,11 +111,11 @@ public TestNGCucumberRunner(Class clazz) { this.runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, bus, backendSupplier, objectFactorySupplier, typeRegistryConfigurerSupplier); } - public void runScenario(Pickle pickle) throws Throwable { + public void runScenario(io.cucumber.testng.Pickle pickle) throws Throwable { //Possibly invoked in a multi-threaded context Runner runner = runnerSupplier.get(); TestCaseResultListener testCaseResultListener = new TestCaseResultListener(runner.getBus(), runtimeOptions.isStrict()); - CucumberPickle cucumberPickle = pickle.getCucumberPickle(); + Pickle cucumberPickle = pickle.getPickle(); runner.runPickle(cucumberPickle); testCaseResultListener.finishExecutionUnit(); @@ -137,7 +140,7 @@ public Object[][] provideScenarios() { .flatMap(feature -> feature.getPickles().stream() .filter(filters) .map(cucumberPickle -> new Object[]{ - new PickleWrapperImpl(new Pickle(cucumberPickle)), + new PickleWrapperImpl(new io.cucumber.testng.Pickle(cucumberPickle)), new FeatureWrapperImpl(feature)})) .collect(toList()) .toArray(new Object[0][0]); @@ -146,12 +149,12 @@ public Object[][] provideScenarios() { } } - private List getFeatures() { + private List getFeatures() { plugins.setSerialEventBusOnEventListenerPlugins(bus); - List features = featureSupplier.get(); + List features = featureSupplier.get(); bus.send(new TestRunStarted(bus.getInstant())); - for (CucumberFeature feature : features) { + for (Feature feature : features) { bus.send(new TestSourceRead(bus.getInstant(), feature.getUri(), feature.getSource())); } return features; diff --git a/testng/src/test/java/io/cucumber/testng/StubBackendProviderService.java b/testng/src/test/java/io/cucumber/testng/StubBackendProviderService.java index b21ecd41ec..1a63a2675b 100644 --- a/testng/src/test/java/io/cucumber/testng/StubBackendProviderService.java +++ b/testng/src/test/java/io/cucumber/testng/StubBackendProviderService.java @@ -51,7 +51,7 @@ private StepDefinition createStepDefinition(final String pattern) { @Override public String getLocation() { - return null; + return "stubbed location"; } @Override diff --git a/testng/src/test/java/io/cucumber/testng/TestCaseResultListenerTest.java b/testng/src/test/java/io/cucumber/testng/TestCaseResultListenerTest.java index 93e29b24ed..e3bb7e3913 100644 --- a/testng/src/test/java/io/cucumber/testng/TestCaseResultListenerTest.java +++ b/testng/src/test/java/io/cucumber/testng/TestCaseResultListenerTest.java @@ -14,6 +14,7 @@ import java.net.URI; import java.time.Clock; +import java.util.UUID; import static io.cucumber.plugin.event.Status.AMBIGUOUS; import static io.cucumber.plugin.event.Status.FAILED; @@ -36,7 +37,7 @@ public class TestCaseResultListenerTest { - private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC()); + private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); private URI uri = URI.create("file:path/to.feature"); private int line = 0; diff --git a/testng/src/test/java/io/cucumber/testng/TestNGCucumberRunnerTest.java b/testng/src/test/java/io/cucumber/testng/TestNGCucumberRunnerTest.java index 9b8e63cb72..9c72e99c31 100644 --- a/testng/src/test/java/io/cucumber/testng/TestNGCucumberRunnerTest.java +++ b/testng/src/test/java/io/cucumber/testng/TestNGCucumberRunnerTest.java @@ -1,6 +1,6 @@ package io.cucumber.testng; -import io.cucumber.core.exception.CucumberException; +import io.cucumber.core.gherkin.FeatureParserException; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -44,12 +44,12 @@ public void runScenarioWithUndefinedStepsStrict() { @Test public void parse_error_propagated_to_testng_test_execution() { testNGCucumberRunner = new TestNGCucumberRunner(ParseError.class); - Object[][] scenarios = testNGCucumberRunner.provideScenarios(); // a CucumberException is caught try { + Object[][] scenarios = testNGCucumberRunner.provideScenarios(); // a CucumberException is caught PickleWrapper pickleWrapper = (PickleWrapper) scenarios[0][0]; pickleWrapper.getPickle(); Assert.fail("CucumberException not thrown"); - } catch (CucumberException e) { + } catch (FeatureParserException e) { Assert.assertEquals(e.getMessage(), "Failed to parse resource at: classpath:io/cucumber/error/parse-error.feature"); } }