track untested rules

Report untested rules as errors. Provide a flag to ignore untested
rules.

Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
Doug Hellmann 2017-08-23 15:03:06 -04:00
parent e7c0b47795
commit d5b75fae18
4 changed files with 71 additions and 14 deletions

View File

@ -27,8 +27,11 @@ from whereto import rules
def process_tests(ruleset, tests):
"""Run the tests against the ruleset and return the results.
Results are generated as tuples containing the inputs that did not
match the expected value. The first element is the test tuple
The return value is a tuple containing a list of tuples with the
inputs that did not match the expected value, and a set containing
the line numbers of the rules that never matched an input test.
The first element of the mismatched tuples is the test tuple
(line, input, expected), and the second element is the list of any
rules that did match the input pattern.
@ -36,17 +39,44 @@ def process_tests(ruleset, tests):
:type ruleset: RuleSet
"""
used = set()
mismatches = []
for linenum, input, code, expected in tests:
matches = list(ruleset.match(input))
if len(matches) == 1:
match = matches[0]
if (code, expected) == match[1:]:
used.add(match[0])
continue
yield ((linenum, input, code, expected), matches)
mismatches.append(
((linenum, input, code, expected), matches)
)
untested = set(ruleset.all_ids) - used
return (mismatches, untested)
def main():
arg_parser = argparse.ArgumentParser()
group = arg_parser.add_mutually_exclusive_group()
group.add_argument(
'--ignore-untested',
action='store_false',
dest='error_untested',
default=True,
help='ignore untested rules',
)
group.add_argument(
'--error-untested',
action='store_true',
dest='error_untested',
help='error if there are untested rules',
)
arg_parser.add_argument(
'-q', '--quiet',
action='store_true',
default=False,
help='run quietly',
)
arg_parser.add_argument(
'htaccess_file',
help='file with rewrite rules',
@ -69,7 +99,8 @@ def main():
]
failures = 0
for test, matches in process_tests(ruleset, tests):
mismatches, untested = process_tests(ruleset, tests)
for test, matches in mismatches:
failures += 1
if not matches:
print('No rule matched test on line {}: {}'.format(
@ -82,6 +113,15 @@ def main():
for match in matches:
print(' {}'.format(ruleset[match[0]]))
if untested:
if not args.quiet:
print('')
for linenum in sorted(untested):
if not args.quiet:
print('Untested rule: {}'.format(ruleset[linenum]))
if args.error_untested:
failures += 1
if failures:
print('\n{} failures'.format(failures))
return 1

View File

@ -96,6 +96,10 @@ class RuleSet(object):
def __iter__(self):
return iter(self._rules)
@property
def all_ids(self):
return list(self._by_num.keys())
def match(self, path):
for rule in self:
m = rule.match(path)

View File

@ -28,11 +28,11 @@ class TestProcessTests(base.TestCase):
)
def test_one_match(self):
actual = list(app.process_tests(
actual = app.process_tests(
self.ruleset,
[(1, '/path', '301', '/new/path')],
))
expected = []
)
expected = ([], set())
self.assertEqual(expected, actual)
def test_two_matches(self):
@ -40,13 +40,14 @@ class TestProcessTests(base.TestCase):
2,
'redirect', '301', '/path', '/duplicate/redirect',
)
actual = list(app.process_tests(
actual = app.process_tests(
self.ruleset,
[(1, '/path', '301', '/new/path')],
))
expected = [
((1, '/path', '301', '/new/path'),
[(1, '301', '/new/path'),
(2, '301', '/duplicate/redirect')])
]
)
expected = (
[((1, '/path', '301', '/new/path'),
[(1, '301', '/new/path'),
(2, '301', '/duplicate/redirect')])],
{1, 2},
)
self.assertEqual(expected, actual)

View File

@ -156,6 +156,18 @@ class TestRuleSet(base.TestCase):
self.assertEqual(1, len(self.ruleset._rules))
self.assertIsInstance(self.ruleset._rules[0], rules.RedirectMatch)
def test_all_ids(self):
self.ruleset.add(
1,
'redirect', '301', '/path', '/new/path',
)
self.assertEqual([1], self.ruleset.all_ids)
self.ruleset.add(
2,
'redirect', '301', '/path', '/other/path',
)
self.assertEqual([1, 2], self.ruleset.all_ids)
def test_match_one(self):
self.ruleset.add(
1,