Skip to content

Commit

Permalink
Use the tag-expressions library.
Browse files Browse the repository at this point in the history
Use the new tag-expressions library, but support both the use of the new
tag expression syntax and the old tag expression syntax.
  • Loading branch information
brasmusson committed Nov 20, 2016
1 parent 9f59d61 commit 832c378
Show file tree
Hide file tree
Showing 23 changed files with 178 additions and 67 deletions.
10 changes: 5 additions & 5 deletions clojure/src/main/clj/cucumber/runtime/clj.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
JdkPatternArgumentMatcher
StepDefinition
HookDefinition
TagExpression)
TagPredicate)
(cucumber.runtime.snippets Snippet
SnippetGenerator)
(clojure.lang RT))
Expand Down Expand Up @@ -92,7 +92,7 @@
(defmulti add-hook-definition (fn [t & _] t))

(defmethod add-hook-definition :before [_ tag-expression hook-fun location]
(let [te (TagExpression. tag-expression)]
(let [tp (TagPredicate. tag-expression)]
(.addBeforeHook
@glue
(reify
Expand All @@ -102,12 +102,12 @@
(execute [hd scenario-result]
(hook-fun))
(matches [hd tags]
(.evaluate te tags))
(.apply tp tags))
(getOrder [hd] 0)
(isScenarioScoped [hd] false)))))

(defmethod add-hook-definition :after [_ tag-expression hook-fun location]
(let [te (TagExpression. tag-expression)
(let [tp (TagPredicate. tag-expression)
max-parameter-count (->> hook-fun class .getDeclaredMethods
(filter #(= "invoke" (.getName %)))
(map #(count (.getParameterTypes %)))
Expand All @@ -123,7 +123,7 @@
(hook-fun)
(hook-fun scenario-result)))
(matches [hd tags]
(.evaluate te tags))
(.apply tp tags))
(getOrder [hd] 0)
(isScenarioScoped [hd] false)))))

Expand Down
2 changes: 2 additions & 0 deletions clojure/tmp.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
a
b
4 changes: 4 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
<groupId>io.cucumber</groupId>
<artifactId>gherkin</artifactId>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>tag-expressions</artifactId>
</dependency>

<dependency>
<groupId>junit</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,26 @@
import java.util.List;
import java.util.Map;

public class TagExpression {
import static java.util.Arrays.asList;

public class TagExpressionOld {
private final Map<String, Integer> limits = new HashMap<String, Integer>();
private And and = new And();

public TagExpression(List<String> tagExpressions) {
public static boolean isOldTagExpression(String tagExpression) {
if (tagExpression == null) {
return false;
}
if (tagExpression.contains(",")) {
System.err.println("WARNING: Found tags option '" + tagExpression + "'. Support for '@tag1,@tag2' will be removed from the next release of Cucumber-JVM. Please use '@tag or @tag2' instead");
}
if (tagExpression.contains("~")) {
System.err.println("WARNING: Found tags option '" + tagExpression + "'. Support for '~@tag' will be removed from the next release of Cucumber-JVM. Please use 'not @tag' instead.");
}
return tagExpression.contains(",") || tagExpression.contains("~");
}

public TagExpressionOld(List<String> tagExpressions) {
for (String tagExpression : tagExpressions) {
add(tagExpression.split("\\s*,\\s*"));
}
Expand Down
45 changes: 39 additions & 6 deletions core/src/main/java/cucumber/runtime/TagPredicate.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,34 @@

import gherkin.pickles.Pickle;
import gherkin.pickles.PickleTag;
import io.cucumber.tagexpressions.Expression;
import io.cucumber.tagexpressions.TagExpressionParser;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import static java.util.Arrays.asList;


public class TagPredicate implements PicklePredicate {
private TagExpression tagExpression;
private final List<Expression> expressions = new ArrayList<Expression>();
private final List<TagExpressionOld> oldStyleExpressions = new ArrayList<TagExpressionOld>();

public TagPredicate(List<String> tags) {
this.tagExpression = new TagExpression(tags);
public TagPredicate(List<String> tagExpressions) {
if (tagExpressions == null) {
return;
}
TagExpressionParser parser = new TagExpressionParser();
for (String tagExpression : tagExpressions) {
if (TagExpressionOld.isOldTagExpression(tagExpression)) {
oldStyleExpressions.add(new TagExpressionOld(asList(tagExpression)));
} else {
expressions.add(parser.parse(tagExpression));
}
}
}

@Override
Expand All @@ -25,10 +43,25 @@ public boolean apply(Pickle pickle) {
} catch (Exception e) {
tags = Collections.<PickleTag>emptyList();
}
if (tagExpression.evaluate(tags)) {
return true;
return apply(tags);
}

public boolean apply(Collection<PickleTag> pickleTags) {
for (TagExpressionOld oldStyleExpression : oldStyleExpressions) {
if (!oldStyleExpression.evaluate(pickleTags)) {
return false;
}
}
List<String> tags = new ArrayList<String>();
for (PickleTag pickleTag : pickleTags) {
tags.add(pickleTag.getName());
}
for (Expression expression : expressions) {
if (!expression.evaluate(tags)) {
return false;
}
}
return false;
return true;
}

}
59 changes: 55 additions & 4 deletions core/src/test/java/cucumber/runtime/TagPredicateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,19 @@ public class TagPredicateTest {
private static final PickleTag FOO_TAG = new PickleTag(MOCK_LOCATION, FOO_TAG_VALUE);
private static final String BAR_TAG_VALUE = "@BAR";
private static final PickleTag BAR_TAG = new PickleTag(MOCK_LOCATION, BAR_TAG_VALUE);
private static final String NOT_FOO_TAG_VALUE = "~@FOO";
private static final String FOO_OR_BAR_TAG_VALUE = "@FOO,@BAR";
private static final String NOT_FOO_TAG_VALUE = "not @FOO";
private static final String FOO_OR_BAR_TAG_VALUE = "@FOO or @BAR";
private static final String FOO_AND_BAR_TAG_VALUE = "@FOO and @BAR";
private static final String OLD_STYLE_NOT_FOO_TAG_VALUE = "~@FOO";
private static final String OLD_STYLE_FOO_OR_BAR_TAG_VALUE = "@FOO,@BAR";

@Test
public void empty_tag_predicate_matches_pickle_with_any_tags() {
Pickle pickle = createPickleWithTags(asList(FOO_TAG));
TagPredicate predicate = new TagPredicate(null);

assertTrue(predicate.apply(pickle));
}

@Test
public void single_tag_predicate_does_not_match_pickle_with_no_tags() {
Expand Down Expand Up @@ -84,15 +95,15 @@ public void not_tag_predicate_matches_pickle_with_different_single_tag() {
@Test
public void and_tag_predicate_matches_pickle_with_all_tags() {
Pickle pickle = createPickleWithTags(asList(FOO_TAG, BAR_TAG));
TagPredicate predicate = new TagPredicate(asList(FOO_TAG_VALUE, BAR_TAG_VALUE));
TagPredicate predicate = new TagPredicate(asList(FOO_AND_BAR_TAG_VALUE));

assertTrue(predicate.apply(pickle));
}

@Test
public void and_tag_predicate_does_not_match_pickle_with_one_of_the_tags() {
Pickle pickle = createPickleWithTags(asList(FOO_TAG));
TagPredicate predicate = new TagPredicate(asList(FOO_TAG_VALUE, BAR_TAG_VALUE));
TagPredicate predicate = new TagPredicate(asList(FOO_AND_BAR_TAG_VALUE));

assertFalse(predicate.apply(pickle));
}
Expand All @@ -105,6 +116,46 @@ public void or_tag_predicate_matches_pickle_with_one_of_the_tags() {
assertTrue(predicate.apply(pickle));
}

@Test
public void or_tag_predicate_does_not_match_pickle_none_of_the_tags() {
Pickle pickle = createPickleWithTags(Collections.<PickleTag>emptyList());
TagPredicate predicate = new TagPredicate(asList(FOO_OR_BAR_TAG_VALUE));

assertFalse(predicate.apply(pickle));
}

@Test
public void old_style_not_tag_predicate_is_handled() {
Pickle pickle = createPickleWithTags(asList(BAR_TAG));
TagPredicate predicate = new TagPredicate(asList(OLD_STYLE_NOT_FOO_TAG_VALUE));

assertTrue(predicate.apply(pickle));
}

@Test
public void old_style_or_tag_predicate_is_handled() {
Pickle pickle = createPickleWithTags(asList(FOO_TAG));
TagPredicate predicate = new TagPredicate(asList(OLD_STYLE_FOO_OR_BAR_TAG_VALUE));

assertTrue(predicate.apply(pickle));
}

@Test
public void multiple_tag_expressions_are_combined_with_and() {
Pickle pickle = createPickleWithTags(asList(FOO_TAG, BAR_TAG));
TagPredicate predicate = new TagPredicate(asList(FOO_TAG_VALUE, BAR_TAG_VALUE));

assertTrue(predicate.apply(pickle));
}

@Test
public void old_and_new_style_tag_expressions_can_be_combined() {
Pickle pickle = createPickleWithTags(asList(BAR_TAG));
TagPredicate predicate = new TagPredicate(asList(BAR_TAG_VALUE, OLD_STYLE_NOT_FOO_TAG_VALUE));

assertTrue(predicate.apply(pickle));
}

private Pickle createPickleWithTags(List<PickleTag> tags) {
return new Pickle(NAME, NO_STEPS, tags, asList(MOCK_LOCATION));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Before("@notused") {

// Register another that also gets run before each scenario tagged with
// (@notused or @important) and @alsonotused.
Before("@notused,@important", "@alsonotused") {
Before("(@notused or @important) and @alsonotused") {
throw new RuntimeException("Never happens")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void the_result_is(double expected) {
assertEquals(expected, calc.value());
}

@Before({"~@foo"})
@Before({"not @foo"})
public void before() {
System.out.println("Runs before scenarios *not* tagged with @foo");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ public void the_result_is(double expected) {
assertEquals(expected, calc.value());
}

@Before({"~@foo"})
public void before() {
System.out.println("Runs before scenarios *not* tagged with @foo");
@Before("not @foo")
public void before(Scenario scenario) {
scenario.write("Runs before scenarios *not* tagged with @foo");
}

@After
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class RpnCalculatorStepDefinitions extends ScalaDsl with EN {
assertEquals(expected, calc.value, 0.001)
}

Before("~@foo"){ scenario : Scenario =>
Before("not @foo"){ scenario : Scenario =>
println("Runs before scenarios *not* tagged with @foo")
}
}
8 changes: 4 additions & 4 deletions groovy/src/main/java/cucumber/api/groovy/Hooks.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cucumber.api.groovy;

import cucumber.runtime.CucumberException;
import cucumber.runtime.TagExpression;
import cucumber.runtime.TagPredicate;
import cucumber.runtime.groovy.GroovyBackend;
import groovy.lang.Closure;

Expand Down Expand Up @@ -82,11 +82,11 @@ private static void addHook(Object[] tagsExpressionsAndBody, boolean before) {
}
}

TagExpression tagExpression = new TagExpression(tagExpressions);
TagPredicate tagPredicate = new TagPredicate(tagExpressions);
if (before) {
GroovyBackend.getInstance().addBeforeHook(tagExpression, timeoutMillis, order, body);
GroovyBackend.getInstance().addBeforeHook(tagPredicate, timeoutMillis, order, body);
} else {
GroovyBackend.getInstance().addAfterHook(tagExpression, timeoutMillis, order, body);
GroovyBackend.getInstance().addAfterHook(tagPredicate, timeoutMillis, order, body);
}
}
}
10 changes: 5 additions & 5 deletions groovy/src/main/java/cucumber/runtime/groovy/GroovyBackend.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import cucumber.runtime.CucumberException;
import cucumber.runtime.Glue;
import cucumber.runtime.UnreportedStepExecutor;
import cucumber.runtime.TagExpression;
import cucumber.runtime.TagPredicate;
import cucumber.runtime.io.Resource;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.io.ResourceLoaderClassFinder;
Expand Down Expand Up @@ -140,12 +140,12 @@ public void registerWorld(Closure closure) {
worldClosures.add(closure);
}

public void addBeforeHook(TagExpression tagExpression, long timeoutMillis, int order, Closure body) {
glue.addBeforeHook(new GroovyHookDefinition(tagExpression, timeoutMillis, order, body, currentLocation(), this));
public void addBeforeHook(TagPredicate tagPredicate, long timeoutMillis, int order, Closure body) {
glue.addBeforeHook(new GroovyHookDefinition(tagPredicate, timeoutMillis, order, body, currentLocation(), this));
}

public void addAfterHook(TagExpression tagExpression, long timeoutMillis, int order, Closure body) {
glue.addAfterHook(new GroovyHookDefinition(tagExpression, timeoutMillis, order, body, currentLocation(), this));
public void addAfterHook(TagPredicate tagPredicate, long timeoutMillis, int order, Closure body) {
glue.addAfterHook(new GroovyHookDefinition(tagPredicate, timeoutMillis, order, body, currentLocation(), this));
}

public void invoke(Closure body, Object[] args) throws Throwable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,29 @@
import cucumber.api.Scenario;
import cucumber.runtime.HookDefinition;
import cucumber.runtime.Timeout;
import cucumber.runtime.TagExpression;
import cucumber.runtime.TagPredicate;
import gherkin.pickles.PickleTag;
import groovy.lang.Closure;

import java.util.Collection;

public class GroovyHookDefinition implements HookDefinition {
private final TagExpression tagExpression;
private final TagPredicate tagPredicate;
private final long timeoutMillis;
private final int order;
private final Closure body;
private final GroovyBackend backend;
private final StackTraceElement location;

public GroovyHookDefinition(
TagExpression tagExpression,
TagPredicate tagPredicate,
long timeoutMillis,
int order,
Closure body,
StackTraceElement location,
GroovyBackend backend) {

this.tagExpression = tagExpression;
this.tagPredicate = tagPredicate;
this.timeoutMillis = timeoutMillis;
this.order = order;
this.body = body;
Expand All @@ -51,7 +51,7 @@ public Object call() throws Throwable {

@Override
public boolean matches(Collection<PickleTag> tags) {
return tagExpression.evaluate(tags);
return tagPredicate.apply(tags);
}

@Override
Expand Down
Loading

0 comments on commit 832c378

Please sign in to comment.