diff options
Diffstat (limited to '_test/lib/test_appliance.py')
-rw-r--r-- | _test/lib/test_appliance.py | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/_test/lib/test_appliance.py b/_test/lib/test_appliance.py new file mode 100644 index 0000000..a95de5e --- /dev/null +++ b/_test/lib/test_appliance.py @@ -0,0 +1,205 @@ +import argparse +import os +import pprint +import sys +import traceback +import types + +# DATA = 'tests/data' +# determine the position of data dynamically relative to program +# this allows running test while the current path is not the top of the +# repository, e.g. from the tests/data directory: python ../test_yaml.py +DATA = __file__.rsplit(os.sep, 2)[0] + '/data' + + +def find_test_functions(collections): + if not isinstance(collections, list): + collections = [collections] + functions = [] + for collection in collections: + if not isinstance(collection, dict): + collection = vars(collection) + for key in sorted(collection): + value = collection[key] + if isinstance(value, types.FunctionType) and hasattr(value, 'unittest'): + functions.append(value) + return functions + + +def find_test_filenames(directory): + filenames = {} + for filename in os.listdir(directory): + if os.path.isfile(os.path.join(directory, filename)): + base, ext = os.path.splitext(filename) + filenames.setdefault(base, []).append(ext) + filenames = sorted(filenames.items()) + return filenames + + +def parse_arguments(args): + """""" + parser = argparse.ArgumentParser( + usage=""" run the yaml tests. By default + all functions on all appropriate test_files are run. Functions have + unittest attributes that determine the required extensions to filenames + that need to be available in order to run that test. E.g.\n\n + python test_yaml.py test_constructor_types\n + python test_yaml.py --verbose test_tokens spec-02-05\n\n + The presence of an extension in the .skip attribute of a function + disables the test for that function.""" + ) + # ToDo: make into int and test > 0 in functions + parser.add_argument( + '--verbose', + '-v', + action='store_true', + default='YAML_TEST_VERBOSE' in os.environ, + help='set verbosity output', + ) + parser.add_argument( + '--list-functions', + action='store_true', + help="""list all functions with required file extensions for test files + """, + ) + parser.add_argument('function', nargs='?', help="""restrict function to run""") + parser.add_argument( + 'filenames', + nargs='*', + help="""basename of filename set, extensions (.code, .data) have to + be a superset of those in the unittest attribute of the selected + function""", + ) + args = parser.parse_args(args) + # print('args', args) + verbose = args.verbose + include_functions = [args.function] if args.function else [] + include_filenames = args.filenames + # if args is None: + # args = sys.argv[1:] + # verbose = False + # if '-v' in args: + # verbose = True + # args.remove('-v') + # if '--verbose' in args: + # verbose = True + # args.remove('--verbose') # never worked without this + # if 'YAML_TEST_VERBOSE' in os.environ: + # verbose = True + # include_functions = [] + # if args: + # include_functions.append(args.pop(0)) + if 'YAML_TEST_FUNCTIONS' in os.environ: + include_functions.extend(os.environ['YAML_TEST_FUNCTIONS'].split()) + # include_filenames = [] + # include_filenames.extend(args) + if 'YAML_TEST_FILENAMES' in os.environ: + include_filenames.extend(os.environ['YAML_TEST_FILENAMES'].split()) + return include_functions, include_filenames, verbose, args + + +def execute(function, filenames, verbose): + name = function.__name__ + if verbose: + sys.stdout.write('=' * 75 + '\n') + sys.stdout.write('%s(%s)...\n' % (name, ', '.join(filenames))) + try: + function(verbose=verbose, *filenames) + except Exception as exc: + info = sys.exc_info() + if isinstance(exc, AssertionError): + kind = 'FAILURE' + else: + kind = 'ERROR' + if verbose: + traceback.print_exc(limit=1, file=sys.stdout) + else: + sys.stdout.write(kind[0]) + sys.stdout.flush() + else: + kind = 'SUCCESS' + info = None + if not verbose: + sys.stdout.write('.') + sys.stdout.flush() + return (name, filenames, kind, info) + + +def display(results, verbose): + if results and not verbose: + sys.stdout.write('\n') + total = len(results) + failures = 0 + errors = 0 + for name, filenames, kind, info in results: + if kind == 'SUCCESS': + continue + if kind == 'FAILURE': + failures += 1 + if kind == 'ERROR': + errors += 1 + sys.stdout.write('=' * 75 + '\n') + sys.stdout.write('%s(%s): %s\n' % (name, ', '.join(filenames), kind)) + if kind == 'ERROR': + traceback.print_exception(file=sys.stdout, *info) + else: + sys.stdout.write('Traceback (most recent call last):\n') + traceback.print_tb(info[2], file=sys.stdout) + sys.stdout.write('%s: see below\n' % info[0].__name__) + sys.stdout.write('~' * 75 + '\n') + for arg in info[1].args: + pprint.pprint(arg, stream=sys.stdout) + for filename in filenames: + sys.stdout.write('-' * 75 + '\n') + sys.stdout.write('%s:\n' % filename) + with open(filename, 'r', errors='replace') as fp: + data = fp.read() + sys.stdout.write(data) + if data and data[-1] != '\n': + sys.stdout.write('\n') + sys.stdout.write('=' * 75 + '\n') + sys.stdout.write('TESTS: %s\n' % total) + ret_val = 0 + if failures: + sys.stdout.write('FAILURES: %s\n' % failures) + ret_val = 1 + if errors: + sys.stdout.write('ERRORS: %s\n' % errors) + ret_val = 2 + return ret_val + + +def run(collections, args=None): + test_functions = find_test_functions(collections) + test_filenames = find_test_filenames(DATA) + include_functions, include_filenames, verbose, a = parse_arguments(args) + if a.list_functions: + print('test functions:') + for f in test_functions: + print(' {:30s} {}'.format(f.__name__, f.unittest)) + return + results = [] + for function in test_functions: + if include_functions and function.__name__ not in include_functions: + continue + if function.unittest: + for base, exts in test_filenames: + if include_filenames and base not in include_filenames: + continue + filenames = [] + for ext in function.unittest: + if ext not in exts: + break + filenames.append(os.path.join(DATA, base + ext)) + else: + skip_exts = getattr(function, 'skip', []) + for skip_ext in skip_exts: + if skip_ext in exts: + break + else: + result = execute(function, filenames, verbose) + results.append(result) + else: + result = execute(function, [], verbose) + results.append(result) + return display(results, verbose=verbose) |