Skip to content

Commit e65f3fc

Browse files
authored
[Core] Add location to tag expression expcetion (#1979)
Updated the TagPredicate and the CoreHookDefinition to catch TagExpressionException and add information from the relevant classes to the exception message which is then wrapped in a RuntimeException and rethrown. The rethrow as a RuntimeException is due to the fact that the constructor for TagExpressionParser is package scope only and is not available in other packages. Fixes #1976.
1 parent c6cb8d6 commit e65f3fc

File tree

16 files changed

+227
-78
lines changed

16 files changed

+227
-78
lines changed

core/src/main/java/io/cucumber/core/filter/Filters.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.cucumber.core.filter;
22

33
import io.cucumber.core.gherkin.Pickle;
4+
import io.cucumber.tagexpressions.Expression;
45

56
import java.net.URI;
67
import java.util.Collection;
@@ -14,7 +15,7 @@ public final class Filters implements Predicate<Pickle> {
1415
private Predicate<Pickle> filter = t -> true;
1516

1617
public Filters(Options options) {
17-
List<String> tagExpressions = options.getTagExpressions();
18+
List<Expression> tagExpressions = options.getTagExpressions();
1819
if (!tagExpressions.isEmpty()) {
1920
this.filter = this.filter.and(new TagPredicate(tagExpressions));
2021
}

core/src/main/java/io/cucumber/core/filter/Options.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.cucumber.core.filter;
22

3+
import io.cucumber.tagexpressions.Expression;
4+
35
import java.net.URI;
46
import java.util.List;
57
import java.util.Map;
@@ -8,7 +10,7 @@
810

911
public interface Options {
1012

11-
List<String> getTagExpressions();
13+
List<Expression> getTagExpressions();
1214

1315
List<Pattern> getNameFilters();
1416

core/src/main/java/io/cucumber/core/filter/TagPredicate.java

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,17 @@
22

33
import io.cucumber.core.gherkin.Pickle;
44
import io.cucumber.tagexpressions.Expression;
5-
import io.cucumber.tagexpressions.TagExpressionParser;
65

7-
import java.util.ArrayList;
86
import java.util.List;
7+
import java.util.Objects;
98
import java.util.function.Predicate;
109

11-
import static java.util.Collections.emptyList;
12-
import static java.util.Collections.singletonList;
13-
1410
final class TagPredicate implements Predicate<Pickle> {
1511

16-
private final List<Expression> expressions = new ArrayList<>();
17-
18-
TagPredicate(String tagExpression) {
19-
this(tagExpression.isEmpty() ? emptyList() : singletonList(tagExpression));
20-
}
12+
private final List<Expression> expressions;
2113

22-
TagPredicate(List<String> tagExpressions) {
23-
if (tagExpressions == null) {
24-
return;
25-
}
26-
TagExpressionParser parser = new TagExpressionParser();
27-
for (String tagExpression : tagExpressions) {
28-
expressions.add(parser.parse(tagExpression));
29-
}
14+
TagPredicate(List<Expression> tagExpressions) {
15+
expressions = Objects.requireNonNull(tagExpressions);
3016
}
3117

3218
@Override

core/src/main/java/io/cucumber/core/options/CommandlineOptionsParser.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import io.cucumber.gherkin.GherkinDialect;
1010
import io.cucumber.gherkin.GherkinDialectProvider;
1111
import io.cucumber.gherkin.IGherkinDialectProvider;
12+
import io.cucumber.tagexpressions.TagExpressionParser;
1213

1314
import java.io.BufferedReader;
1415
import java.io.InputStream;
@@ -89,7 +90,7 @@ private RuntimeOptionsBuilder parse(List<String> args) {
8990
URI parse = GluePath.parse(gluePath);
9091
parsedOptions.addGlue(parse);
9192
} else if (arg.equals("--tags") || arg.equals("-t")) {
92-
parsedOptions.addTagFilter(removeArgFor(arg, args));
93+
parsedOptions.addTagFilter(TagExpressionParser.parse(removeArgFor(arg, args)));
9394
} else if (arg.equals("--plugin") || arg.equals("-p")) {
9495
parsedOptions.addPluginName(removeArgFor(arg, args));
9596
} else if (arg.equals("--no-dry-run") || arg.equals("--dry-run") || arg.equals("-d")) {

core/src/main/java/io/cucumber/core/options/CucumberOptionsAnnotationParser.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import io.cucumber.core.feature.FeatureWithLines;
66
import io.cucumber.core.feature.GluePath;
77
import io.cucumber.core.snippets.SnippetType;
8+
import io.cucumber.tagexpressions.TagExpressionException;
9+
import io.cucumber.tagexpressions.TagExpressionParser;
810

911
import java.nio.file.Path;
1012
import java.nio.file.Paths;
@@ -35,7 +37,7 @@ public RuntimeOptionsBuilder parse(Class<?> clazz) {
3537
if (options != null) {
3638
addDryRun(options, args);
3739
addMonochrome(options, args);
38-
addTags(options, args);
40+
addTags(classWithOptions, options, args);
3941
addPlugins(options, args);
4042
addStrict(options, args);
4143
addName(options, args);
@@ -45,6 +47,7 @@ public RuntimeOptionsBuilder parse(Class<?> clazz) {
4547
addObjectFactory(options, args);
4648
}
4749
}
50+
4851
addDefaultFeaturePathIfNoFeaturePathIsSpecified(args, clazz);
4952
addDefaultGlueIfNoOverridingGlueIsSpecified(args, clazz);
5053
return args;
@@ -66,10 +69,15 @@ private void addMonochrome(CucumberOptions options, RuntimeOptionsBuilder args)
6669
}
6770
}
6871

69-
private void addTags(CucumberOptions options, RuntimeOptionsBuilder args) {
72+
private void addTags(Class<?> clazz, CucumberOptions options, RuntimeOptionsBuilder args) {
7073
String tagExpression = options.tags();
7174
if (!tagExpression.isEmpty()) {
72-
args.addTagFilter(tagExpression);
75+
try {
76+
args.addTagFilter(TagExpressionParser.parse(tagExpression));
77+
} catch (TagExpressionException tee) {
78+
throw new IllegalArgumentException(String.format("Invalid tag expression at '%s'", clazz.getName()),
79+
tee);
80+
}
7381
}
7482
}
7583

core/src/main/java/io/cucumber/core/options/CucumberPropertiesParser.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.cucumber.core.exception.CucumberException;
44
import io.cucumber.core.feature.FeatureWithLines;
55
import io.cucumber.core.feature.GluePath;
6+
import io.cucumber.tagexpressions.TagExpressionParser;
67

78
import java.nio.file.Path;
89
import java.nio.file.Paths;
@@ -65,6 +66,7 @@ public RuntimeOptionsBuilder parse(Map<String, String> properties) {
6566
FEATURES_PROPERTY_NAME,
6667
splitAndThenFlatMap(CucumberPropertiesParser::parseFeatureFile),
6768
builder::addFeature);
69+
6870
parseAll(properties,
6971
FEATURES_PROPERTY_NAME,
7072
splitAndThenFlatMap(CucumberPropertiesParser::parseRerunFile),
@@ -77,7 +79,7 @@ public RuntimeOptionsBuilder parse(Map<String, String> properties) {
7779

7880
parse(properties,
7981
FILTER_TAGS_PROPERTY_NAME,
80-
Function.identity(),
82+
TagExpressionParser::parse,
8183
builder::addTagFilter);
8284

8385
parseAll(properties,
@@ -99,6 +101,7 @@ public RuntimeOptionsBuilder parse(Map<String, String> properties) {
99101
SNIPPET_TYPE_PROPERTY_NAME,
100102
SnippetTypeParser::parseSnippetType,
101103
builder::setSnippetType);
104+
102105
parse(properties,
103106
WIP_PROPERTY_NAME,
104107
Boolean::parseBoolean,

core/src/main/java/io/cucumber/core/options/RuntimeOptions.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import io.cucumber.core.order.PickleOrder;
66
import io.cucumber.core.order.StandardPickleOrders;
77
import io.cucumber.core.snippets.SnippetType;
8+
import io.cucumber.tagexpressions.Expression;
89

910
import java.net.URI;
1011
import java.util.ArrayList;
@@ -30,7 +31,7 @@ public final class RuntimeOptions implements
3031
io.cucumber.core.backend.Options {
3132

3233
private final List<URI> glue = new ArrayList<>();
33-
private final List<String> tagExpressions = new ArrayList<>();
34+
private final List<Expression> tagExpressions = new ArrayList<>();
3435
private final List<Pattern> nameFilters = new ArrayList<>();
3536
private final List<FeatureWithLines> featurePaths = new ArrayList<>();
3637
private final List<Plugin> formatters = new ArrayList<>();
@@ -173,7 +174,7 @@ void setFeaturePaths(List<FeatureWithLines> featurePaths) {
173174
}
174175

175176
@Override
176-
public List<String> getTagExpressions() {
177+
public List<Expression> getTagExpressions() {
177178
return unmodifiableList(tagExpressions);
178179
}
179180

@@ -215,7 +216,7 @@ void setCount(int count) {
215216
this.count = count;
216217
}
217218

218-
void setTagExpressions(List<String> tagExpressions) {
219+
void setTagExpressions(List<Expression> tagExpressions) {
219220
this.tagExpressions.clear();
220221
this.tagExpressions.addAll(tagExpressions);
221222
}

core/src/main/java/io/cucumber/core/options/RuntimeOptionsBuilder.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.cucumber.core.order.PickleOrder;
77
import io.cucumber.core.plugin.Options;
88
import io.cucumber.core.snippets.SnippetType;
9+
import io.cucumber.tagexpressions.Expression;
910

1011
import java.net.URI;
1112
import java.util.ArrayList;
@@ -16,7 +17,7 @@
1617

1718
public final class RuntimeOptionsBuilder {
1819

19-
private final List<String> parsedTagFilters = new ArrayList<>();
20+
private final List<Expression> parsedTagFilters = new ArrayList<>();
2021
private final List<Pattern> parsedNameFilters = new ArrayList<>();
2122
private final List<FeatureWithLines> parsedFeaturePaths = new ArrayList<>();
2223
private final List<URI> parsedGlue = new ArrayList<>();
@@ -63,7 +64,7 @@ public RuntimeOptionsBuilder addPluginName(String name) {
6364
return this;
6465
}
6566

66-
public RuntimeOptionsBuilder addTagFilter(String tagExpression) {
67+
public RuntimeOptionsBuilder addTagFilter(Expression tagExpression) {
6768
this.parsedTagFilters.add(tagExpression);
6869
return this;
6970
}

core/src/main/java/io/cucumber/core/runner/CoreHookDefinition.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.cucumber.core.backend.ScenarioScoped;
55
import io.cucumber.core.backend.TestCaseState;
66
import io.cucumber.tagexpressions.Expression;
7+
import io.cucumber.tagexpressions.TagExpressionException;
78
import io.cucumber.tagexpressions.TagExpressionParser;
89

910
import java.util.List;
@@ -20,7 +21,14 @@ class CoreHookDefinition {
2021
private CoreHookDefinition(UUID id, HookDefinition delegate) {
2122
this.id = requireNonNull(id);
2223
this.delegate = delegate;
23-
this.tagExpression = new TagExpressionParser().parse(delegate.getTagExpression());
24+
25+
try {
26+
this.tagExpression = TagExpressionParser.parse(delegate.getTagExpression());
27+
} catch (TagExpressionException tee) {
28+
throw new IllegalArgumentException(
29+
String.format("Invalid tag expression at '%s'", delegate.getLocation()),
30+
tee);
31+
}
2432
}
2533

2634
static CoreHookDefinition create(HookDefinition hookDefinition) {

core/src/test/java/io/cucumber/core/filter/TagPredicateTest.java

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import io.cucumber.core.feature.TestFeatureParser;
44
import io.cucumber.core.gherkin.Feature;
55
import io.cucumber.core.gherkin.Pickle;
6+
import io.cucumber.tagexpressions.TagExpressionParser;
67
import org.junit.jupiter.api.Test;
78

8-
import static java.util.Arrays.asList;
9-
import static java.util.Collections.singletonList;
9+
import java.util.stream.Collectors;
10+
11+
import static java.util.Arrays.stream;
1012
import static org.junit.jupiter.api.Assertions.assertFalse;
1113
import static org.junit.jupiter.api.Assertions.assertTrue;
1214

@@ -15,101 +17,106 @@ class TagPredicateTest {
1517
@Test
1618
void empty_tag_predicate_matches_pickle_with_any_tags() {
1719
Pickle pickle = createPickleWithTags("@FOO");
18-
TagPredicate predicate = new TagPredicate("");
20+
TagPredicate predicate = createPredicate("");
1921
assertTrue(predicate.test(pickle));
2022
}
2123

22-
private Pickle createPickleWithTags(String... tags) {
23-
Feature feature = TestFeatureParser.parse("" +
24-
"Feature: Test feature\n" +
25-
" " + String.join(" ", tags) + "\n" +
26-
" Scenario: Test scenario\n" +
27-
" Given I have 4 cukes in my belly\n");
28-
return feature.getPickles().get(0);
29-
}
30-
3124
@Test
3225
void list_of_empty_tag_predicates_matches_pickle_with_any_tags() {
3326
Pickle pickle = createPickleWithTags("@FOO");
34-
TagPredicate predicate = new TagPredicate(asList("", ""));
27+
TagPredicate predicate = createPredicate("", "");
3528
assertTrue(predicate.test(pickle));
3629
}
3730

3831
@Test
3932
void single_tag_predicate_does_not_match_pickle_with_no_tags() {
4033
Pickle pickle = createPickleWithTags();
41-
TagPredicate predicate = new TagPredicate("@FOO");
34+
TagPredicate predicate = createPredicate("@FOO");
4235
assertFalse(predicate.test(pickle));
4336
}
4437

4538
@Test
4639
void single_tag_predicate_matches_pickle_with_same_single_tag() {
4740
Pickle pickle = createPickleWithTags("@FOO");
48-
TagPredicate predicate = new TagPredicate("@FOO");
41+
TagPredicate predicate = createPredicate("@FOO");
4942
assertTrue(predicate.test(pickle));
5043
}
5144

5245
@Test
5346
void single_tag_predicate_matches_pickle_with_more_tags() {
5447
Pickle pickle = createPickleWithTags("@FOO", "@BAR");
55-
TagPredicate predicate = new TagPredicate("@FOO");
48+
TagPredicate predicate = createPredicate("@FOO");
5649
assertTrue(predicate.test(pickle));
5750
}
5851

5952
@Test
6053
void single_tag_predicate_does_not_match_pickle_with_different_single_tag() {
6154
Pickle pickle = createPickleWithTags("@BAR");
62-
TagPredicate predicate = new TagPredicate("@FOO");
55+
TagPredicate predicate = createPredicate("@FOO");
6356
assertFalse(predicate.test(pickle));
6457
}
6558

6659
@Test
6760
void not_tag_predicate_matches_pickle_with_no_tags() {
6861
Pickle pickle = createPickleWithTags();
69-
TagPredicate predicate = new TagPredicate(singletonList("not @FOO"));
62+
TagPredicate predicate = createPredicate("not @FOO");
7063
assertTrue(predicate.test(pickle));
7164
}
7265

7366
@Test
7467
void not_tag_predicate_does_not_match_pickle_with_same_single_tag() {
7568
Pickle pickle = createPickleWithTags("@FOO");
76-
TagPredicate predicate = new TagPredicate(singletonList("not @FOO"));
69+
TagPredicate predicate = createPredicate("not @FOO");
7770
assertFalse(predicate.test(pickle));
7871
}
7972

8073
@Test
8174
void not_tag_predicate_matches_pickle_with_different_single_tag() {
8275
Pickle pickle = createPickleWithTags("@BAR");
83-
TagPredicate predicate = new TagPredicate(singletonList("not @FOO"));
76+
TagPredicate predicate = createPredicate("not @FOO");
8477
assertTrue(predicate.test(pickle));
8578
}
8679

8780
@Test
8881
void and_tag_predicate_matches_pickle_with_all_tags() {
8982
Pickle pickle = createPickleWithTags("@FOO", "@BAR");
90-
TagPredicate predicate = new TagPredicate(singletonList("@FOO and @BAR"));
83+
TagPredicate predicate = createPredicate("@FOO and @BAR");
9184
assertTrue(predicate.test(pickle));
9285
}
9386

9487
@Test
9588
void and_tag_predicate_does_not_match_pickle_with_one_of_the_tags() {
9689
Pickle pickle = createPickleWithTags("@FOO");
97-
TagPredicate predicate = new TagPredicate(singletonList("@FOO and @BAR"));
90+
TagPredicate predicate = createPredicate("@FOO and @BAR");
9891
assertFalse(predicate.test(pickle));
9992
}
10093

10194
@Test
10295
void or_tag_predicate_matches_pickle_with_one_of_the_tags() {
10396
Pickle pickle = createPickleWithTags("@FOO");
104-
TagPredicate predicate = new TagPredicate(singletonList("@FOO or @BAR"));
97+
TagPredicate predicate = createPredicate("@FOO or @BAR");
10598
assertTrue(predicate.test(pickle));
10699
}
107100

108101
@Test
109102
void or_tag_predicate_does_not_match_pickle_none_of_the_tags() {
110103
Pickle pickle = createPickleWithTags();
111-
TagPredicate predicate = new TagPredicate(singletonList("@FOO or @BAR"));
104+
TagPredicate predicate = createPredicate("@FOO or @BAR");
112105
assertFalse(predicate.test(pickle));
113106
}
114107

108+
private static Pickle createPickleWithTags(String... tags) {
109+
Feature feature = TestFeatureParser.parse("" +
110+
"Feature: Test feature\n" +
111+
" " + String.join(" ", tags) + "\n" +
112+
" Scenario: Test scenario\n" +
113+
" Given I have 4 cukes in my belly\n");
114+
return feature.getPickles().get(0);
115+
}
116+
117+
private static TagPredicate createPredicate(String... expressions) {
118+
return new TagPredicate(stream(expressions)
119+
.map(TagExpressionParser::parse)
120+
.collect(Collectors.toList()));
121+
}
115122
}

0 commit comments

Comments
 (0)