From 42bb69a6a2f12165a9758b192e80da089e00ab5c Mon Sep 17 00:00:00 2001 From: Simon Feltman Date: Wed, 18 Dec 2013 18:21:55 -0800 Subject: tests: Refactor test_parser and test_patterns to use unittest.TestSuite Update both test_parser.py and test_patterns.py to dynamically generate TestCase sub-classes rather than modify a statically defined one. Use unittest.TestSuite to queue up the generated test cases via the "load_tests" hook. Use underscores instead of periods as a seperator for dynamically generated test cases and methods to match Python identifiers. Beyond general cleanup, these changes will help the ability to specify Python tests individually. https://bugzilla.gnome.org/show_bug.cgi?id=720713 --- tests/scanner/annotationparser/test_parser.py | 54 +++++++++-- tests/scanner/annotationparser/test_patterns.py | 115 +++++++++++++++--------- 2 files changed, 119 insertions(+), 50 deletions(-) diff --git a/tests/scanner/annotationparser/test_parser.py b/tests/scanner/annotationparser/test_parser.py index ef4d7462..79cc8622 100644 --- a/tests/scanner/annotationparser/test_parser.py +++ b/tests/scanner/annotationparser/test_parser.py @@ -380,8 +380,7 @@ class TestCommentBlock(unittest.TestCase): return retval -def create_tests(logger, tests_dir, tests_file): - tests_name = os.path.relpath(tests_file[:-4], tests_dir).replace('/', '.').replace('\\', '.') +def create_test_case(logger, tests_dir, tests_file): tests_tree = etree.parse(tests_file).getroot() fix_cdata_elements = tests_tree.findall(ns('{}test/{}input')) @@ -393,17 +392,27 @@ def create_tests(logger, tests_dir, tests_file): element.text = element.text.replace('{{?', '') + test_methods = {} for counter, test in enumerate(tests_tree.findall(ns('{}test'))): - test_name = 'test_%s.%03d' % (tests_name, counter + 1) + test_name = 'test_%03d' % (counter + 1) test_method = TestCommentBlock.__create_test__(logger, test) - setattr(TestCommentBlock, test_name, test_method) + test_method.__name__ = test_name + test_methods[test_name] = test_method + # Dynamically generate a new subclass of TestCommentBlock in TitleCase + # with generated test methods. + test_class_name = os.path.relpath(tests_file[:-4], tests_dir) + test_class_name = test_class_name.replace('/', ' ').replace('\\', ' ').replace('.', ' ') + test_class_name = 'Test' + test_class_name.title().replace(' ', '') + return type(test_class_name, (TestCommentBlock,), test_methods) -if __name__ == '__main__': + +def create_test_cases(): # Initialize message logger namespace = Namespace('Test', '1.0') logger = MessageLogger.get(namespace=namespace) logger.enable_warnings((WARNING, ERROR, FATAL)) + test_cases = {} # Load test cases from disc tests_dir = os.path.dirname(os.path.abspath(__file__)) @@ -413,7 +422,40 @@ if __name__ == '__main__': tests_file = os.path.join(dirpath, filename) if os.path.basename(tests_file).endswith('.xml'): validate(tests_file) - create_tests(logger, tests_dir, tests_file) + test_case = create_test_case(logger, tests_dir, tests_file) + test_cases[test_case.__name__] = test_case + + return test_cases + + +# We currently need to push all the new test cases into the modules globals +# in order for parameterized tests to work. Ideally all that should be needed +# is the "load_tests" hook, but this does not work in the case were the tests +# are run in parameterized mode, e.g: python -m unittest test_parser.Test... +_all_tests = create_test_cases() +globals().update(_all_tests) + + +# Hook function for Python test loader. +def load_tests(loader, tests, pattern): + suite = unittest.TestSuite() + # add standard tests from module + suite.addTests(tests) + + # Initialize message logger + namespace = Namespace('Test', '1.0') + logger = MessageLogger.get(namespace=namespace) + logger.enable_warnings((WARNING, ERROR, FATAL)) + # Load test cases from disc + tests_dir = os.path.dirname(os.path.abspath(__file__)) + + for name, test_case in _all_tests.iteritems(): + tests = loader.loadTestsFromTestCase(test_case) + suite.addTests(tests) + return suite + + +if __name__ == '__main__': # Run test suite unittest.main() diff --git a/tests/scanner/annotationparser/test_patterns.py b/tests/scanner/annotationparser/test_patterns.py index d5da82b6..774d674c 100644 --- a/tests/scanner/annotationparser/test_patterns.py +++ b/tests/scanner/annotationparser/test_patterns.py @@ -36,7 +36,7 @@ from giscanner.annotationparser import (COMMENT_BLOCK_START_RE, COMMENT_BLOCK_EN SECTION_RE, SYMBOL_RE, PROPERTY_RE, SIGNAL_RE, PARAMETER_RE, TAG_RE, TAG_VALUE_VERSION_RE, TAG_VALUE_STABILITY_RE) -from unittest import (TestCase, main) +import unittest comment_start_tests = [ @@ -858,59 +858,86 @@ tag_value_stability_tests = [ 'description': 'xyz: abc'})] -def create_tests(tests_name, testcases): +def create_test_method(testcase): + def do_test(self): + (program, text, expected) = testcase + + match = program.match(text) + + if match is not None: + msg = 'Test matched pattern but specifies no expected named groups.' + self.assertTrue(isinstance(expected, dict), msg) + + for group in match.groupdict().keys(): + msg = 'Test case is missing expected results for named group "%s".' % (group) + self.assertTrue(group in expected.keys(), msg) + + if expected is None: + msg = 'Program matched text but shouldn\'t:\n"%s"' + self.assertTrue(match is None, msg % (text, )) + else: + msg = 'Program should match text but didn\'t:\n"%s"' + self.assertTrue(match is not None, msg % (text, )) + + for key, value in expected.items(): + msg = 'expected "%s" for "%s" but match returned "%s"' + msg = msg % (value, key, match.group(key)) + self.assertEqual(match.group(key), value, msg) + + return do_test + + +def create_test_case(tests_class_name, testcases): + test_methods = {} for (index, testcase) in enumerate(testcases): - real_test_name = '%s_%03d' % (tests_name, index) + test_method_name = 'test_%03d' % index + + test_method = create_test_method(testcase) + test_method.__name__ = test_method_name + test_methods[test_method_name] = test_method - test_method = TestProgram.__create_test__(testcase) - test_method.__name__ = real_test_name - setattr(TestProgram, real_test_name, test_method) + return type(tests_class_name, (unittest.TestCase,), test_methods) -class TestProgram(TestCase): - @classmethod - def __create_test__(cls, testcase): - def do_test(self): - (program, text, expected) = testcase +def create_test_cases(): + test_cases = {} + for name, test_data in (('TestCommentStart', comment_start_tests), + ('TestCommentEnd', comment_end_tests), + ('TestCommentAsterisk', comment_asterisk_tests), + ('TestIndentaton', indentaton_tests), + ('TestEmptyLine', empty_line_tests), + ('TestIdentifierSection', identifier_section_tests), + ('TestIdentifierSymbol', identifier_symbol_tests), + ('TestIdentifierProperty', identifier_property_tests), + ('TestIdentifierSignal', identifier_signal_tests), + ('TestParameter', parameter_tests), + ('TestTag', tag_tests), + ('TestTagValueVersion', tag_value_version_tests), + ('TestTagValueStability', tag_value_stability_tests)): + test_cases[name] = create_test_case(name, test_data) - match = program.match(text) + return test_cases - if match is not None: - msg = 'Test matched pattern but specifies no expected named groups.' - self.assertTrue(isinstance(expected, dict), msg) - for group in match.groupdict().keys(): - msg = 'Test case is missing expected results for named group "%s".' % (group) - self.assertTrue(group in expected.keys(), msg) +# We currently need to push all the new test cases into the modules globals +# in order for parameterized tests to work. Ideally all that should be needed +# is the "load_tests" hook, but this does not work in the case were the tests +# are run in parameterized mode, e.g: python -m unittest test_parser.Test... +_all_tests = create_test_cases() +globals().update(_all_tests) - if expected is None: - msg = 'Program matched text but shouldn\'t:\n"%s"' - self.assertTrue(match is None, msg % (text, )) - else: - msg = 'Program should match text but didn\'t:\n"%s"' - self.assertTrue(match is not None, msg % (text, )) - for key, value in expected.items(): - msg = 'expected "%s" for "%s" but match returned "%s"' - msg = msg % (value, key, match.group(key)) - self.assertEqual(match.group(key), value, msg) +# Hook function for Python test loader. +def load_tests(loader, tests, pattern): + suite = unittest.TestSuite() + # add standard tests from module + suite.addTests(tests) - return do_test + for name, test_case in _all_tests.iteritems(): + tests = loader.loadTestsFromTestCase(test_case) + suite.addTests(tests) + return suite if __name__ == '__main__': - create_tests('test_comment_start', comment_start_tests) - create_tests('test_comment_end', comment_end_tests) - create_tests('test_comment_asterisk', comment_asterisk_tests) - create_tests('test_indentaton', indentaton_tests) - create_tests('test_empty_line', empty_line_tests) - create_tests('test_identifier_section', identifier_section_tests) - create_tests('test_identifier_symbol', identifier_symbol_tests) - create_tests('test_identifier_property', identifier_property_tests) - create_tests('test_identifier_signal', identifier_signal_tests) - create_tests('test_parameter', parameter_tests) - create_tests('test_tag', tag_tests) - create_tests('test_tag_value_version', tag_value_version_tests) - create_tests('test_tag_value_stability', tag_value_stability_tests) - - main() + unittest.main() -- cgit v1.2.1