unittest_suite.py revision 78b0595e3841a41aae3c0ce20178629c42c1e3b3
1#!/usr/bin/python -u 2 3import os, sys, unittest, optparse 4import common 5from autotest_lib.utils import parallel 6from autotest_lib.client.common_lib.test_utils import unittest as custom_unittest 7 8parser = optparse.OptionParser() 9parser.add_option("-r", action="store", type="string", dest="start", 10 default='', 11 help="root directory to start running unittests") 12parser.add_option("--full", action="store_true", dest="full", default=False, 13 help="whether to run the shortened version of the test") 14parser.add_option("--debug", action="store_true", dest="debug", default=False, 15 help="run in debug mode") 16parser.add_option("--skip-tests", dest="skip_tests", default=[], 17 help="A space separated list of tests to skip") 18 19parser.set_defaults(module_list=None) 20 21 22REQUIRES_DJANGO = set(( 23 'monitor_db_unittest.py', 24 'monitor_db_functional_test.py', 25 'monitor_db_cleanup_test.py', 26 'frontend_unittest.py', 27 'csv_encoder_unittest.py', 28 'rpc_interface_unittest.py', 29 'models_test.py', 30 'scheduler_models_unittest.py', 31 'metahost_scheduler_unittest.py', 32 'site_metahost_scheduler_unittest.py', 33 'rpc_utils_unittest.py', 34 'site_rpc_utils_unittest.py', 35 'execution_engine_unittest.py', 36 'service_proxy_lib_test.py', 37 )) 38 39REQUIRES_MYSQLDB = set(( 40 'migrate_unittest.py', 41 'db_utils_unittest.py', 42 )) 43 44REQUIRES_GWT = set(( 45 'client_compilation_unittest.py', 46 )) 47 48REQUIRES_SIMPLEJSON = set(( 49 'resources_test.py', 50 'serviceHandler_unittest.py', 51 )) 52 53REQUIRES_AUTH = set (( 54 'trigger_unittest.py', 55 )) 56 57REQUIRES_HTTPLIB2 = set(( 58 )) 59 60REQUIRES_PROTOBUFS = set(( 61 'job_serializer_unittest.py', 62 )) 63 64LONG_RUNTIME = set(( 65 'base_barrier_unittest.py', 66 'logging_manager_test.py', 67 )) 68 69 70SKIP = set(( 71 # This particular KVM autotest test is not a unittest 72 'guest_test.py', 73 'ap_configurator_test.py' 74 )) 75 76LONG_TESTS = (REQUIRES_DJANGO | 77 REQUIRES_MYSQLDB | 78 REQUIRES_GWT | 79 REQUIRES_SIMPLEJSON | 80 REQUIRES_HTTPLIB2 | 81 REQUIRES_AUTH | 82 REQUIRES_PROTOBUFS | 83 LONG_RUNTIME) 84 85 86ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 87 88 89class TestFailure(Exception): 90 """Exception type for any test failure.""" 91 pass 92 93 94def run_test(mod_names, options): 95 """ 96 @param mod_names: A list of individual parts of the module name to import 97 and run as a test suite. 98 @param options: optparse options. 99 """ 100 if not options.debug: 101 parallel.redirect_io() 102 103 print "Running %s" % '.'.join(mod_names) 104 mod = common.setup_modules.import_module(mod_names[-1], 105 '.'.join(mod_names[:-1])) 106 for ut_module in [unittest, custom_unittest]: 107 test = ut_module.defaultTestLoader.loadTestsFromModule(mod) 108 suite = ut_module.TestSuite(test) 109 runner = ut_module.TextTestRunner(verbosity=2) 110 result = runner.run(suite) 111 if result.errors or result.failures: 112 msg = '%s had %d failures and %d errors.' 113 msg %= '.'.join(mod_names), len(result.failures), len(result.errors) 114 raise TestFailure(msg) 115 116 117def scan_for_modules(start, options): 118 """Scan folders and find all test modules. 119 @param start: The absolute directory to look for tests under. 120 @param options: optparse options. 121 @return a list of modules to be executed. 122 """ 123 modules = [] 124 125 skip_tests = SKIP 126 if options.skip_tests: 127 skip_tests.update(options.skip_tests.split()) 128 129 for dirpath, subdirs, filenames in os.walk(start): 130 # Only look in and below subdirectories that are python modules. 131 if '__init__.py' not in filenames: 132 if options.full: 133 for filename in filenames: 134 if filename.endswith('.pyc'): 135 os.unlink(os.path.join(dirpath, filename)) 136 # Skip all subdirectories below this one, it is not a module. 137 del subdirs[:] 138 if options.debug: 139 print 'Skipping', dirpath 140 continue # Skip this directory. 141 142 # Look for unittest files. 143 for fname in filenames: 144 if fname.endswith('_unittest.py') or fname.endswith('_test.py'): 145 if not options.full and fname in LONG_TESTS: 146 continue 147 if fname in skip_tests: 148 continue 149 path_no_py = os.path.join(dirpath, fname).rstrip('.py') 150 assert path_no_py.startswith(ROOT) 151 names = path_no_py[len(ROOT)+1:].split('/') 152 modules.append(['autotest_lib'] + names) 153 if options.debug: 154 print 'testing', path_no_py 155 return modules 156 157def find_and_run_tests(start, options): 158 """ 159 Find and run Python unittest suites below the given directory. Only look 160 in subdirectories of start that are actual importable Python modules. 161 162 @param start: The absolute directory to look for tests under. 163 @param options: optparse options. 164 """ 165 if options.module_list: 166 modules = [] 167 for m in options.module_list: 168 modules.append(m.split('.')) 169 else: 170 modules = scan_for_modules(start, options) 171 172 if options.debug: 173 print 'Number of test modules found:', len(modules) 174 175 functions = {} 176 for module_names in modules: 177 # Create a function that'll test a particular module. module=module 178 # is a hack to force python to evaluate the params now. We then 179 # rename the function to make error reporting nicer. 180 run_module = lambda module=module_names: run_test(module, options) 181 name = '.'.join(module_names) 182 run_module.__name__ = name 183 functions[run_module] = set() 184 185 try: 186 dargs = {} 187 if options.debug: 188 dargs['max_simultaneous_procs'] = 1 189 pe = parallel.ParallelExecute(functions, **dargs) 190 pe.run_until_completion() 191 except parallel.ParallelError, err: 192 return err.errors 193 return [] 194 195 196def main(): 197 """Entry point for unittest_suite.py""" 198 options, args = parser.parse_args() 199 if args: 200 options.module_list = args 201 202 # Strip the arguments off the command line, so that the unit tests do not 203 # see them. 204 del sys.argv[1:] 205 206 absolute_start = os.path.join(ROOT, options.start) 207 errors = find_and_run_tests(absolute_start, options) 208 if errors: 209 print "%d tests resulted in an error/failure:" % len(errors) 210 for error in errors: 211 print "\t%s" % error 212 print "Rerun", sys.argv[0], "--debug to see the failure details." 213 sys.exit(1) 214 else: 215 print "All passed!" 216 sys.exit(0) 217 218 219if __name__ == "__main__": 220 main() 221