Skip to content

Commit

Permalink
Allow exclusionary tags to omit scenarios
Browse files Browse the repository at this point in the history
If a user passes in a tag for which no scenario should run, e.g.

   --tag=-A

then the expectation is that no scenario with that tag would run.
However, the previous behavior was to include a scenario if an
inclusion tag matches regardless if an exclusion tag also matches.
scenario with an inclusionary. For example, if a scenario's
tags were:

   ['A', 'B']

and the user specified:

   --tag=-A --tag=B

the scenario would run.

This change changes the behavior of scenario filtering:
1) If there are inclusionary tags and none match, the
   scenario is rejected
2) If there are exclusionary tags and any match, the
   scenario is rejected
3) If there are ~ or ~- tags and none match, the scenario
   is rejected
4) If the above did not reject the scenario, it is accepted

gabrielfalcao#498
  • Loading branch information
brarcher committed Nov 22, 2016
1 parent 70f20f4 commit 058db25
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 15 deletions.
45 changes: 30 additions & 15 deletions lettuce/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,30 +647,39 @@ def matches_tags(self, tags):
if tags is None:
return True

has_exclusionary_tags = any([t.startswith('-') for t in tags])
exclusionary_tags = [t[1:] for t in tags if t.startswith('-') and not t.startswith('-~')]
inclusionary_tags = [t for t in tags if not t.startswith('-') and not t.startswith('~')]

if not self.tags and not has_exclusionary_tags:
return False
if not isinstance(self.tags, list):
self.tags = []

matched = []
# If there are inclusionary tags, at least one must match
if len(inclusionary_tags) > 0:
matches = set(self.tags).intersection(inclusionary_tags)
if len(matches) == 0:
# This scenario did not match any of the inclusionary
# tags, and it must be excluded
return False

# If there are exclusionary tags, if any match the scenario must
# be thrown out
if len(exclusionary_tags) > 0:
matches = set(self.tags).intersection(exclusionary_tags)
if len(matches) > 0:
# At least one exclusionary tag matches, omit the
# scenario
return False

if isinstance(self.tags, list):
for tag in self.tags:
if tag in tags:
return True
else:
self.tags = []
matched = []

for tag in tags:
exclude = tag.startswith('-')
if exclude:
tag = tag[1:]

fuzzable = tag.startswith('~')
if fuzzable:
tag = tag[1:]

result = tag in self.tags
if fuzzable:
fuzzed = []
for internal_tag in self.tags:
Expand All @@ -679,14 +688,20 @@ def matches_tags(self, tags):
fuzzed.append(ratio <= 80)
else:
fuzzed.append(ratio > 80)

result = any(fuzzed)
matched.append(result)
elif exclude:
result = tag not in self.tags
matched.append(result)

matched.append(result)
# Determine if any tags may optionally be included.
# If so, all must fail for the scenario to be omitted
if not all(matched):
return False

return all(matched)
# If the check make it here, there is no reason to
# disclude the test
return True

@property
def evaluated(self):
Expand Down
12 changes: 12 additions & 0 deletions tests/unit/test_scenario_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,18 @@ def test_scenario_matches_tags_excluding_when_scenario_has_no_tags():

assert scenario.matches_tags(['-nope', '-neither'])

def test_scenario_matches_tags_both_include_and_exclude_tags():
("When Scenario#matches_tags is called for a scenario "
"that has an inclusionary and exclusionary tag that matches, "
"the scenario is excluded")

scenario = Scenario.from_string(
SCENARIO1,
original_string=SCENARIO1.strip(),
tags=['tag1', 'tag2'])

assert not scenario.matches_tags(['tag1', '-tag2'])


def test_scenario_matches_tags_excluding_fuzzywuzzy():
("When Scenario#matches_tags is called with a member starting with -~ "
Expand Down

0 comments on commit 058db25

Please sign in to comment.