1#! /usr/bin/env python 2 3import glob 4import os 5import struct 6import sys 7import unittest 8 9_glob_test_py = 'test_*.py' 10 11if os.linesep == '\n': 12 def _print(text): 13 print(text) 14else: 15 def _print(text): 16 print(text.replace('\n', os.linesep)) 17 18def get_tests(dir='.', clean=False): 19 '''Get a list of test module names in the given directory. 20 ''' 21 res, dir_ = [], '' 22 # walk recursively through all subdirectories 23 if os.path.isdir(dir): 24 if dir != '.': 25 dir_ = dir.rstrip(os.sep) + os.sep 26 for sub in os.listdir(dir): 27 if sub[0] != '.': # os.curdir 28 sub = dir_ + sub 29 if os.path.isdir(sub): 30 res.extend(get_tests(sub)) 31 glob_py = dir_ + _glob_test_py 32 elif os.path.isfile(dir): 33 glob_py = dir 34 sub = os.path.split(dir)[0] 35 if sub: # prefix 36 dir_ = sub + os.sep 37 else: 38 return res 39 # append all tests as module names 40 for test in glob.glob(glob_py): 41 test = dir_ + os.path.basename(test) 42 if clean: # remove existing bytecodes 43 for co in ('c', 'o'): 44 try: 45 os.remove(test + co) 46 except OSError: 47 pass 48 # convert to module name and remove leading ./ or .\ 49 test = test[:-3].replace(os.sep, '.') 50 test = test.lstrip('.') 51 res.append(test) 52 res.sort(key=lambda s: -s.count('muppy')) 53 return res # sorted(res) 54 55def suite(dirs=['.'], clean=False, pre=True, verbose=2): 56 '''Create a suite with all tests from the given directories. 57 58 This will also include tests from subdirectories. 59 ''' 60 all = True 61 res = unittest.TestSuite() 62 for dir in dirs: 63 for test in get_tests(dir, clean): 64 # Import tests relative to 'test' directory 65 test = test[test.find('.')+1:] 66 try: 67 mod = test.rfind('.') + 1 68 if mod > 0: # from test import mod 69 mod = test[mod:] 70 mod = __import__(test, globals(), locals(), [mod]) 71 else: # import test 72 mod = __import__(test) 73 res.addTest(unittest.defaultTestLoader.loadTestsFromModule(mod)) 74 except ImportError: 75 if sys.hexversion < 0x02050000: 76 _print('Warning: ignoring %r - incompatible with this Python version' % test) 77 else: 78 raise 79 except (SyntaxError, NameError): 80 if pre: 81 _print('Warning: ignoring %r due to an error while importing' % test) 82 else: 83 _print('Error: %r missing or not found' % test) 84 if verbose > 2: 85 raise # show the error 86 all = False 87 return res, all 88 89 90if __name__ == '__main__': 91 92 # default values for options 93 clean = False # -c[lean] -- remove all .pyo and .pyc files 94 pre = True # -pre[-install] -- try before installation 95 #pre = False # -post[-install] -- test after installation 96 verbose = 2 # -v[erbose] <level> -- verbosity level 97 98 # get options and test case directories (files?) 99 dirs = sys.argv[1:] 100 while dirs: 101 t = dirs[0] 102 n = len(t) 103 if '-clean'.startswith(t) and n > 1: 104 clean = True 105 elif '-post-install'.startswith(t) and n > 4: 106 pre = False 107 elif '-pre-install'.startswith(t) and n > 3: 108 pre = True 109 elif '-verbose'.startswith(t) and n > 1: 110 verbose = int(dirs.pop(1)) 111 else: 112 break 113 dirs = dirs[1:] 114 else: 115 dirs = ['.'] 116 117 # Insert parent directory such that the code modules can be 118 # imported, but only for pre-install testing. Import test directory 119 # for pre and post-install testing. 120 t = os.path.split(sys.path[0]) 121 if t and pre: 122 sys.path.insert(1, t[0]) 123 sys.path.append(os.path.join(t[0], 'test')) 124 125 # print some details 126 if verbose > 1: 127 t = '%d-bit [' % (struct.calcsize('P') << 3) 128 _print('Python %s\n' % sys.version.replace('[', t)) 129 if verbose > 4: 130 t = '\n ' # indentation 131 _print('Sys.path: %s\n' % t.join(sys.path)) 132 if verbose > 3: 133 _print('Test dirs: %r\n' % dirs) 134 135 # build and run single test suite 136 tst, all = suite(dirs, clean=clean, pre=pre, verbose=verbose) 137 if pre or all: 138 res = unittest.TextTestRunner(verbosity=verbose).run(tst) 139 if res.wasSuccessful(): 140 sys.exit(0) 141 sys.exit(1) 142