Skip to content

[Core] Add location to tag expression expcetion #1979

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jun 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion core/src/main/java/io/cucumber/core/filter/Filters.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.cucumber.core.filter;

import io.cucumber.core.gherkin.Pickle;
import io.cucumber.tagexpressions.Expression;

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

public Filters(Options options) {
List<String> tagExpressions = options.getTagExpressions();
List<Expression> tagExpressions = options.getTagExpressions();
if (!tagExpressions.isEmpty()) {
this.filter = this.filter.and(new TagPredicate(tagExpressions));
}
Expand Down
4 changes: 3 additions & 1 deletion core/src/main/java/io/cucumber/core/filter/Options.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.cucumber.core.filter;

import io.cucumber.tagexpressions.Expression;

import java.net.URI;
import java.util.List;
import java.util.Map;
Expand All @@ -8,7 +10,7 @@

public interface Options {

List<String> getTagExpressions();
List<Expression> getTagExpressions();

List<Pattern> getNameFilters();

Expand Down
22 changes: 4 additions & 18 deletions core/src/main/java/io/cucumber/core/filter/TagPredicate.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,17 @@

import io.cucumber.core.gherkin.Pickle;
import io.cucumber.tagexpressions.Expression;
import io.cucumber.tagexpressions.TagExpressionParser;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;

final class TagPredicate implements Predicate<Pickle> {

private final List<Expression> expressions = new ArrayList<>();

TagPredicate(String tagExpression) {
this(tagExpression.isEmpty() ? emptyList() : singletonList(tagExpression));
}
private final List<Expression> expressions;

TagPredicate(List<String> tagExpressions) {
if (tagExpressions == null) {
return;
}
TagExpressionParser parser = new TagExpressionParser();
for (String tagExpression : tagExpressions) {
expressions.add(parser.parse(tagExpression));
}
TagPredicate(List<Expression> tagExpressions) {
expressions = Objects.requireNonNull(tagExpressions);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.cucumber.gherkin.GherkinDialect;
import io.cucumber.gherkin.GherkinDialectProvider;
import io.cucumber.gherkin.IGherkinDialectProvider;
import io.cucumber.tagexpressions.TagExpressionParser;

import java.io.BufferedReader;
import java.io.InputStream;
Expand Down Expand Up @@ -89,7 +90,7 @@ private RuntimeOptionsBuilder parse(List<String> args) {
URI parse = GluePath.parse(gluePath);
parsedOptions.addGlue(parse);
} else if (arg.equals("--tags") || arg.equals("-t")) {
parsedOptions.addTagFilter(removeArgFor(arg, args));
parsedOptions.addTagFilter(TagExpressionParser.parse(removeArgFor(arg, args)));
} else if (arg.equals("--plugin") || arg.equals("-p")) {
parsedOptions.addPluginName(removeArgFor(arg, args));
} else if (arg.equals("--no-dry-run") || arg.equals("--dry-run") || arg.equals("-d")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import io.cucumber.core.feature.FeatureWithLines;
import io.cucumber.core.feature.GluePath;
import io.cucumber.core.snippets.SnippetType;
import io.cucumber.tagexpressions.TagExpressionException;
import io.cucumber.tagexpressions.TagExpressionParser;

import java.nio.file.Path;
import java.nio.file.Paths;
Expand Down Expand Up @@ -35,7 +37,7 @@ public RuntimeOptionsBuilder parse(Class<?> clazz) {
if (options != null) {
addDryRun(options, args);
addMonochrome(options, args);
addTags(options, args);
addTags(classWithOptions, options, args);
addPlugins(options, args);
addStrict(options, args);
addName(options, args);
Expand All @@ -45,6 +47,7 @@ public RuntimeOptionsBuilder parse(Class<?> clazz) {
addObjectFactory(options, args);
}
}

addDefaultFeaturePathIfNoFeaturePathIsSpecified(args, clazz);
addDefaultGlueIfNoOverridingGlueIsSpecified(args, clazz);
return args;
Expand All @@ -66,10 +69,15 @@ private void addMonochrome(CucumberOptions options, RuntimeOptionsBuilder args)
}
}

private void addTags(CucumberOptions options, RuntimeOptionsBuilder args) {
private void addTags(Class<?> clazz, CucumberOptions options, RuntimeOptionsBuilder args) {
String tagExpression = options.tags();
if (!tagExpression.isEmpty()) {
args.addTagFilter(tagExpression);
try {
args.addTagFilter(TagExpressionParser.parse(tagExpression));
} catch (TagExpressionException tee) {
throw new IllegalArgumentException(String.format("Invalid tag expression at '%s'", clazz.getName()),
tee);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.cucumber.core.exception.CucumberException;
import io.cucumber.core.feature.FeatureWithLines;
import io.cucumber.core.feature.GluePath;
import io.cucumber.tagexpressions.TagExpressionParser;

import java.nio.file.Path;
import java.nio.file.Paths;
Expand Down Expand Up @@ -65,6 +66,7 @@ public RuntimeOptionsBuilder parse(Map<String, String> properties) {
FEATURES_PROPERTY_NAME,
splitAndThenFlatMap(CucumberPropertiesParser::parseFeatureFile),
builder::addFeature);

parseAll(properties,
FEATURES_PROPERTY_NAME,
splitAndThenFlatMap(CucumberPropertiesParser::parseRerunFile),
Expand All @@ -77,7 +79,7 @@ public RuntimeOptionsBuilder parse(Map<String, String> properties) {

parse(properties,
FILTER_TAGS_PROPERTY_NAME,
Function.identity(),
TagExpressionParser::parse,
builder::addTagFilter);

parseAll(properties,
Expand All @@ -99,6 +101,7 @@ public RuntimeOptionsBuilder parse(Map<String, String> properties) {
SNIPPET_TYPE_PROPERTY_NAME,
SnippetTypeParser::parseSnippetType,
builder::setSnippetType);

parse(properties,
WIP_PROPERTY_NAME,
Boolean::parseBoolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.cucumber.core.order.PickleOrder;
import io.cucumber.core.order.StandardPickleOrders;
import io.cucumber.core.snippets.SnippetType;
import io.cucumber.tagexpressions.Expression;

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

private final List<URI> glue = new ArrayList<>();
private final List<String> tagExpressions = new ArrayList<>();
private final List<Expression> tagExpressions = new ArrayList<>();
private final List<Pattern> nameFilters = new ArrayList<>();
private final List<FeatureWithLines> featurePaths = new ArrayList<>();
private final List<Plugin> formatters = new ArrayList<>();
Expand Down Expand Up @@ -173,7 +174,7 @@ void setFeaturePaths(List<FeatureWithLines> featurePaths) {
}

@Override
public List<String> getTagExpressions() {
public List<Expression> getTagExpressions() {
return unmodifiableList(tagExpressions);
}

Expand Down Expand Up @@ -215,7 +216,7 @@ void setCount(int count) {
this.count = count;
}

void setTagExpressions(List<String> tagExpressions) {
void setTagExpressions(List<Expression> tagExpressions) {
this.tagExpressions.clear();
this.tagExpressions.addAll(tagExpressions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.cucumber.core.order.PickleOrder;
import io.cucumber.core.plugin.Options;
import io.cucumber.core.snippets.SnippetType;
import io.cucumber.tagexpressions.Expression;

import java.net.URI;
import java.util.ArrayList;
Expand All @@ -16,7 +17,7 @@

public final class RuntimeOptionsBuilder {

private final List<String> parsedTagFilters = new ArrayList<>();
private final List<Expression> parsedTagFilters = new ArrayList<>();
private final List<Pattern> parsedNameFilters = new ArrayList<>();
private final List<FeatureWithLines> parsedFeaturePaths = new ArrayList<>();
private final List<URI> parsedGlue = new ArrayList<>();
Expand Down Expand Up @@ -63,7 +64,7 @@ public RuntimeOptionsBuilder addPluginName(String name) {
return this;
}

public RuntimeOptionsBuilder addTagFilter(String tagExpression) {
public RuntimeOptionsBuilder addTagFilter(Expression tagExpression) {
this.parsedTagFilters.add(tagExpression);
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.cucumber.core.backend.ScenarioScoped;
import io.cucumber.core.backend.TestCaseState;
import io.cucumber.tagexpressions.Expression;
import io.cucumber.tagexpressions.TagExpressionException;
import io.cucumber.tagexpressions.TagExpressionParser;

import java.util.List;
Expand All @@ -20,7 +21,14 @@ class CoreHookDefinition {
private CoreHookDefinition(UUID id, HookDefinition delegate) {
this.id = requireNonNull(id);
this.delegate = delegate;
this.tagExpression = new TagExpressionParser().parse(delegate.getTagExpression());

try {
this.tagExpression = TagExpressionParser.parse(delegate.getTagExpression());
} catch (TagExpressionException tee) {
throw new IllegalArgumentException(
String.format("Invalid tag expression at '%s'", delegate.getLocation()),
tee);
}
}

static CoreHookDefinition create(HookDefinition hookDefinition) {
Expand Down
55 changes: 31 additions & 24 deletions core/src/test/java/io/cucumber/core/filter/TagPredicateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import io.cucumber.core.feature.TestFeatureParser;
import io.cucumber.core.gherkin.Feature;
import io.cucumber.core.gherkin.Pickle;
import io.cucumber.tagexpressions.TagExpressionParser;
import org.junit.jupiter.api.Test;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import java.util.stream.Collectors;

import static java.util.Arrays.stream;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand All @@ -15,101 +17,106 @@ class TagPredicateTest {
@Test
void empty_tag_predicate_matches_pickle_with_any_tags() {
Pickle pickle = createPickleWithTags("@FOO");
TagPredicate predicate = new TagPredicate("");
TagPredicate predicate = createPredicate("");
assertTrue(predicate.test(pickle));
}

private Pickle createPickleWithTags(String... tags) {
Feature feature = TestFeatureParser.parse("" +
"Feature: Test feature\n" +
" " + String.join(" ", tags) + "\n" +
" Scenario: Test scenario\n" +
" Given I have 4 cukes in my belly\n");
return feature.getPickles().get(0);
}

@Test
void list_of_empty_tag_predicates_matches_pickle_with_any_tags() {
Pickle pickle = createPickleWithTags("@FOO");
TagPredicate predicate = new TagPredicate(asList("", ""));
TagPredicate predicate = createPredicate("", "");
assertTrue(predicate.test(pickle));
}

@Test
void single_tag_predicate_does_not_match_pickle_with_no_tags() {
Pickle pickle = createPickleWithTags();
TagPredicate predicate = new TagPredicate("@FOO");
TagPredicate predicate = createPredicate("@FOO");
assertFalse(predicate.test(pickle));
}

@Test
void single_tag_predicate_matches_pickle_with_same_single_tag() {
Pickle pickle = createPickleWithTags("@FOO");
TagPredicate predicate = new TagPredicate("@FOO");
TagPredicate predicate = createPredicate("@FOO");
assertTrue(predicate.test(pickle));
}

@Test
void single_tag_predicate_matches_pickle_with_more_tags() {
Pickle pickle = createPickleWithTags("@FOO", "@BAR");
TagPredicate predicate = new TagPredicate("@FOO");
TagPredicate predicate = createPredicate("@FOO");
assertTrue(predicate.test(pickle));
}

@Test
void single_tag_predicate_does_not_match_pickle_with_different_single_tag() {
Pickle pickle = createPickleWithTags("@BAR");
TagPredicate predicate = new TagPredicate("@FOO");
TagPredicate predicate = createPredicate("@FOO");
assertFalse(predicate.test(pickle));
}

@Test
void not_tag_predicate_matches_pickle_with_no_tags() {
Pickle pickle = createPickleWithTags();
TagPredicate predicate = new TagPredicate(singletonList("not @FOO"));
TagPredicate predicate = createPredicate("not @FOO");
assertTrue(predicate.test(pickle));
}

@Test
void not_tag_predicate_does_not_match_pickle_with_same_single_tag() {
Pickle pickle = createPickleWithTags("@FOO");
TagPredicate predicate = new TagPredicate(singletonList("not @FOO"));
TagPredicate predicate = createPredicate("not @FOO");
assertFalse(predicate.test(pickle));
}

@Test
void not_tag_predicate_matches_pickle_with_different_single_tag() {
Pickle pickle = createPickleWithTags("@BAR");
TagPredicate predicate = new TagPredicate(singletonList("not @FOO"));
TagPredicate predicate = createPredicate("not @FOO");
assertTrue(predicate.test(pickle));
}

@Test
void and_tag_predicate_matches_pickle_with_all_tags() {
Pickle pickle = createPickleWithTags("@FOO", "@BAR");
TagPredicate predicate = new TagPredicate(singletonList("@FOO and @BAR"));
TagPredicate predicate = createPredicate("@FOO and @BAR");
assertTrue(predicate.test(pickle));
}

@Test
void and_tag_predicate_does_not_match_pickle_with_one_of_the_tags() {
Pickle pickle = createPickleWithTags("@FOO");
TagPredicate predicate = new TagPredicate(singletonList("@FOO and @BAR"));
TagPredicate predicate = createPredicate("@FOO and @BAR");
assertFalse(predicate.test(pickle));
}

@Test
void or_tag_predicate_matches_pickle_with_one_of_the_tags() {
Pickle pickle = createPickleWithTags("@FOO");
TagPredicate predicate = new TagPredicate(singletonList("@FOO or @BAR"));
TagPredicate predicate = createPredicate("@FOO or @BAR");
assertTrue(predicate.test(pickle));
}

@Test
void or_tag_predicate_does_not_match_pickle_none_of_the_tags() {
Pickle pickle = createPickleWithTags();
TagPredicate predicate = new TagPredicate(singletonList("@FOO or @BAR"));
TagPredicate predicate = createPredicate("@FOO or @BAR");
assertFalse(predicate.test(pickle));
}

private static Pickle createPickleWithTags(String... tags) {
Feature feature = TestFeatureParser.parse("" +
"Feature: Test feature\n" +
" " + String.join(" ", tags) + "\n" +
" Scenario: Test scenario\n" +
" Given I have 4 cukes in my belly\n");
return feature.getPickles().get(0);
}

private static TagPredicate createPredicate(String... expressions) {
return new TagPredicate(stream(expressions)
.map(TagExpressionParser::parse)
.collect(Collectors.toList()));
}
}
Loading