1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 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) # noqa: B026
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(*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)
|