1*9c5db199SXin Li#!/usr/bin/python3 -u 2*9c5db199SXin Li 3*9c5db199SXin Lifrom __future__ import absolute_import 4*9c5db199SXin Lifrom __future__ import division 5*9c5db199SXin Lifrom __future__ import print_function 6*9c5db199SXin Liimport os, sys, unittest, optparse 7*9c5db199SXin Liimport common 8*9c5db199SXin Lifrom autotest_lib.utils import parallel 9*9c5db199SXin Li 10*9c5db199SXin Liparser = optparse.OptionParser() 11*9c5db199SXin Liparser.add_option("-r", action="store", type="string", dest="start", 12*9c5db199SXin Li default='', 13*9c5db199SXin Li help="root directory to start running unittests") 14*9c5db199SXin Liparser.add_option("--full", action="store_true", dest="full", default=False, 15*9c5db199SXin Li help="whether to run the shortened version of the test") 16*9c5db199SXin Liparser.add_option("--debug", action="store_true", dest="debug", default=False, 17*9c5db199SXin Li help="run in debug mode") 18*9c5db199SXin Liparser.add_option("--skip-tests", dest="skip_tests", default=[], 19*9c5db199SXin Li help="A space separated list of tests to skip") 20*9c5db199SXin Li 21*9c5db199SXin Liparser.set_defaults(module_list=None) 22*9c5db199SXin Li 23*9c5db199SXin Li# Following sets are used to define a collection of modules that are optional 24*9c5db199SXin Li# tests and do not need to be executed in unittest suite for various reasons. 25*9c5db199SXin Li# Each entry can be file name or relative path that's relative to the parent 26*9c5db199SXin Li# folder of the folder containing this file (unittest_suite.py). The list 27*9c5db199SXin Li# will be used to filter any test file with matching name or matching full 28*9c5db199SXin Li# path. If a file's name is too general and has a chance to collide with files 29*9c5db199SXin Li# in other folder, it is recommended to specify its relative path here, e.g., 30*9c5db199SXin Li# using 'mirror/trigger_unittest.py', instead of 'trigger_unittest.py' only. 31*9c5db199SXin Li 32*9c5db199SXin LiREQUIRES_DJANGO = set(( 33*9c5db199SXin Li 'frontend_unittest.py', 34*9c5db199SXin Li 'csv_encoder_unittest.py', 35*9c5db199SXin Li 'rpc_interface_unittest.py', 36*9c5db199SXin Li 'models_test.py', 37*9c5db199SXin Li 'rpc_utils_unittest.py', 38*9c5db199SXin Li 'site_rpc_utils_unittest.py', 39*9c5db199SXin Li 'execution_engine_unittest.py', 40*9c5db199SXin Li 'service_proxy_lib_test.py', 41*9c5db199SXin Li 'site_parse_unittest.py', 42*9c5db199SXin Li )) 43*9c5db199SXin Li 44*9c5db199SXin LiREQUIRES_MYSQLDB = set(( 45*9c5db199SXin Li 'migrate_unittest.py', 46*9c5db199SXin Li 'db_utils_unittest.py', 47*9c5db199SXin Li )) 48*9c5db199SXin Li 49*9c5db199SXin LiREQUIRES_GWT = set(( 50*9c5db199SXin Li 'client_compilation_unittest.py', 51*9c5db199SXin Li )) 52*9c5db199SXin Li 53*9c5db199SXin LiREQUIRES_SIMPLEJSON = set(( 54*9c5db199SXin Li 'serviceHandler_unittest.py', 55*9c5db199SXin Li )) 56*9c5db199SXin Li 57*9c5db199SXin LiREQUIRES_AUTH = set (( 58*9c5db199SXin Li 'trigger_unittest.py', 59*9c5db199SXin Li )) 60*9c5db199SXin Li 61*9c5db199SXin LiREQUIRES_HTTPLIB2 = set(( 62*9c5db199SXin Li )) 63*9c5db199SXin Li 64*9c5db199SXin LiREQUIRES_PROTOBUFS = set(( 65*9c5db199SXin Li 'cloud_console_client_unittest.py', 66*9c5db199SXin Li 'job_serializer_unittest.py', 67*9c5db199SXin Li )) 68*9c5db199SXin Li 69*9c5db199SXin LiREQUIRES_SELENIUM = set(( 70*9c5db199SXin Li 'ap_configurator_factory_unittest.py', 71*9c5db199SXin Li 'ap_batch_locker_unittest.py' 72*9c5db199SXin Li )) 73*9c5db199SXin Li 74*9c5db199SXin LiLONG_RUNTIME = set(( 75*9c5db199SXin Li 'barrier_unittest.py', 76*9c5db199SXin Li 'logging_manager_test.py', 77*9c5db199SXin Li 'task_loop_unittest.py' # crbug.com/254030 78*9c5db199SXin Li )) 79*9c5db199SXin Li 80*9c5db199SXin Li# Unitests that only work in chroot. The names are for module name, thus no 81*9c5db199SXin Li# file extension of ".py". 82*9c5db199SXin LiREQUIRES_CHROOT = set(( 83*9c5db199SXin Li 'mbim_channel_unittest', 84*9c5db199SXin Li )) 85*9c5db199SXin Li 86*9c5db199SXin LiSKIP = set(( 87*9c5db199SXin Li # This particular KVM autotest test is not a unittest 88*9c5db199SXin Li 'guest_test.py', 89*9c5db199SXin Li 'ap_configurator_test.py', 90*9c5db199SXin Li 'chaos_base_test.py', 91*9c5db199SXin Li 'chaos_interop_test.py', 92*9c5db199SXin Li # crbug.com/251395 93*9c5db199SXin Li 'dev_server_test.py', 94*9c5db199SXin Li 'full_release_test.py', 95*9c5db199SXin Li 'scheduler_lib_unittest.py', 96*9c5db199SXin Li 'webstore_test.py', 97*9c5db199SXin Li # crbug.com/432621 These files are not tests, and will disappear soon. 98*9c5db199SXin Li 'des_01_test.py', 99*9c5db199SXin Li 'des_02_test.py', 100*9c5db199SXin Li # Require lxc to be installed 101*9c5db199SXin Li 'base_image_unittest.py', 102*9c5db199SXin Li 'container_bucket_unittest.py', 103*9c5db199SXin Li 'container_factory_unittest.py', 104*9c5db199SXin Li 'container_unittest.py', 105*9c5db199SXin Li 'lxc_functional_test.py', 106*9c5db199SXin Li 'service_unittest.py', 107*9c5db199SXin Li 'zygote_unittest.py', 108*9c5db199SXin Li )) 109*9c5db199SXin Li 110*9c5db199SXin LiLONG_TESTS = (REQUIRES_MYSQLDB | 111*9c5db199SXin Li REQUIRES_GWT | 112*9c5db199SXin Li REQUIRES_HTTPLIB2 | 113*9c5db199SXin Li REQUIRES_AUTH | 114*9c5db199SXin Li REQUIRES_PROTOBUFS | 115*9c5db199SXin Li REQUIRES_SELENIUM | 116*9c5db199SXin Li LONG_RUNTIME) 117*9c5db199SXin Li 118*9c5db199SXin LiROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 119*9c5db199SXin Li 120*9c5db199SXin Li# The set of files in LONG_TESTS with its full path 121*9c5db199SXin LiLONG_TESTS_FULL_PATH = {os.path.join(ROOT, t) for t in LONG_TESTS} 122*9c5db199SXin Li 123*9c5db199SXin Liclass TestFailure(Exception): 124*9c5db199SXin Li """Exception type for any test failure.""" 125*9c5db199SXin Li pass 126*9c5db199SXin Li 127*9c5db199SXin Li 128*9c5db199SXin Lidef run_test(mod_names, options): 129*9c5db199SXin Li """ 130*9c5db199SXin Li @param mod_names: A list of individual parts of the module name to import 131*9c5db199SXin Li and run as a test suite. 132*9c5db199SXin Li @param options: optparse options. 133*9c5db199SXin Li """ 134*9c5db199SXin Li if not options.debug: 135*9c5db199SXin Li parallel.redirect_io() 136*9c5db199SXin Li 137*9c5db199SXin Li print("Running %s" % '.'.join(mod_names)) 138*9c5db199SXin Li mod = common.setup_modules.import_module(mod_names[-1], 139*9c5db199SXin Li '.'.join(mod_names[:-1])) 140*9c5db199SXin Li test = unittest.defaultTestLoader.loadTestsFromModule(mod) 141*9c5db199SXin Li suite = unittest.TestSuite(test) 142*9c5db199SXin Li runner = unittest.TextTestRunner(verbosity=2) 143*9c5db199SXin Li result = runner.run(suite) 144*9c5db199SXin Li if result.errors or result.failures: 145*9c5db199SXin Li msg = '%s had %d failures and %d errors.' 146*9c5db199SXin Li msg %= '.'.join(mod_names), len(result.failures), len(result.errors) 147*9c5db199SXin Li raise TestFailure(msg) 148*9c5db199SXin Li 149*9c5db199SXin Li 150*9c5db199SXin Lidef scan_for_modules(start, options): 151*9c5db199SXin Li """Scan folders and find all test modules that are not included in the 152*9c5db199SXin Li denylist (defined in LONG_TESTS). 153*9c5db199SXin Li 154*9c5db199SXin Li @param start: The absolute directory to look for tests under. 155*9c5db199SXin Li @param options: optparse options. 156*9c5db199SXin Li @return a list of modules to be executed. 157*9c5db199SXin Li """ 158*9c5db199SXin Li modules = [] 159*9c5db199SXin Li 160*9c5db199SXin Li skip_tests = SKIP 161*9c5db199SXin Li if options.skip_tests: 162*9c5db199SXin Li skip_tests.update(options.skip_tests.split()) 163*9c5db199SXin Li skip_tests_full_path = {os.path.join(ROOT, t) for t in skip_tests} 164*9c5db199SXin Li 165*9c5db199SXin Li for dir_path, sub_dirs, file_names in os.walk(start): 166*9c5db199SXin Li # Only look in and below subdirectories that are python modules. 167*9c5db199SXin Li if '__init__.py' not in file_names: 168*9c5db199SXin Li if options.full: 169*9c5db199SXin Li for file_name in file_names: 170*9c5db199SXin Li if file_name.endswith('.pyc'): 171*9c5db199SXin Li os.unlink(os.path.join(dir_path, file_name)) 172*9c5db199SXin Li # Skip all subdirectories below this one, it is not a module. 173*9c5db199SXin Li del sub_dirs[:] 174*9c5db199SXin Li if options.debug: 175*9c5db199SXin Li print('Skipping', dir_path) 176*9c5db199SXin Li continue # Skip this directory. 177*9c5db199SXin Li 178*9c5db199SXin Li # Look for unittest files. 179*9c5db199SXin Li for file_name in file_names: 180*9c5db199SXin Li if (file_name.endswith('_unittest.py') or 181*9c5db199SXin Li file_name.endswith('_test.py')): 182*9c5db199SXin Li file_path = os.path.join(dir_path, file_name) 183*9c5db199SXin Li if (not options.full and 184*9c5db199SXin Li (file_name in LONG_TESTS or 185*9c5db199SXin Li file_path in LONG_TESTS_FULL_PATH)): 186*9c5db199SXin Li continue 187*9c5db199SXin Li if (file_name in skip_tests or 188*9c5db199SXin Li file_path in skip_tests_full_path): 189*9c5db199SXin Li continue 190*9c5db199SXin Li path_no_py = os.path.join(dir_path, file_name).rstrip('.py') 191*9c5db199SXin Li assert path_no_py.startswith(ROOT) 192*9c5db199SXin Li names = path_no_py[len(ROOT)+1:].split('/') 193*9c5db199SXin Li modules.append(['autotest_lib'] + names) 194*9c5db199SXin Li if options.debug: 195*9c5db199SXin Li print('testing', path_no_py) 196*9c5db199SXin Li return modules 197*9c5db199SXin Li 198*9c5db199SXin Li 199*9c5db199SXin Lidef is_inside_chroot(): 200*9c5db199SXin Li """Check if the process is running inside the chroot. 201*9c5db199SXin Li 202*9c5db199SXin Li @return: True if the process is running inside the chroot, False otherwise. 203*9c5db199SXin Li """ 204*9c5db199SXin Li return os.path.exists('/etc/cros_chroot_version') 205*9c5db199SXin Li 206*9c5db199SXin Li 207*9c5db199SXin Lidef find_and_run_tests(start, options): 208*9c5db199SXin Li """ 209*9c5db199SXin Li Find and run Python unittest suites below the given directory. Only look 210*9c5db199SXin Li in subdirectories of start that are actual importable Python modules. 211*9c5db199SXin Li 212*9c5db199SXin Li @param start: The absolute directory to look for tests under. 213*9c5db199SXin Li @param options: optparse options. 214*9c5db199SXin Li """ 215*9c5db199SXin Li if options.module_list: 216*9c5db199SXin Li modules = [] 217*9c5db199SXin Li for m in options.module_list: 218*9c5db199SXin Li modules.append(m.split('.')) 219*9c5db199SXin Li else: 220*9c5db199SXin Li modules = scan_for_modules(start, options) 221*9c5db199SXin Li 222*9c5db199SXin Li if options.debug: 223*9c5db199SXin Li print('Number of test modules found:', len(modules)) 224*9c5db199SXin Li 225*9c5db199SXin Li chroot = is_inside_chroot() 226*9c5db199SXin Li functions = {} 227*9c5db199SXin Li for module_names in modules: 228*9c5db199SXin Li if not chroot and module_names[-1] in REQUIRES_CHROOT: 229*9c5db199SXin Li if options.debug: 230*9c5db199SXin Li print('Test %s requires to run in chroot, skipped.' % 231*9c5db199SXin Li module_names[-1]) 232*9c5db199SXin Li continue 233*9c5db199SXin Li # Create a function that'll test a particular module. module=module 234*9c5db199SXin Li # is a hack to force python to evaluate the params now. We then 235*9c5db199SXin Li # rename the function to make error reporting nicer. 236*9c5db199SXin Li run_module = lambda module=module_names: run_test(module, options) 237*9c5db199SXin Li name = '.'.join(module_names) 238*9c5db199SXin Li run_module.__name__ = name 239*9c5db199SXin Li functions[run_module] = set() 240*9c5db199SXin Li 241*9c5db199SXin Li try: 242*9c5db199SXin Li dargs = {} 243*9c5db199SXin Li if options.debug: 244*9c5db199SXin Li dargs['max_simultaneous_procs'] = 1 245*9c5db199SXin Li pe = parallel.ParallelExecute(functions, **dargs) 246*9c5db199SXin Li pe.run_until_completion() 247*9c5db199SXin Li except parallel.ParallelError as err: 248*9c5db199SXin Li return err.errors 249*9c5db199SXin Li return [] 250*9c5db199SXin Li 251*9c5db199SXin Li 252*9c5db199SXin Lidef main(): 253*9c5db199SXin Li """Entry point for unittest_suite.py""" 254*9c5db199SXin Li options, args = parser.parse_args() 255*9c5db199SXin Li if args: 256*9c5db199SXin Li options.module_list = args 257*9c5db199SXin Li 258*9c5db199SXin Li # Strip the arguments off the command line, so that the unit tests do not 259*9c5db199SXin Li # see them. 260*9c5db199SXin Li del sys.argv[1:] 261*9c5db199SXin Li 262*9c5db199SXin Li absolute_start = os.path.join(ROOT, options.start) 263*9c5db199SXin Li errors = find_and_run_tests(absolute_start, options) 264*9c5db199SXin Li if errors: 265*9c5db199SXin Li print("%d tests resulted in an error/failure:" % len(errors)) 266*9c5db199SXin Li for error in errors: 267*9c5db199SXin Li print("\t%s" % error) 268*9c5db199SXin Li print("Rerun", sys.argv[0], "--debug to see the failure details.") 269*9c5db199SXin Li sys.exit(1) 270*9c5db199SXin Li else: 271*9c5db199SXin Li print("All passed!") 272*9c5db199SXin Li sys.exit(0) 273*9c5db199SXin Li 274*9c5db199SXin Li 275*9c5db199SXin Liif __name__ == "__main__": 276*9c5db199SXin Li main() 277