From 2579af9a97fbb4d4154886b8d74e10d36fb06a1e Mon Sep 17 00:00:00 2001 From: MetallFoX Date: Tue, 24 Nov 2020 08:25:57 +0300 Subject: [PATCH 1/2] Cucumber gherkin version 15.0.2 to support cucumber 6.9.0 AllureCucumber6Jvm correspondingly updated dry-run steps results changed to PASSED due to cucumber behavior change: https://github.com/cucumber/cucumber-jvm/pull/2109 --- allure-cucumber6-jvm/build.gradle.kts | 4 +- .../cucumber6jvm/AllureCucumber6Jvm.java | 30 +-- .../allure/cucumber6jvm/LabelBuilder.java | 2 +- .../qameta/allure/cucumber6jvm/TagParser.java | 4 +- .../testsourcemodel/TestSourcesModel.java | 195 ++++++++++-------- .../TestSourcesModelProxy.java | 12 +- .../cucumber6jvm/AllureCucumber6JvmTest.java | 24 +-- 7 files changed, 149 insertions(+), 122 deletions(-) diff --git a/allure-cucumber6-jvm/build.gradle.kts b/allure-cucumber6-jvm/build.gradle.kts index 7f2c6bda5..d355fdf04 100644 --- a/allure-cucumber6-jvm/build.gradle.kts +++ b/allure-cucumber6-jvm/build.gradle.kts @@ -2,8 +2,8 @@ description = "Allure CucumberJVM 6.0" val agent: Configuration by configurations.creating -val cucumberVersion = "6.1.1" -val cucumberGherkinVersion = "5.1.0" +val cucumberVersion = "6.9.0" +val cucumberGherkinVersion = "15.0.2" dependencies { agent("org.aspectj:aspectjweaver") diff --git a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/AllureCucumber6Jvm.java b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/AllureCucumber6Jvm.java index e57514bea..83679ffb5 100644 --- a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/AllureCucumber6Jvm.java +++ b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/AllureCucumber6Jvm.java @@ -15,11 +15,10 @@ */ package io.qameta.allure.cucumber6jvm; -import gherkin.ast.Examples; -import gherkin.ast.Feature; -import gherkin.ast.ScenarioDefinition; -import gherkin.ast.ScenarioOutline; -import gherkin.ast.TableRow; +import io.cucumber.messages.Messages.GherkinDocument.Feature; +import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario; +import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario.Examples; +import io.cucumber.messages.Messages.GherkinDocument.Feature.TableRow; import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.event.DataTableArgument; import io.cucumber.plugin.event.EmbedEvent; @@ -154,11 +153,12 @@ private void handleTestCaseStarted(final TestCaseStarted event) { .setLabels(labelBuilder.getScenarioLabels()) .setLinks(labelBuilder.getScenarioLinks()); - final ScenarioDefinition scenarioDefinition = + final Scenario scenarioDefinition = testSources.getScenarioDefinition(currentFeatureFile.get(), currentTestCase.get().getLine()); - if (scenarioDefinition instanceof ScenarioOutline) { + + if (scenarioDefinition.getExamplesCount() > 0) { result.setParameters( - getExamplesAsParameters((ScenarioOutline) scenarioDefinition, currentTestCase.get()) + getExamplesAsParameters(scenarioDefinition, currentTestCase.get()) ); } @@ -300,21 +300,21 @@ private Status translateTestCaseStatus(final Result testCaseResult) { } private List getExamplesAsParameters( - final ScenarioOutline scenarioOutline, final TestCase localCurrentTestCase + final Scenario scenario, final TestCase localCurrentTestCase ) { final Optional examplesBlock = - scenarioOutline.getExamples().stream() - .filter(example -> example.getTableBody().stream() + scenario.getExamplesList().stream() + .filter(example -> example.getTableBodyList().stream() .anyMatch(row -> row.getLocation().getLine() == localCurrentTestCase.getLine()) ).findFirst(); if (examplesBlock.isPresent()) { - final TableRow row = examplesBlock.get().getTableBody().stream() + final TableRow row = examplesBlock.get().getTableBodyList().stream() .filter(example -> example.getLocation().getLine() == localCurrentTestCase.getLine()) .findFirst().get(); - return IntStream.range(0, examplesBlock.get().getTableHeader().getCells().size()).mapToObj(index -> { - final String name = examplesBlock.get().getTableHeader().getCells().get(index).getValue(); - final String value = row.getCells().get(index).getValue(); + return IntStream.range(0, examplesBlock.get().getTableHeader().getCellsList().size()).mapToObj(index -> { + final String name = examplesBlock.get().getTableHeader().getCellsList().get(index).getValue(); + final String value = row.getCellsList().get(index).getValue(); return createParameter(name, value); }).collect(Collectors.toList()); } else { diff --git a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/LabelBuilder.java b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/LabelBuilder.java index 5ad4a04e5..3a51a15dc 100644 --- a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/LabelBuilder.java +++ b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/LabelBuilder.java @@ -15,8 +15,8 @@ */ package io.qameta.allure.cucumber6jvm; +import io.cucumber.messages.Messages.GherkinDocument.Feature; import io.cucumber.plugin.event.TestCase; -import gherkin.ast.Feature; import io.qameta.allure.model.Label; import io.qameta.allure.model.Link; import io.qameta.allure.util.ResultsUtils; diff --git a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/TagParser.java b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/TagParser.java index c3b460f5b..f1e5940bf 100644 --- a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/TagParser.java +++ b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/TagParser.java @@ -15,7 +15,7 @@ */ package io.qameta.allure.cucumber6jvm; -import gherkin.ast.Feature; +import io.cucumber.messages.Messages.GherkinDocument.Feature; import io.cucumber.plugin.event.TestCase; import io.qameta.allure.SeverityLevel; @@ -52,7 +52,7 @@ public boolean isKnown() { private boolean getStatusDetailByTag(final String tagName) { return scenario.getTags().stream() .anyMatch(tag -> tag.equalsIgnoreCase(tagName)) - || feature.getTags().stream() + || feature.getTagsList().stream() .anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName)); } diff --git a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java index 2afc7c9fa..00575c7b6 100644 --- a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java +++ b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java @@ -15,31 +15,41 @@ */ package io.qameta.allure.cucumber6jvm.testsourcemodel; -import gherkin.AstBuilder; -import gherkin.Parser; -import gherkin.ParserException; -import gherkin.TokenMatcher; -import gherkin.ast.Examples; -import gherkin.ast.Feature; -import gherkin.ast.GherkinDocument; -import gherkin.ast.Node; -import gherkin.ast.ScenarioDefinition; -import gherkin.ast.ScenarioOutline; -import gherkin.ast.Step; -import gherkin.ast.TableRow; +import io.cucumber.gherkin.Gherkin; +import io.cucumber.messages.Messages; +import io.cucumber.messages.Messages.GherkinDocument; +import io.cucumber.messages.Messages.GherkinDocument.Feature; +import io.cucumber.messages.Messages.GherkinDocument.Feature.Background; +import io.cucumber.messages.Messages.GherkinDocument.Feature.FeatureChild; +import io.cucumber.messages.Messages.GherkinDocument.Feature.FeatureChild.RuleChild; +import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario; +import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario.Examples; +import io.cucumber.messages.Messages.GherkinDocument.Feature.Step; +import io.cucumber.messages.Messages.GherkinDocument.Feature.TableRow; +import io.cucumber.messages.internal.com.google.protobuf.GeneratedMessageV3; import io.cucumber.plugin.event.TestSourceRead; + import java.net.URI; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.UUID; + +import static io.cucumber.gherkin.Gherkin.makeSourceEnvelope; +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.toList; -public final class TestSourcesModel { +final class TestSourcesModel { private final Map pathToReadEventMap = new HashMap<>(); private final Map pathToAstMap = new HashMap<>(); private final Map> pathToNodeMap = new HashMap<>(); - public static ScenarioDefinition getScenarioDefinition(final AstNode astNode) { - return astNode.node instanceof ScenarioDefinition ? (ScenarioDefinition) astNode.node - : (ScenarioDefinition) astNode.parent.parent.node; + public static Scenario getScenarioDefinition(final AstNode astNode) { + AstNode candidate = astNode; + while (candidate != null && !(candidate.node instanceof Scenario)) { + candidate = candidate.parent; + } + return candidate == null ? null : (Scenario) candidate.node; } public void addTestSourceReadEvent(final URI path, final TestSourceRead event) { @@ -56,102 +66,119 @@ public Feature getFeature(final URI path) { return null; } - public AstNode getAstNode(final URI path, final int line) { - if (!pathToNodeMap.containsKey(path)) { - parseGherkinSource(path); - } - if (pathToNodeMap.containsKey(path)) { - return pathToNodeMap.get(path).get(line); - } - return null; - } - private void parseGherkinSource(final URI path) { if (!pathToReadEventMap.containsKey(path)) { return; } - final Parser parser = new Parser<>(new AstBuilder()); - final TokenMatcher matcher = new TokenMatcher(); - try { - final GherkinDocument gherkinDocument = parser.parse(pathToReadEventMap.get(path).getSource(), - matcher); - pathToAstMap.put(path, gherkinDocument); - final Map nodeMap = new HashMap<>(); - final AstNode currentParent = new AstNode(gherkinDocument.getFeature(), null); - for (ScenarioDefinition child : gherkinDocument.getFeature().getChildren()) { - processScenarioDefinition(nodeMap, child, currentParent); + final String source = pathToReadEventMap.get(path).getSource(); + + final List sources = singletonList( + makeSourceEnvelope(source, path.toString())); + + final List envelopes = Gherkin.fromSources( + sources, + true, + true, + true, + () -> String.valueOf(UUID.randomUUID())).collect(toList()); + + final GherkinDocument gherkinDocument = envelopes.stream() + .filter(Messages.Envelope::hasGherkinDocument) + .map(Messages.Envelope::getGherkinDocument) + .findFirst() + .orElse(null); + + pathToAstMap.put(path, gherkinDocument); + final Map nodeMap = new HashMap<>(); + final AstNode currentParent = createAstNode(gherkinDocument.getFeature(), null); + for (FeatureChild child : gherkinDocument.getFeature().getChildrenList()) { + processFeatureDefinition(nodeMap, child, currentParent); + } + pathToNodeMap.put(path, nodeMap); + + } + + private void processFeatureDefinition( + final Map nodeMap, final FeatureChild child, final AstNode currentParent) { + if (child.hasBackground()) { + processBackgroundDefinition(nodeMap, child.getBackground(), currentParent); + } else if (child.hasScenario()) { + processScenarioDefinition(nodeMap, child.getScenario(), currentParent); + } else if (child.hasRule()) { + final AstNode childNode = createAstNode(child.getRule(), currentParent); + nodeMap.put(child.getRule().getLocation().getLine(), childNode); + for (RuleChild ruleChild : child.getRule().getChildrenList()) { + processRuleDefinition(nodeMap, ruleChild, childNode); } - pathToNodeMap.put(path, nodeMap); - } catch (ParserException e) { - throw new IllegalStateException("You are using a plugin that only supports till Gherkin 5.\n" - + "Please check if the Gherkin provided follows the standard of Gherkin 5\n", e - ); } } - private void processScenarioDefinition(final Map nodeMap, final ScenarioDefinition child, - final AstNode currentParent) { - final AstNode childNode = new AstNode(child, currentParent); + private void processBackgroundDefinition( + final Map nodeMap, final Background background, final AstNode currentParent + ) { + final AstNode childNode = createAstNode(background, currentParent); + nodeMap.put(background.getLocation().getLine(), childNode); + for (Step step : background.getStepsList()) { + nodeMap.put(step.getLocation().getLine(), createAstNode(step, childNode)); + } + } + + private void processScenarioDefinition( + final Map nodeMap, final Scenario child, final AstNode currentParent) { + final AstNode childNode = createAstNode(child, currentParent); nodeMap.put(child.getLocation().getLine(), childNode); - for (Step step : child.getSteps()) { + for (Step step : child.getStepsList()) { nodeMap.put(step.getLocation().getLine(), createAstNode(step, childNode)); } - if (child instanceof ScenarioOutline) { - processScenarioOutlineExamples(nodeMap, (ScenarioOutline) child, childNode); + if (child.getExamplesCount() > 0) { + processScenarioOutlineExamples(nodeMap, child, childNode); } } - private void processScenarioOutlineExamples(final Map nodeMap, - final ScenarioOutline scenarioOutline, - final AstNode childNode) { - for (Examples examples : scenarioOutline.getExamples()) { - final AstNode examplesNode = createAstNode(examples, childNode); + private void processRuleDefinition( + final Map nodeMap, final RuleChild child, final AstNode currentParent) { + if (child.hasBackground()) { + processBackgroundDefinition(nodeMap, child.getBackground(), currentParent); + } else if (child.hasScenario()) { + processScenarioDefinition(nodeMap, child.getScenario(), currentParent); + } + } + + private void processScenarioOutlineExamples( + final Map nodeMap, final Scenario scenarioOutline, final AstNode parent + ) { + for (Examples examples : scenarioOutline.getExamplesList()) { + final AstNode examplesNode = createAstNode(examples, parent); final TableRow headerRow = examples.getTableHeader(); final AstNode headerNode = createAstNode(headerRow, examplesNode); nodeMap.put(headerRow.getLocation().getLine(), headerNode); - for (int i = 0; i < examples.getTableBody().size(); ++i) { - final TableRow examplesRow = examples.getTableBody().get(i); - final Node rowNode = createExamplesRowWrapperNode(examplesRow, i); - final AstNode expandedScenarioNode = createAstNode(rowNode, examplesNode); + for (int i = 0; i < examples.getTableBodyCount(); ++i) { + final TableRow examplesRow = examples.getTableBody(i); + final AstNode expandedScenarioNode = createAstNode(examplesRow, examplesNode); nodeMap.put(examplesRow.getLocation().getLine(), expandedScenarioNode); } } } - private static ExamplesRowWrapperNode createExamplesRowWrapperNode(final Node examplesRow, final int bodyRowIndex) { - return new ExamplesRowWrapperNode(examplesRow, bodyRowIndex); - } - - private static AstNode createAstNode(final Node node, final AstNode astNode) { - return new AstNode(node, astNode); - } - - static class ExamplesRowWrapperNode extends Node { - private final int bodyRowIndex; - - public int getBodyRowIndex() { - return bodyRowIndex; + public AstNode getAstNode(final URI path, final int line) { + if (!pathToNodeMap.containsKey(path)) { + parseGherkinSource(path); } - - ExamplesRowWrapperNode(final Node examplesRow, final int bodyRowIndex) { - super(examplesRow.getLocation()); - this.bodyRowIndex = bodyRowIndex; + if (pathToNodeMap.containsKey(path)) { + return pathToNodeMap.get(path).get(line); } + return null; } - static class AstNode { - private final Node node; - private final AstNode parent; - - public Node getNode() { - return node; - } + private AstNode createAstNode(final GeneratedMessageV3 node, final AstNode astNode) { + return createAstNode(node, astNode); + } - public AstNode getParent() { - return parent; - } + private static class AstNode { + private final GeneratedMessageV3 node; + private final AstNode parent; - AstNode(final Node node, final AstNode parent) { + AstNode(final GeneratedMessageV3 node, final AstNode parent) { this.node = node; this.parent = parent; } diff --git a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModelProxy.java b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModelProxy.java index ff2b9380b..84f757ff0 100644 --- a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModelProxy.java +++ b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModelProxy.java @@ -15,10 +15,10 @@ */ package io.qameta.allure.cucumber6jvm.testsourcemodel; -import gherkin.GherkinDialect; -import gherkin.GherkinDialectProvider; -import gherkin.ast.Feature; -import gherkin.ast.ScenarioDefinition; +import io.cucumber.gherkin.GherkinDialect; +import io.cucumber.gherkin.GherkinDialectProvider; +import io.cucumber.messages.Messages.GherkinDocument.Feature; +import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario; import io.cucumber.plugin.event.TestSourceRead; import java.net.URI; @@ -43,8 +43,8 @@ public Feature getFeature(final URI path) { return testSources.getFeature(path); } - public ScenarioDefinition getScenarioDefinition(final URI path, final int line) { - return testSources.getScenarioDefinition(testSources.getAstNode(path, line)); + public Scenario getScenarioDefinition(final URI path, final int line) { + return TestSourcesModel.getScenarioDefinition(testSources.getAstNode(path, line)); } public String getKeywordFromSource(final URI uri, final int stepLine) { diff --git a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/AllureCucumber6JvmTest.java b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/AllureCucumber6JvmTest.java index 548acaf41..06922634e 100644 --- a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/AllureCucumber6JvmTest.java +++ b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/AllureCucumber6JvmTest.java @@ -504,16 +504,16 @@ void shouldSupportDryRunForSimpleFeatures() { assertThat(testResults) .extracting(TestResult::getName, TestResult::getStatus) .containsExactlyInAnyOrder( - tuple("Add a to b", Status.SKIPPED) + tuple("Add a to b", Status.PASSED) ); assertThat(testResults.get(0).getSteps()) .extracting(StepResult::getName, StepResult::getStatus) .containsExactlyInAnyOrder( - tuple("Given a is 5", Status.SKIPPED), - tuple("And b is 10", Status.SKIPPED), - tuple("When I add a to b", Status.SKIPPED), - tuple("Then result is 15", Status.SKIPPED) + tuple("Given a is 5", Status.PASSED), + tuple("And b is 10", Status.PASSED), + tuple("When I add a to b", Status.PASSED), + tuple("Then result is 15", Status.PASSED) ); } @@ -529,28 +529,28 @@ void shouldSupportDryRunForHooks() { assertThat(testResults) .extracting(TestResult::getName, TestResult::getStatus) .startsWith( - tuple("Simple scenario with Before and After hooks", Status.SKIPPED) + tuple("Simple scenario with Before and After hooks", Status.PASSED) ); assertThat(writer.getTestResultContainers().get(0).getBefores()) .extracting(FixtureResult::getName, FixtureResult::getStatus) .containsExactlyInAnyOrder( - tuple("io.qameta.allure.cucumber6jvm.samples.HookSteps.beforeHook()", Status.SKIPPED) + tuple("io.qameta.allure.cucumber6jvm.samples.HookSteps.beforeHook()", Status.PASSED) ); assertThat(writer.getTestResultContainers().get(0).getAfters()) .extracting(FixtureResult::getName, FixtureResult::getStatus) .containsExactlyInAnyOrder( - tuple("io.qameta.allure.cucumber6jvm.samples.HookSteps.afterHook()", Status.SKIPPED) + tuple("io.qameta.allure.cucumber6jvm.samples.HookSteps.afterHook()", Status.PASSED) ); assertThat(testResults.get(0).getSteps()) .extracting(StepResult::getName, StepResult::getStatus) .containsExactlyInAnyOrder( - tuple("Given a is 7", Status.SKIPPED), - tuple("And b is 8", Status.SKIPPED), - tuple("When I add a to b", Status.SKIPPED), - tuple("Then result is 15", Status.SKIPPED) + tuple("Given a is 7", Status.PASSED), + tuple("And b is 8", Status.PASSED), + tuple("When I add a to b", Status.PASSED), + tuple("Then result is 15", Status.PASSED) ); } From 2840a98d905e87b3d79db3a14d04e5a72798f7e6 Mon Sep 17 00:00:00 2001 From: MetallFoX Date: Wed, 9 Dec 2020 20:30:08 +0300 Subject: [PATCH 2/2] TestSourcesModel.createAstNode recursive call fix --- .../allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java index 00575c7b6..8545fd102 100644 --- a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java +++ b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java @@ -171,7 +171,7 @@ public AstNode getAstNode(final URI path, final int line) { } private AstNode createAstNode(final GeneratedMessageV3 node, final AstNode astNode) { - return createAstNode(node, astNode); + return new AstNode(node, astNode); } private static class AstNode {