1515 */
1616package io .qameta .allure .cucumber6jvm .testsourcemodel ;
1717
18- import gherkin .AstBuilder ;
19- import gherkin . Parser ;
20- import gherkin . ParserException ;
21- import gherkin . TokenMatcher ;
22- import gherkin . ast . Examples ;
23- import gherkin . ast . Feature ;
24- import gherkin . ast . GherkinDocument ;
25- import gherkin . ast . Node ;
26- import gherkin . ast . ScenarioDefinition ;
27- import gherkin . ast . ScenarioOutline ;
28- import gherkin . ast . Step ;
29- import gherkin . ast . TableRow ;
18+ import io . cucumber . gherkin .Gherkin ;
19+ import io . cucumber . messages . Messages ;
20+ import io . cucumber . messages . Messages . GherkinDocument ;
21+ import io . cucumber . messages . Messages . GherkinDocument . Feature ;
22+ import io . cucumber . messages . Messages . GherkinDocument . Feature . Background ;
23+ import io . cucumber . messages . Messages . GherkinDocument . Feature . FeatureChild ;
24+ import io . cucumber . messages . Messages . GherkinDocument . Feature . FeatureChild . RuleChild ;
25+ import io . cucumber . messages . Messages . GherkinDocument . Feature . Scenario ;
26+ import io . cucumber . messages . Messages . GherkinDocument . Feature . Scenario . Examples ;
27+ import io . cucumber . messages . Messages . GherkinDocument . Feature . Step ;
28+ import io . cucumber . messages . Messages . GherkinDocument . Feature . TableRow ;
29+ import io . cucumber . messages . internal . com . google . protobuf . GeneratedMessageV3 ;
3030import io .cucumber .plugin .event .TestSourceRead ;
31+
3132import java .net .URI ;
3233import java .util .HashMap ;
34+ import java .util .List ;
3335import java .util .Map ;
36+ import java .util .UUID ;
37+
38+ import static io .cucumber .gherkin .Gherkin .makeSourceEnvelope ;
39+ import static java .util .Collections .singletonList ;
40+ import static java .util .stream .Collectors .toList ;
3441
35- public final class TestSourcesModel {
42+ final class TestSourcesModel {
3643 private final Map <URI , TestSourceRead > pathToReadEventMap = new HashMap <>();
3744 private final Map <URI , GherkinDocument > pathToAstMap = new HashMap <>();
3845 private final Map <URI , Map <Integer , AstNode >> pathToNodeMap = new HashMap <>();
3946
40- public static ScenarioDefinition getScenarioDefinition (final AstNode astNode ) {
41- return astNode .node instanceof ScenarioDefinition ? (ScenarioDefinition ) astNode .node
42- : (ScenarioDefinition ) astNode .parent .parent .node ;
47+ public static Scenario getScenarioDefinition (final AstNode astNode ) {
48+ AstNode candidate = astNode ;
49+ while (candidate != null && !(candidate .node instanceof Scenario )) {
50+ candidate = candidate .parent ;
51+ }
52+ return candidate == null ? null : (Scenario ) candidate .node ;
4353 }
4454
4555 public void addTestSourceReadEvent (final URI path , final TestSourceRead event ) {
@@ -56,102 +66,119 @@ public Feature getFeature(final URI path) {
5666 return null ;
5767 }
5868
59- public AstNode getAstNode (final URI path , final int line ) {
60- if (!pathToNodeMap .containsKey (path )) {
61- parseGherkinSource (path );
62- }
63- if (pathToNodeMap .containsKey (path )) {
64- return pathToNodeMap .get (path ).get (line );
65- }
66- return null ;
67- }
68-
6969 private void parseGherkinSource (final URI path ) {
7070 if (!pathToReadEventMap .containsKey (path )) {
7171 return ;
7272 }
73- final Parser <GherkinDocument > parser = new Parser <>(new AstBuilder ());
74- final TokenMatcher matcher = new TokenMatcher ();
75- try {
76- final GherkinDocument gherkinDocument = parser .parse (pathToReadEventMap .get (path ).getSource (),
77- matcher );
78- pathToAstMap .put (path , gherkinDocument );
79- final Map <Integer , AstNode > nodeMap = new HashMap <>();
80- final AstNode currentParent = new AstNode (gherkinDocument .getFeature (), null );
81- for (ScenarioDefinition child : gherkinDocument .getFeature ().getChildren ()) {
82- processScenarioDefinition (nodeMap , child , currentParent );
73+ final String source = pathToReadEventMap .get (path ).getSource ();
74+
75+ final List <Messages .Envelope > sources = singletonList (
76+ makeSourceEnvelope (source , path .toString ()));
77+
78+ final List <Messages .Envelope > envelopes = Gherkin .fromSources (
79+ sources ,
80+ true ,
81+ true ,
82+ true ,
83+ () -> String .valueOf (UUID .randomUUID ())).collect (toList ());
84+
85+ final GherkinDocument gherkinDocument = envelopes .stream ()
86+ .filter (Messages .Envelope ::hasGherkinDocument )
87+ .map (Messages .Envelope ::getGherkinDocument )
88+ .findFirst ()
89+ .orElse (null );
90+
91+ pathToAstMap .put (path , gherkinDocument );
92+ final Map <Integer , AstNode > nodeMap = new HashMap <>();
93+ final AstNode currentParent = createAstNode (gherkinDocument .getFeature (), null );
94+ for (FeatureChild child : gherkinDocument .getFeature ().getChildrenList ()) {
95+ processFeatureDefinition (nodeMap , child , currentParent );
96+ }
97+ pathToNodeMap .put (path , nodeMap );
98+
99+ }
100+
101+ private void processFeatureDefinition (
102+ final Map <Integer , AstNode > nodeMap , final FeatureChild child , final AstNode currentParent ) {
103+ if (child .hasBackground ()) {
104+ processBackgroundDefinition (nodeMap , child .getBackground (), currentParent );
105+ } else if (child .hasScenario ()) {
106+ processScenarioDefinition (nodeMap , child .getScenario (), currentParent );
107+ } else if (child .hasRule ()) {
108+ final AstNode childNode = createAstNode (child .getRule (), currentParent );
109+ nodeMap .put (child .getRule ().getLocation ().getLine (), childNode );
110+ for (RuleChild ruleChild : child .getRule ().getChildrenList ()) {
111+ processRuleDefinition (nodeMap , ruleChild , childNode );
83112 }
84- pathToNodeMap .put (path , nodeMap );
85- } catch (ParserException e ) {
86- throw new IllegalStateException ("You are using a plugin that only supports till Gherkin 5.\n "
87- + "Please check if the Gherkin provided follows the standard of Gherkin 5\n " , e
88- );
89113 }
90114 }
91115
92- private void processScenarioDefinition (final Map <Integer , AstNode > nodeMap , final ScenarioDefinition child ,
93- final AstNode currentParent ) {
94- final AstNode childNode = new AstNode (child , currentParent );
116+ private void processBackgroundDefinition (
117+ final Map <Integer , AstNode > nodeMap , final Background background , final AstNode currentParent
118+ ) {
119+ final AstNode childNode = createAstNode (background , currentParent );
120+ nodeMap .put (background .getLocation ().getLine (), childNode );
121+ for (Step step : background .getStepsList ()) {
122+ nodeMap .put (step .getLocation ().getLine (), createAstNode (step , childNode ));
123+ }
124+ }
125+
126+ private void processScenarioDefinition (
127+ final Map <Integer , AstNode > nodeMap , final Scenario child , final AstNode currentParent ) {
128+ final AstNode childNode = createAstNode (child , currentParent );
95129 nodeMap .put (child .getLocation ().getLine (), childNode );
96- for (Step step : child .getSteps ()) {
130+ for (Step step : child .getStepsList ()) {
97131 nodeMap .put (step .getLocation ().getLine (), createAstNode (step , childNode ));
98132 }
99- if (child instanceof ScenarioOutline ) {
100- processScenarioOutlineExamples (nodeMap , ( ScenarioOutline ) child , childNode );
133+ if (child . getExamplesCount () > 0 ) {
134+ processScenarioOutlineExamples (nodeMap , child , childNode );
101135 }
102136 }
103137
104- private void processScenarioOutlineExamples (final Map <Integer , AstNode > nodeMap ,
105- final ScenarioOutline scenarioOutline ,
106- final AstNode childNode ) {
107- for (Examples examples : scenarioOutline .getExamples ()) {
108- final AstNode examplesNode = createAstNode (examples , childNode );
138+ private void processRuleDefinition (
139+ final Map <Integer , AstNode > nodeMap , final RuleChild child , final AstNode currentParent ) {
140+ if (child .hasBackground ()) {
141+ processBackgroundDefinition (nodeMap , child .getBackground (), currentParent );
142+ } else if (child .hasScenario ()) {
143+ processScenarioDefinition (nodeMap , child .getScenario (), currentParent );
144+ }
145+ }
146+
147+ private void processScenarioOutlineExamples (
148+ final Map <Integer , AstNode > nodeMap , final Scenario scenarioOutline , final AstNode parent
149+ ) {
150+ for (Examples examples : scenarioOutline .getExamplesList ()) {
151+ final AstNode examplesNode = createAstNode (examples , parent );
109152 final TableRow headerRow = examples .getTableHeader ();
110153 final AstNode headerNode = createAstNode (headerRow , examplesNode );
111154 nodeMap .put (headerRow .getLocation ().getLine (), headerNode );
112- for (int i = 0 ; i < examples .getTableBody ().size (); ++i ) {
113- final TableRow examplesRow = examples .getTableBody ().get (i );
114- final Node rowNode = createExamplesRowWrapperNode (examplesRow , i );
115- final AstNode expandedScenarioNode = createAstNode (rowNode , examplesNode );
155+ for (int i = 0 ; i < examples .getTableBodyCount (); ++i ) {
156+ final TableRow examplesRow = examples .getTableBody (i );
157+ final AstNode expandedScenarioNode = createAstNode (examplesRow , examplesNode );
116158 nodeMap .put (examplesRow .getLocation ().getLine (), expandedScenarioNode );
117159 }
118160 }
119161 }
120162
121- private static ExamplesRowWrapperNode createExamplesRowWrapperNode (final Node examplesRow , final int bodyRowIndex ) {
122- return new ExamplesRowWrapperNode (examplesRow , bodyRowIndex );
123- }
124-
125- private static AstNode createAstNode (final Node node , final AstNode astNode ) {
126- return new AstNode (node , astNode );
127- }
128-
129- static class ExamplesRowWrapperNode extends Node {
130- private final int bodyRowIndex ;
131-
132- public int getBodyRowIndex () {
133- return bodyRowIndex ;
163+ public AstNode getAstNode (final URI path , final int line ) {
164+ if (!pathToNodeMap .containsKey (path )) {
165+ parseGherkinSource (path );
134166 }
135-
136- ExamplesRowWrapperNode (final Node examplesRow , final int bodyRowIndex ) {
137- super (examplesRow .getLocation ());
138- this .bodyRowIndex = bodyRowIndex ;
167+ if (pathToNodeMap .containsKey (path )) {
168+ return pathToNodeMap .get (path ).get (line );
139169 }
170+ return null ;
140171 }
141172
142- static class AstNode {
143- private final Node node ;
144- private final AstNode parent ;
145-
146- public Node getNode () {
147- return node ;
148- }
173+ private AstNode createAstNode (final GeneratedMessageV3 node , final AstNode astNode ) {
174+ return createAstNode (node , astNode );
175+ }
149176
150- public AstNode getParent () {
151- return parent ;
152- }
177+ private static class AstNode {
178+ private final GeneratedMessageV3 node ;
179+ private final AstNode parent ;
153180
154- AstNode (final Node node , final AstNode parent ) {
181+ AstNode (final GeneratedMessageV3 node , final AstNode parent ) {
155182 this .node = node ;
156183 this .parent = parent ;
157184 }
0 commit comments