1 2import sys, os, os.path, types, traceback, pprint 3 4DATA = 'tests/data' 5 6has_ucs4 = sys.maxunicode > 0xffff 7 8def find_test_functions(collections): 9 if not isinstance(collections, list): 10 collections = [collections] 11 functions = [] 12 for collection in collections: 13 if not isinstance(collection, dict): 14 collection = vars(collection) 15 keys = collection.keys() 16 keys.sort() 17 for key in keys: 18 value = collection[key] 19 if isinstance(value, types.FunctionType) and hasattr(value, 'unittest'): 20 functions.append(value) 21 return functions 22 23def find_test_filenames(directory): 24 filenames = {} 25 for filename in os.listdir(directory): 26 if os.path.isfile(os.path.join(directory, filename)): 27 base, ext = os.path.splitext(filename) 28 if base.endswith('-py3'): 29 continue 30 if not has_ucs4 and base.find('-ucs4-') > -1: 31 continue 32 filenames.setdefault(base, []).append(ext) 33 filenames = filenames.items() 34 filenames.sort() 35 return filenames 36 37def parse_arguments(args): 38 if args is None: 39 args = sys.argv[1:] 40 verbose = False 41 if '-v' in args: 42 verbose = True 43 args.remove('-v') 44 if '--verbose' in args: 45 verbose = True 46 args.remove('--verbose') 47 if 'YAML_TEST_VERBOSE' in os.environ: 48 verbose = True 49 include_functions = [] 50 if args: 51 include_functions.append(args.pop(0)) 52 if 'YAML_TEST_FUNCTIONS' in os.environ: 53 include_functions.extend(os.environ['YAML_TEST_FUNCTIONS'].split()) 54 include_filenames = [] 55 include_filenames.extend(args) 56 if 'YAML_TEST_FILENAMES' in os.environ: 57 include_filenames.extend(os.environ['YAML_TEST_FILENAMES'].split()) 58 return include_functions, include_filenames, verbose 59 60def execute(function, filenames, verbose): 61 if hasattr(function, 'unittest_name'): 62 name = function.unittest_name 63 else: 64 name = function.func_name 65 if verbose: 66 sys.stdout.write('='*75+'\n') 67 sys.stdout.write('%s(%s)...\n' % (name, ', '.join(filenames))) 68 try: 69 function(verbose=verbose, *filenames) 70 except Exception, exc: 71 info = sys.exc_info() 72 if isinstance(exc, AssertionError): 73 kind = 'FAILURE' 74 else: 75 kind = 'ERROR' 76 if verbose: 77 traceback.print_exc(limit=1, file=sys.stdout) 78 else: 79 sys.stdout.write(kind[0]) 80 sys.stdout.flush() 81 else: 82 kind = 'SUCCESS' 83 info = None 84 if not verbose: 85 sys.stdout.write('.') 86 sys.stdout.flush() 87 return (name, filenames, kind, info) 88 89def display(results, verbose): 90 if results and not verbose: 91 sys.stdout.write('\n') 92 total = len(results) 93 failures = 0 94 errors = 0 95 for name, filenames, kind, info in results: 96 if kind == 'SUCCESS': 97 continue 98 if kind == 'FAILURE': 99 failures += 1 100 if kind == 'ERROR': 101 errors += 1 102 sys.stdout.write('='*75+'\n') 103 sys.stdout.write('%s(%s): %s\n' % (name, ', '.join(filenames), kind)) 104 if kind == 'ERROR': 105 traceback.print_exception(file=sys.stdout, *info) 106 else: 107 sys.stdout.write('Traceback (most recent call last):\n') 108 traceback.print_tb(info[2], file=sys.stdout) 109 sys.stdout.write('%s: see below\n' % info[0].__name__) 110 sys.stdout.write('~'*75+'\n') 111 for arg in info[1].args: 112 pprint.pprint(arg, stream=sys.stdout) 113 for filename in filenames: 114 sys.stdout.write('-'*75+'\n') 115 sys.stdout.write('%s:\n' % filename) 116 data = open(filename, 'rb').read() 117 sys.stdout.write(data) 118 if data and data[-1] != '\n': 119 sys.stdout.write('\n') 120 sys.stdout.write('='*75+'\n') 121 sys.stdout.write('TESTS: %s\n' % total) 122 if failures: 123 sys.stdout.write('FAILURES: %s\n' % failures) 124 if errors: 125 sys.stdout.write('ERRORS: %s\n' % errors) 126 return not (failures or errors) 127 128def run(collections, args=None): 129 test_functions = find_test_functions(collections) 130 test_filenames = find_test_filenames(DATA) 131 include_functions, include_filenames, verbose = parse_arguments(args) 132 results = [] 133 for function in test_functions: 134 if include_functions and function.func_name not in include_functions: 135 continue 136 if function.unittest: 137 for base, exts in test_filenames: 138 if include_filenames and base not in include_filenames: 139 continue 140 filenames = [] 141 for ext in function.unittest: 142 if ext not in exts: 143 break 144 filenames.append(os.path.join(DATA, base+ext)) 145 else: 146 skip_exts = getattr(function, 'skip', []) 147 for skip_ext in skip_exts: 148 if skip_ext in exts: 149 break 150 else: 151 result = execute(function, filenames, verbose) 152 results.append(result) 153 else: 154 result = execute(function, [], verbose) 155 results.append(result) 156 return display(results, verbose=verbose) 157 158