testsuite.py

Wed, 31 Jan 2018 14:50:19 +0200

author
Santeri Piippo
date
Wed, 31 Jan 2018 14:50:19 +0200
changeset 41
4d87bc126368
parent 32
75f44d3063da
child 37
e46fa477007b
child 42
be50d98d3668
permissions
-rw-r--r--

don't crash and burn if someone sends something that's not LDraw

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 notice(bad_object, error_name, **args):
    return report_element(bad_object, 'notice', 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 problem_key(problem):
    problem_hierarchy = ['error', 'warning', 'notice']
    return (problem_hierarchy.index(problem['type']), problem['line-number'])

def check_model(model, test_suite = None):
    if not test_suite:
        test_suite = load_tests()
    problems = []
    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():
        for problem in test_function(model):
            problem['body-index'], problem['line-number'] \
                = line_numbers[problem['object']]
            del problem['object']
            problems.append(problem)
    return {
        'passed': not any(
            problem['type'] == 'error'
            for problem in problems
        ),
        'problems': sorted(problems, key = problem_key),
    }

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):
    messages = []
    for problem in report['problems']:
        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,
        )
        messages.append(message)
    return '\n'.join(messages)

def format_report(report, model, test_suite):
    import colorama
    colorama.init()
    messages = []
    for problem in report['problems']:
        if problem['type'] == 'error':
            text_colour = colorama.Fore.LIGHTRED_EX
        elif problem['type'] == 'warning':
            text_colour = colorama.Fore.LIGHTYELLOW_EX
        elif problem['type'] == 'notice':
            text_colour = colorama.Fore.LIGHTBLUE_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,
        )
        messages.append(message)
    return '\n'.join(messages)

if __name__ == '__main__':
    from pprint import pprint
    pprint(load_tests())

mercurial