diff --git a/whereto/app.py b/whereto/app.py index b06e409..fef9636 100644 --- a/whereto/app.py +++ b/whereto/app.py @@ -15,18 +15,74 @@ # License for the specific language governing permissions and limitations # under the License. +from __future__ import print_function + import argparse +import io + +from whereto import parser +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 + (line, input, expected), and the second element is the list of any + rules that did match the input pattern. + + :param ruleset: The redirect rules. + :type ruleset: RuleSet + + """ + for linenum, input, code, expected in tests: + matches = list(ruleset.match(input)) + if len(matches) == 1: + match = matches[0] + if (code, expected) == match[1:]: + continue + yield ((linenum, input, code, expected), matches) def main(): - parser = argparse.ArgumentParser() - parser.add_argument( + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument( 'htaccess_file', help='file with rewrite rules', ) - parser.add_argument( + arg_parser.add_argument( 'test_file', help='file with test data', ) - args = parser.parse_args() - print(args) + args = arg_parser.parse_args() + + ruleset = rules.RuleSet() + with io.open(args.htaccess_file, 'r', encoding='utf-8') as f: + for linenum, params in parser.parse_rules(f): + ruleset.add(linenum, *params) + + with io.open(args.test_file, 'r', encoding='utf-8') as f: + tests = [ + (linenum,) + tuple(params) + for linenum, params in parser.parse_rules(f) + ] + + failures = 0 + for test, matches in process_tests(ruleset, tests): + failures += 1 + if not matches: + print('No rule matched test on line {}: {}'.format( + test[0], ' '.join(test[1:])) + ) + else: + print('Test on line {} did not produce expected result: {}'.format( + test[0], ' '.join(test[1:])) + ) + for match in matches: + print(' {}'.format(ruleset[match[0]])) + + if failures: + print('\n{} failures'.format(failures)) + return 1 + return 0 diff --git a/whereto/rules.py b/whereto/rules.py index 366c310..9ed3603 100644 --- a/whereto/rules.py +++ b/whereto/rules.py @@ -82,11 +82,16 @@ class RuleSet(object): def __init__(self): self._rules = [] + self._by_num = {} def add(self, linenum, *params): rule_type = params[0].lower() rule = self._factories[rule_type](linenum, *params) self._rules.append(rule) + self._by_num[linenum] = rule + + def __getitem__(self, index): + return self._by_num[index] def __iter__(self): return iter(self._rules) diff --git a/whereto/tests/test_app.py b/whereto/tests/test_app.py new file mode 100644 index 0000000..48920af --- /dev/null +++ b/whereto/tests/test_app.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from whereto import app +from whereto import rules +from whereto.tests import base + + +class TestProcessTests(base.TestCase): + + def setUp(self): + super(TestProcessTests, self).setUp() + self.ruleset = rules.RuleSet() + self.ruleset.add( + 1, + 'redirect', '301', '/path', '/new/path', + ) + + def test_one_match(self): + actual = list(app.process_tests( + self.ruleset, + [(1, '/path', '301', '/new/path')], + )) + expected = [] + self.assertEqual(expected, actual) + + def test_two_matches(self): + self.ruleset.add( + 2, + 'redirect', '301', '/path', '/duplicate/redirect', + ) + actual = list(app.process_tests( + self.ruleset, + [(1, '/path', '301', '/new/path')], + )) + expected = [ + ((1, '/path', '301', '/new/path'), + [(1, '301', '/new/path'), + (2, '301', '/duplicate/redirect')]) + ] + self.assertEqual(expected, actual)