Tue, 23 Jan 2018 15:30:48 +0200
added test for collinearity, fixed bowtie test
from warnings import warn def report_element(bad_object, type, error_name, args): return { 'type': type, 'object': bad_object, 'name': error_name, 'args': args, } def warning(bad_object, error_name, **args): return report_element(bad_object, 'warning', error_name, args) def error(bad_object, error_name, **args): return report_element(bad_object, 'error', error_name, args) def test_discovery(): ''' Finds all test modules and yields their names. ''' from pkgutil import walk_packages import tests yield from sorted( 'tests.' + result.name for result in walk_packages(tests.__path__) ) def do_manifest_integrity_checks(test_suite, module): ''' Runs integrity checks on a given module's manifest. ''' def check_for_extra_keys(): extra_keys = module.manifest.keys() - test_suite.keys() if extra_keys: warn(str.format( '{}: extra keys in manifest: {}', module.__name__, ', '.join(map(str, extra_keys)) )) def check_for_manifest_duplicates(): for key in test_suite.keys(): duplicates = module.manifest[key].keys() & test_suite[key].keys() if duplicates: warn(str.format( '{}: redefined {} in manifests: {}', module.__name__, key, duplicates, )) check_for_extra_keys() check_for_manifest_duplicates() def load_tests(): ''' Imports test modules and combines their manifests into a test suite. ''' test_suite = {'tests': {}, 'messages': {}} for module_name in test_discovery(): from importlib import import_module module = import_module(module_name) if hasattr(module, 'manifest'): do_manifest_integrity_checks(test_suite, module) # Merge the data from the manifest for key in module.manifest.keys() & test_suite.keys(): test_suite[key].update(module.manifest[key]) else: warn(str.format('Module {} does not have a manifest', module_name)) return test_suite def check_model(model, test_suite = None): if not test_suite: test_suite = load_tests() report = [] line_numbers = { element: (i, i + 1 + model.body_offset) for i, element in enumerate(model.body) } for test_name, test_function in test_suite['tests'].items(): problems = test_function(model) for problem in problems: problem['body-index'], problem['line-number'] \ = line_numbers[problem['object']] del problem['object'] report.append(problem) return report def problem_text(problem, test_suite): message = test_suite['messages'][problem['name']] if callable(message): message = message(**problem['args']) return message def format_report_html(report, model, test_suite): result = [] for problem in report: ldraw_code = model.body[problem['body-index']].textual_representation() message = str.format( '<li class="{problem_type}">{model_name}:{line_number}:' '{problem_type}: {message}<br />{ldraw_code}</li>', model_name = model.name, line_number = problem['line-number'], problem_type = problem['type'], message = problem_text(problem, test_suite), ldraw_code = ldraw_code, ) result.append((problem['line-number'], message)) return '\n'.join( problem[1] for problem in sorted(result) ) def format_report(report, model, test_suite): import colorama colorama.init() result = [] for problem in report: if problem['type'] == 'error': text_colour = colorama.Fore.LIGHTRED_EX elif problem['type'] == 'warning': text_colour = colorama.Fore.LIGHTYELLOW_EX else: text_colour = '' ldraw_code = model.body[problem['body-index']].textual_representation() message = str.format( '{text_colour}{model_name}:{line_number}: {problem_type}: {message}' '{colour_reset}\n\t{ldraw_code}', text_colour = text_colour, model_name = model.name, line_number = problem['line-number'], problem_type = problem['type'], message = problem_text(problem, test_suite), colour_reset = colorama.Fore.RESET, ldraw_code = ldraw_code, ) result.append((problem['line-number'], message)) return '\n'.join( problem[1] for problem in sorted(result) ) if __name__ == '__main__': from pprint import pprint pprint(load_tests())