11596Szelenkov@nginx.comimport fcntl 21803Szelenkov@nginx.comimport inspect 31803Szelenkov@nginx.comimport json 41596Szelenkov@nginx.comimport os 51596Szelenkov@nginx.comimport platform 61635Szelenkov@nginx.comimport re 71621Szelenkov@nginx.comimport shutil 81596Szelenkov@nginx.comimport signal 91596Szelenkov@nginx.comimport stat 101596Szelenkov@nginx.comimport subprocess 111596Szelenkov@nginx.comimport sys 121596Szelenkov@nginx.comimport tempfile 131596Szelenkov@nginx.comimport time 141654Szelenkov@nginx.comfrom multiprocessing import Process 151596Szelenkov@nginx.com 161635Szelenkov@nginx.comimport pytest 171858Szelenkov@nginx.comfrom unit.check.chroot import check_chroot 181621Szelenkov@nginx.comfrom unit.check.go import check_go 191771Szelenkov@nginx.comfrom unit.check.isolation import check_isolation 201621Szelenkov@nginx.comfrom unit.check.node import check_node 211848Szelenkov@nginx.comfrom unit.check.regex import check_regex 221621Szelenkov@nginx.comfrom unit.check.tls import check_openssl 231803Szelenkov@nginx.comfrom unit.http import TestHTTP 241902Szelenkov@nginx.comfrom unit.log import Log 251730Szelenkov@nginx.comfrom unit.option import option 261735Szelenkov@nginx.comfrom unit.utils import public_dir 271735Szelenkov@nginx.comfrom unit.utils import waitforfiles 281621Szelenkov@nginx.com 291596Szelenkov@nginx.com 301596Szelenkov@nginx.comdef pytest_addoption(parser): 311596Szelenkov@nginx.com parser.addoption( 321596Szelenkov@nginx.com "--detailed", 331596Szelenkov@nginx.com default=False, 341596Szelenkov@nginx.com action="store_true", 351596Szelenkov@nginx.com help="Detailed output for tests", 361596Szelenkov@nginx.com ) 371596Szelenkov@nginx.com parser.addoption( 381743Szelenkov@nginx.com "--print-log", 391596Szelenkov@nginx.com default=False, 401596Szelenkov@nginx.com action="store_true", 411596Szelenkov@nginx.com help="Print unit.log to stdout in case of errors", 421596Szelenkov@nginx.com ) 431596Szelenkov@nginx.com parser.addoption( 441743Szelenkov@nginx.com "--save-log", 451596Szelenkov@nginx.com default=False, 461596Szelenkov@nginx.com action="store_true", 471596Szelenkov@nginx.com help="Save unit.log after the test execution", 481596Szelenkov@nginx.com ) 491596Szelenkov@nginx.com parser.addoption( 501596Szelenkov@nginx.com "--unsafe", 511596Szelenkov@nginx.com default=False, 521596Szelenkov@nginx.com action="store_true", 531596Szelenkov@nginx.com help="Run unsafe tests", 541596Szelenkov@nginx.com ) 551761Sdefan@nginx.com parser.addoption( 561761Sdefan@nginx.com "--user", 571761Sdefan@nginx.com type=str, 581761Sdefan@nginx.com help="Default user for non-privileged processes of unitd", 591761Sdefan@nginx.com ) 601803Szelenkov@nginx.com parser.addoption( 611844Szelenkov@nginx.com "--fds-threshold", 621844Szelenkov@nginx.com type=int, 631844Szelenkov@nginx.com default=0, 641844Szelenkov@nginx.com help="File descriptors threshold", 651844Szelenkov@nginx.com ) 661844Szelenkov@nginx.com parser.addoption( 671803Szelenkov@nginx.com "--restart", 681803Szelenkov@nginx.com default=False, 691803Szelenkov@nginx.com action="store_true", 701803Szelenkov@nginx.com help="Force Unit to restart after every test", 711803Szelenkov@nginx.com ) 721596Szelenkov@nginx.com 731596Szelenkov@nginx.com 741596Szelenkov@nginx.comunit_instance = {} 751654Szelenkov@nginx.com_processes = [] 761914Szelenkov@nginx.com_fds_info = { 771844Szelenkov@nginx.com 'main': {'fds': 0, 'skip': False}, 781844Szelenkov@nginx.com 'router': {'name': 'unit: router', 'pid': -1, 'fds': 0, 'skip': False}, 791844Szelenkov@nginx.com 'controller': { 801844Szelenkov@nginx.com 'name': 'unit: controller', 811844Szelenkov@nginx.com 'pid': -1, 821844Szelenkov@nginx.com 'fds': 0, 831844Szelenkov@nginx.com 'skip': False, 841844Szelenkov@nginx.com }, 851844Szelenkov@nginx.com} 861803Szelenkov@nginx.comhttp = TestHTTP() 871596Szelenkov@nginx.com 881848Szelenkov@nginx.com 891596Szelenkov@nginx.comdef pytest_configure(config): 901730Szelenkov@nginx.com option.config = config.option 911730Szelenkov@nginx.com 921730Szelenkov@nginx.com option.detailed = config.option.detailed 931844Szelenkov@nginx.com option.fds_threshold = config.option.fds_threshold 941730Szelenkov@nginx.com option.print_log = config.option.print_log 951730Szelenkov@nginx.com option.save_log = config.option.save_log 961730Szelenkov@nginx.com option.unsafe = config.option.unsafe 971761Sdefan@nginx.com option.user = config.option.user 981803Szelenkov@nginx.com option.restart = config.option.restart 991596Szelenkov@nginx.com 1001596Szelenkov@nginx.com option.generated_tests = {} 1011596Szelenkov@nginx.com option.current_dir = os.path.abspath( 1021596Szelenkov@nginx.com os.path.join(os.path.dirname(__file__), os.pardir) 1031596Szelenkov@nginx.com ) 1041596Szelenkov@nginx.com option.test_dir = option.current_dir + '/test' 1051596Szelenkov@nginx.com option.architecture = platform.architecture()[0] 1061596Szelenkov@nginx.com option.system = platform.system() 1071596Szelenkov@nginx.com 1081757St.nateldemoura@f5.com option.cache_dir = tempfile.mkdtemp(prefix='unit-test-cache-') 1091757St.nateldemoura@f5.com public_dir(option.cache_dir) 1101757St.nateldemoura@f5.com 1111596Szelenkov@nginx.com # set stdout to non-blocking 1121596Szelenkov@nginx.com 1131596Szelenkov@nginx.com if option.detailed or option.print_log: 1141596Szelenkov@nginx.com fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0) 1151596Szelenkov@nginx.com 1161596Szelenkov@nginx.com 1171914Szelenkov@nginx.comdef print_log_on_assert(func): 1181914Szelenkov@nginx.com def inner_function(*args, **kwargs): 1191914Szelenkov@nginx.com try: 1201914Szelenkov@nginx.com func(*args, **kwargs) 1211914Szelenkov@nginx.com except AssertionError as e: 1221914Szelenkov@nginx.com _print_log(kwargs.get('log', None)) 1231914Szelenkov@nginx.com raise e 1241914Szelenkov@nginx.com 1251914Szelenkov@nginx.com return inner_function 1261914Szelenkov@nginx.com 1271914Szelenkov@nginx.com 1281596Szelenkov@nginx.comdef pytest_generate_tests(metafunc): 1291596Szelenkov@nginx.com cls = metafunc.cls 1301848Szelenkov@nginx.com if ( 1311848Szelenkov@nginx.com not hasattr(cls, 'application_type') 1321848Szelenkov@nginx.com or cls.application_type == None 1331848Szelenkov@nginx.com or cls.application_type == 'external' 1341848Szelenkov@nginx.com ): 1351596Szelenkov@nginx.com return 1361596Szelenkov@nginx.com 1371596Szelenkov@nginx.com type = cls.application_type 1381596Szelenkov@nginx.com 1391611Smax.romanov@nginx.com def generate_tests(versions): 1401611Smax.romanov@nginx.com metafunc.fixturenames.append('tmp_ct') 1411630Smax.romanov@nginx.com metafunc.parametrize('tmp_ct', versions) 1421611Smax.romanov@nginx.com 1431630Smax.romanov@nginx.com for version in versions: 1441611Smax.romanov@nginx.com option.generated_tests[ 1451630Smax.romanov@nginx.com metafunc.function.__name__ + '[{}]'.format(version) 1461611Smax.romanov@nginx.com ] = (type + ' ' + version) 1471611Smax.romanov@nginx.com 1481596Szelenkov@nginx.com # take available module from option and generate tests for each version 1491596Szelenkov@nginx.com 1501611Smax.romanov@nginx.com for module, prereq_version in cls.prerequisites['modules'].items(): 1511596Szelenkov@nginx.com if module in option.available['modules']: 1521596Szelenkov@nginx.com available_versions = option.available['modules'][module] 1531596Szelenkov@nginx.com 1541596Szelenkov@nginx.com if prereq_version == 'all': 1551611Smax.romanov@nginx.com generate_tests(available_versions) 1561596Szelenkov@nginx.com 1571596Szelenkov@nginx.com elif prereq_version == 'any': 1581596Szelenkov@nginx.com option.generated_tests[metafunc.function.__name__] = ( 1591596Szelenkov@nginx.com type + ' ' + available_versions[0] 1601596Szelenkov@nginx.com ) 1611611Smax.romanov@nginx.com elif callable(prereq_version): 1621611Smax.romanov@nginx.com generate_tests( 1631611Smax.romanov@nginx.com list(filter(prereq_version, available_versions)) 1641611Smax.romanov@nginx.com ) 1651611Smax.romanov@nginx.com 1661596Szelenkov@nginx.com else: 1671611Smax.romanov@nginx.com raise ValueError( 1681611Smax.romanov@nginx.com """ 1691611Smax.romanov@nginx.comUnexpected prerequisite version "%s" for module "%s" in %s. 1701611Smax.romanov@nginx.com'all', 'any' or callable expected.""" 1711611Smax.romanov@nginx.com % (str(prereq_version), module, str(cls)) 1721611Smax.romanov@nginx.com ) 1731596Szelenkov@nginx.com 1741596Szelenkov@nginx.com 1751596Szelenkov@nginx.comdef pytest_sessionstart(session): 1761596Szelenkov@nginx.com option.available = {'modules': {}, 'features': {}} 1771596Szelenkov@nginx.com 1781596Szelenkov@nginx.com unit = unit_run() 1791596Szelenkov@nginx.com 1801596Szelenkov@nginx.com # read unit.log 1811596Szelenkov@nginx.com 1821596Szelenkov@nginx.com for i in range(50): 1831850Smax.romanov@nginx.com with open(Log.get_path(), 'r') as f: 1841596Szelenkov@nginx.com log = f.read() 1851596Szelenkov@nginx.com m = re.search('controller started', log) 1861596Szelenkov@nginx.com 1871596Szelenkov@nginx.com if m is None: 1881596Szelenkov@nginx.com time.sleep(0.1) 1891596Szelenkov@nginx.com else: 1901596Szelenkov@nginx.com break 1911596Szelenkov@nginx.com 1921596Szelenkov@nginx.com if m is None: 1931654Szelenkov@nginx.com _print_log(log) 1941596Szelenkov@nginx.com exit("Unit is writing log too long") 1951596Szelenkov@nginx.com 1961596Szelenkov@nginx.com # discover available modules from unit.log 1971596Szelenkov@nginx.com 1981596Szelenkov@nginx.com for module in re.findall(r'module: ([a-zA-Z]+) (.*) ".*"$', log, re.M): 1991705Smax.romanov@nginx.com versions = option.available['modules'].setdefault(module[0], []) 2001705Smax.romanov@nginx.com if module[1] not in versions: 2011705Smax.romanov@nginx.com versions.append(module[1]) 2021596Szelenkov@nginx.com 2031621Szelenkov@nginx.com # discover modules from check 2041621Szelenkov@nginx.com 2051621Szelenkov@nginx.com option.available['modules']['openssl'] = check_openssl(unit['unitd']) 2061621Szelenkov@nginx.com option.available['modules']['go'] = check_go( 2071621Szelenkov@nginx.com option.current_dir, unit['temp_dir'], option.test_dir 2081621Szelenkov@nginx.com ) 2091621Szelenkov@nginx.com option.available['modules']['node'] = check_node(option.current_dir) 2101807Szelenkov@nginx.com option.available['modules']['regex'] = check_regex(unit['unitd']) 2111621Szelenkov@nginx.com 2121621Szelenkov@nginx.com # remove None values 2131621Szelenkov@nginx.com 2141621Szelenkov@nginx.com option.available['modules'] = { 2151621Szelenkov@nginx.com k: v for k, v in option.available['modules'].items() if v is not None 2161621Szelenkov@nginx.com } 2171621Szelenkov@nginx.com 2181858Szelenkov@nginx.com check_chroot() 2191740Szelenkov@nginx.com check_isolation() 2201740Szelenkov@nginx.com 2211805Szelenkov@nginx.com _clear_conf(unit['temp_dir'] + '/control.unit.sock') 2221803Szelenkov@nginx.com 2231596Szelenkov@nginx.com unit_stop() 2241596Szelenkov@nginx.com 2251730Szelenkov@nginx.com _check_alerts() 2261730Szelenkov@nginx.com 2271803Szelenkov@nginx.com if option.restart: 2281803Szelenkov@nginx.com shutil.rmtree(unit_instance['temp_dir']) 2291596Szelenkov@nginx.com 2301848Szelenkov@nginx.com 2311654Szelenkov@nginx.com@pytest.hookimpl(tryfirst=True, hookwrapper=True) 2321654Szelenkov@nginx.comdef pytest_runtest_makereport(item, call): 2331654Szelenkov@nginx.com # execute all other hooks to obtain the report object 2341654Szelenkov@nginx.com outcome = yield 2351654Szelenkov@nginx.com rep = outcome.get_result() 2361654Szelenkov@nginx.com 2371654Szelenkov@nginx.com # set a report attribute for each phase of a call, which can 2381654Szelenkov@nginx.com # be "setup", "call", "teardown" 2391654Szelenkov@nginx.com 2401654Szelenkov@nginx.com setattr(item, "rep_" + rep.when, rep) 2411654Szelenkov@nginx.com 2421654Szelenkov@nginx.com 2431741Szelenkov@nginx.com@pytest.fixture(scope='class', autouse=True) 2441741Szelenkov@nginx.comdef check_prerequisites(request): 2451741Szelenkov@nginx.com cls = request.cls 2461741Szelenkov@nginx.com missed = [] 2471741Szelenkov@nginx.com 2481741Szelenkov@nginx.com # check modules 2491741Szelenkov@nginx.com 2501741Szelenkov@nginx.com if 'modules' in cls.prerequisites: 2511741Szelenkov@nginx.com available_modules = list(option.available['modules'].keys()) 2521741Szelenkov@nginx.com 2531741Szelenkov@nginx.com for module in cls.prerequisites['modules']: 2541741Szelenkov@nginx.com if module in available_modules: 2551741Szelenkov@nginx.com continue 2561741Szelenkov@nginx.com 2571741Szelenkov@nginx.com missed.append(module) 2581741Szelenkov@nginx.com 2591741Szelenkov@nginx.com if missed: 2601741Szelenkov@nginx.com pytest.skip('Unit has no ' + ', '.join(missed) + ' module(s)') 2611741Szelenkov@nginx.com 2621741Szelenkov@nginx.com # check features 2631741Szelenkov@nginx.com 2641741Szelenkov@nginx.com if 'features' in cls.prerequisites: 2651741Szelenkov@nginx.com available_features = list(option.available['features'].keys()) 2661741Szelenkov@nginx.com 2671741Szelenkov@nginx.com for feature in cls.prerequisites['features']: 2681741Szelenkov@nginx.com if feature in available_features: 2691741Szelenkov@nginx.com continue 2701741Szelenkov@nginx.com 2711741Szelenkov@nginx.com missed.append(feature) 2721741Szelenkov@nginx.com 2731741Szelenkov@nginx.com if missed: 2741741Szelenkov@nginx.com pytest.skip(', '.join(missed) + ' feature(s) not supported') 2751741Szelenkov@nginx.com 2761741Szelenkov@nginx.com 2771654Szelenkov@nginx.com@pytest.fixture(autouse=True) 2781654Szelenkov@nginx.comdef run(request): 2791654Szelenkov@nginx.com unit = unit_run() 2801654Szelenkov@nginx.com 2811596Szelenkov@nginx.com option.skip_alerts = [ 2821596Szelenkov@nginx.com r'read signalfd\(4\) failed', 2831596Szelenkov@nginx.com r'sendmsg.+failed', 2841596Szelenkov@nginx.com r'recvmsg.+failed', 2851596Szelenkov@nginx.com ] 2861596Szelenkov@nginx.com option.skip_sanitizer = False 2871596Szelenkov@nginx.com 2881914Szelenkov@nginx.com _fds_info['main']['skip'] = False 2891914Szelenkov@nginx.com _fds_info['router']['skip'] = False 2901914Szelenkov@nginx.com _fds_info['controller']['skip'] = False 2911844Szelenkov@nginx.com 2921654Szelenkov@nginx.com yield 2931654Szelenkov@nginx.com 2941654Szelenkov@nginx.com # stop unit 2951654Szelenkov@nginx.com 2961803Szelenkov@nginx.com error_stop_unit = unit_stop() 2971803Szelenkov@nginx.com error_stop_processes = stop_processes() 2981803Szelenkov@nginx.com 2991803Szelenkov@nginx.com # prepare log 3001803Szelenkov@nginx.com 3011850Smax.romanov@nginx.com with Log.open(encoding='utf-8') as f: 3021803Szelenkov@nginx.com log = f.read() 3031850Smax.romanov@nginx.com Log.set_pos(f.tell()) 3041654Szelenkov@nginx.com 3051803Szelenkov@nginx.com if not option.save_log and option.restart: 3061803Szelenkov@nginx.com shutil.rmtree(unit['temp_dir']) 3071850Smax.romanov@nginx.com Log.set_pos(0) 3081803Szelenkov@nginx.com 3091803Szelenkov@nginx.com # clean temp_dir before the next test 3101654Szelenkov@nginx.com 3111803Szelenkov@nginx.com if not option.restart: 3121914Szelenkov@nginx.com _clear_conf(unit['temp_dir'] + '/control.unit.sock', log=log) 3131803Szelenkov@nginx.com 3141803Szelenkov@nginx.com for item in os.listdir(unit['temp_dir']): 3151803Szelenkov@nginx.com if item not in [ 3161803Szelenkov@nginx.com 'control.unit.sock', 3171803Szelenkov@nginx.com 'state', 3181803Szelenkov@nginx.com 'unit.pid', 3191803Szelenkov@nginx.com 'unit.log', 3201803Szelenkov@nginx.com ]: 3211803Szelenkov@nginx.com path = os.path.join(unit['temp_dir'], item) 3221654Szelenkov@nginx.com 3231803Szelenkov@nginx.com public_dir(path) 3241654Szelenkov@nginx.com 3251848Szelenkov@nginx.com if os.path.isfile(path) or stat.S_ISSOCK( 3261848Szelenkov@nginx.com os.stat(path).st_mode 3271848Szelenkov@nginx.com ): 3281803Szelenkov@nginx.com os.remove(path) 3291803Szelenkov@nginx.com else: 3301933Smax.romanov@nginx.com for attempt in range(10): 3311933Smax.romanov@nginx.com try: 3321933Smax.romanov@nginx.com shutil.rmtree(path) 3331933Smax.romanov@nginx.com break 3341933Smax.romanov@nginx.com except OSError as err: 3351933Smax.romanov@nginx.com if err.errno != 16: 3361933Smax.romanov@nginx.com raise 3371933Smax.romanov@nginx.com time.sleep(1) 3381654Szelenkov@nginx.com 3391914Szelenkov@nginx.com # check descriptors 3401844Szelenkov@nginx.com 3411914Szelenkov@nginx.com _check_fds(log=log) 3421844Szelenkov@nginx.com 3431654Szelenkov@nginx.com # print unit.log in case of error 3441654Szelenkov@nginx.com 3451706Smax.romanov@nginx.com if hasattr(request.node, 'rep_call') and request.node.rep_call.failed: 3461803Szelenkov@nginx.com _print_log(log) 3471803Szelenkov@nginx.com 3481803Szelenkov@nginx.com if error_stop_unit or error_stop_processes: 3491803Szelenkov@nginx.com _print_log(log) 3501654Szelenkov@nginx.com 3511803Szelenkov@nginx.com # check unit.log for errors 3521654Szelenkov@nginx.com 3531803Szelenkov@nginx.com assert error_stop_unit is None, 'stop unit' 3541803Szelenkov@nginx.com assert error_stop_processes is None, 'stop processes' 3551803Szelenkov@nginx.com 3561803Szelenkov@nginx.com _check_alerts(log=log) 3571654Szelenkov@nginx.com 3581848Szelenkov@nginx.com 359*1986Szelenkov@nginx.comdef unit_run(state_dir=None): 3601596Szelenkov@nginx.com global unit_instance 3611803Szelenkov@nginx.com 3621803Szelenkov@nginx.com if not option.restart and 'unitd' in unit_instance: 3631803Szelenkov@nginx.com return unit_instance 3641803Szelenkov@nginx.com 3651596Szelenkov@nginx.com build_dir = option.current_dir + '/build' 3661596Szelenkov@nginx.com unitd = build_dir + '/unitd' 3671596Szelenkov@nginx.com 3681596Szelenkov@nginx.com if not os.path.isfile(unitd): 3691596Szelenkov@nginx.com exit('Could not find unit') 3701596Szelenkov@nginx.com 3711596Szelenkov@nginx.com temp_dir = tempfile.mkdtemp(prefix='unit-test-') 3721596Szelenkov@nginx.com public_dir(temp_dir) 3731596Szelenkov@nginx.com 3741596Szelenkov@nginx.com if oct(stat.S_IMODE(os.stat(build_dir).st_mode)) != '0o777': 3751596Szelenkov@nginx.com public_dir(build_dir) 3761596Szelenkov@nginx.com 377*1986Szelenkov@nginx.com state = temp_dir + '/state' if state_dir is None else state_dir 378*1986Szelenkov@nginx.com if not os.path.isdir(state): 379*1986Szelenkov@nginx.com os.mkdir(state) 3801596Szelenkov@nginx.com 3811761Sdefan@nginx.com unitd_args = [ 3821761Sdefan@nginx.com unitd, 3831761Sdefan@nginx.com '--no-daemon', 3841761Sdefan@nginx.com '--modules', 3851761Sdefan@nginx.com build_dir, 3861761Sdefan@nginx.com '--state', 387*1986Szelenkov@nginx.com state, 3881761Sdefan@nginx.com '--pid', 3891761Sdefan@nginx.com temp_dir + '/unit.pid', 3901761Sdefan@nginx.com '--log', 3911761Sdefan@nginx.com temp_dir + '/unit.log', 3921761Sdefan@nginx.com '--control', 3931761Sdefan@nginx.com 'unix:' + temp_dir + '/control.unit.sock', 3941761Sdefan@nginx.com '--tmp', 3951761Sdefan@nginx.com temp_dir, 3961761Sdefan@nginx.com ] 3971761Sdefan@nginx.com 3981761Sdefan@nginx.com if option.user: 3991761Sdefan@nginx.com unitd_args.extend(['--user', option.user]) 4001761Sdefan@nginx.com 4011596Szelenkov@nginx.com with open(temp_dir + '/unit.log', 'w') as log: 4021761Sdefan@nginx.com unit_instance['process'] = subprocess.Popen(unitd_args, stderr=log) 4031596Szelenkov@nginx.com 4041934Smax.romanov@nginx.com Log.temp_dir = temp_dir 4051934Smax.romanov@nginx.com 4061596Szelenkov@nginx.com if not waitforfiles(temp_dir + '/control.unit.sock'): 4071596Szelenkov@nginx.com _print_log() 4081596Szelenkov@nginx.com exit('Could not start unit') 4091596Szelenkov@nginx.com 4101596Szelenkov@nginx.com unit_instance['temp_dir'] = temp_dir 4111596Szelenkov@nginx.com unit_instance['control_sock'] = temp_dir + '/control.unit.sock' 4121596Szelenkov@nginx.com unit_instance['unitd'] = unitd 4131596Szelenkov@nginx.com 4141850Smax.romanov@nginx.com option.temp_dir = temp_dir 4151850Smax.romanov@nginx.com 4161844Szelenkov@nginx.com with open(temp_dir + '/unit.pid', 'r') as f: 4171844Szelenkov@nginx.com unit_instance['pid'] = f.read().rstrip() 4181844Szelenkov@nginx.com 419*1986Szelenkov@nginx.com if state_dir is None: 420*1986Szelenkov@nginx.com _clear_conf(unit_instance['temp_dir'] + '/control.unit.sock') 4211844Szelenkov@nginx.com 4221914Szelenkov@nginx.com _fds_info['main']['fds'] = _count_fds(unit_instance['pid']) 4231844Szelenkov@nginx.com 4241914Szelenkov@nginx.com router = _fds_info['router'] 4251844Szelenkov@nginx.com router['pid'] = pid_by_name(router['name']) 4261844Szelenkov@nginx.com router['fds'] = _count_fds(router['pid']) 4271844Szelenkov@nginx.com 4281914Szelenkov@nginx.com controller = _fds_info['controller'] 4291844Szelenkov@nginx.com controller['pid'] = pid_by_name(controller['name']) 4301844Szelenkov@nginx.com controller['fds'] = _count_fds(controller['pid']) 4311844Szelenkov@nginx.com 4321596Szelenkov@nginx.com return unit_instance 4331596Szelenkov@nginx.com 4341596Szelenkov@nginx.com 4351596Szelenkov@nginx.comdef unit_stop(): 4361803Szelenkov@nginx.com if not option.restart: 4371803Szelenkov@nginx.com if inspect.stack()[1].function.startswith('test_'): 4381803Szelenkov@nginx.com pytest.skip('no restart mode') 4391803Szelenkov@nginx.com 4401803Szelenkov@nginx.com return 4411803Szelenkov@nginx.com 4421596Szelenkov@nginx.com p = unit_instance['process'] 4431596Szelenkov@nginx.com 4441596Szelenkov@nginx.com if p.poll() is not None: 4451596Szelenkov@nginx.com return 4461596Szelenkov@nginx.com 4471596Szelenkov@nginx.com p.send_signal(signal.SIGQUIT) 4481596Szelenkov@nginx.com 4491596Szelenkov@nginx.com try: 4501596Szelenkov@nginx.com retcode = p.wait(15) 4511596Szelenkov@nginx.com if retcode: 4521596Szelenkov@nginx.com return 'Child process terminated with code ' + str(retcode) 4531706Smax.romanov@nginx.com 4541706Smax.romanov@nginx.com except KeyboardInterrupt: 4551706Smax.romanov@nginx.com p.kill() 4561706Smax.romanov@nginx.com raise 4571706Smax.romanov@nginx.com 4581596Szelenkov@nginx.com except: 4591596Szelenkov@nginx.com p.kill() 4601596Szelenkov@nginx.com return 'Could not terminate unit' 4611596Szelenkov@nginx.com 4621596Szelenkov@nginx.com 4631914Szelenkov@nginx.com@print_log_on_assert 4641914Szelenkov@nginx.comdef _check_alerts(*, log=None): 4651803Szelenkov@nginx.com if log is None: 4661850Smax.romanov@nginx.com with Log.open(encoding='utf-8') as f: 4671803Szelenkov@nginx.com log = f.read() 4681654Szelenkov@nginx.com 4691596Szelenkov@nginx.com found = False 4701596Szelenkov@nginx.com 4711596Szelenkov@nginx.com alerts = re.findall(r'.+\[alert\].+', log) 4721596Szelenkov@nginx.com 4731596Szelenkov@nginx.com if alerts: 4741736Szelenkov@nginx.com print('\nAll alerts/sanitizer errors found in log:') 4751596Szelenkov@nginx.com [print(alert) for alert in alerts] 4761596Szelenkov@nginx.com found = True 4771596Szelenkov@nginx.com 4781596Szelenkov@nginx.com if option.skip_alerts: 4791596Szelenkov@nginx.com for skip in option.skip_alerts: 4801596Szelenkov@nginx.com alerts = [al for al in alerts if re.search(skip, al) is None] 4811596Szelenkov@nginx.com 4821914Szelenkov@nginx.com assert not alerts, 'alert(s)' 4831596Szelenkov@nginx.com 4841596Szelenkov@nginx.com if not option.skip_sanitizer: 4851596Szelenkov@nginx.com sanitizer_errors = re.findall('.+Sanitizer.+', log) 4861596Szelenkov@nginx.com 4871914Szelenkov@nginx.com assert not sanitizer_errors, 'sanitizer error(s)' 4881596Szelenkov@nginx.com 4891596Szelenkov@nginx.com if found: 4901596Szelenkov@nginx.com print('skipped.') 4911596Szelenkov@nginx.com 4921596Szelenkov@nginx.com 4931934Smax.romanov@nginx.comdef _print_log(log=None): 4941850Smax.romanov@nginx.com path = Log.get_path() 4951596Szelenkov@nginx.com 4961621Szelenkov@nginx.com print('Path to unit.log:\n' + path + '\n') 4971596Szelenkov@nginx.com 4981596Szelenkov@nginx.com if option.print_log: 4991596Szelenkov@nginx.com os.set_blocking(sys.stdout.fileno(), True) 5001596Szelenkov@nginx.com sys.stdout.flush() 5011596Szelenkov@nginx.com 5021914Szelenkov@nginx.com if log is None: 5031621Szelenkov@nginx.com with open(path, 'r', encoding='utf-8', errors='ignore') as f: 5041596Szelenkov@nginx.com shutil.copyfileobj(f, sys.stdout) 5051596Szelenkov@nginx.com else: 5061914Szelenkov@nginx.com sys.stdout.write(log) 5071596Szelenkov@nginx.com 5081596Szelenkov@nginx.com 5091914Szelenkov@nginx.com@print_log_on_assert 5101914Szelenkov@nginx.comdef _clear_conf(sock, *, log=None): 5111805Szelenkov@nginx.com resp = http.put( 5121803Szelenkov@nginx.com url='/config', 5131803Szelenkov@nginx.com sock_type='unix', 5141803Szelenkov@nginx.com addr=sock, 5151803Szelenkov@nginx.com body=json.dumps({"listeners": {}, "applications": {}}), 5161803Szelenkov@nginx.com )['body'] 5171803Szelenkov@nginx.com 5181914Szelenkov@nginx.com assert 'success' in resp, 'clear conf' 5191805Szelenkov@nginx.com 5201808Szelenkov@nginx.com if 'openssl' not in option.available['modules']: 5211808Szelenkov@nginx.com return 5221808Szelenkov@nginx.com 5231805Szelenkov@nginx.com try: 5241848Szelenkov@nginx.com certs = json.loads( 5251848Szelenkov@nginx.com http.get(url='/certificates', sock_type='unix', addr=sock,)['body'] 5261848Szelenkov@nginx.com ).keys() 5271805Szelenkov@nginx.com 5281805Szelenkov@nginx.com except json.JSONDecodeError: 5291805Szelenkov@nginx.com pytest.fail('Can\'t parse certificates list.') 5301805Szelenkov@nginx.com 5311805Szelenkov@nginx.com for cert in certs: 5321805Szelenkov@nginx.com resp = http.delete( 5331848Szelenkov@nginx.com url='/certificates/' + cert, sock_type='unix', addr=sock, 5341805Szelenkov@nginx.com )['body'] 5351805Szelenkov@nginx.com 5361914Szelenkov@nginx.com assert 'success' in resp, 'remove certificate' 5371914Szelenkov@nginx.com 5381914Szelenkov@nginx.com 5391914Szelenkov@nginx.com@print_log_on_assert 5401914Szelenkov@nginx.comdef _check_fds(*, log=None): 5411914Szelenkov@nginx.com def waitforfds(diff): 5421914Szelenkov@nginx.com for i in range(600): 5431914Szelenkov@nginx.com fds_diff = diff() 5441914Szelenkov@nginx.com 5451914Szelenkov@nginx.com if fds_diff <= option.fds_threshold: 5461914Szelenkov@nginx.com break 5471914Szelenkov@nginx.com 5481914Szelenkov@nginx.com time.sleep(0.1) 5491914Szelenkov@nginx.com 5501914Szelenkov@nginx.com return fds_diff 5511914Szelenkov@nginx.com 5521914Szelenkov@nginx.com ps = _fds_info['main'] 5531914Szelenkov@nginx.com if not ps['skip']: 5541914Szelenkov@nginx.com fds_diff = waitforfds( 5551914Szelenkov@nginx.com lambda: _count_fds(unit_instance['pid']) - ps['fds'] 5561914Szelenkov@nginx.com ) 5571914Szelenkov@nginx.com ps['fds'] += fds_diff 5581914Szelenkov@nginx.com 5591914Szelenkov@nginx.com assert ( 5601914Szelenkov@nginx.com fds_diff <= option.fds_threshold 5611914Szelenkov@nginx.com ), 'descriptors leak main process' 5621914Szelenkov@nginx.com 5631914Szelenkov@nginx.com else: 5641914Szelenkov@nginx.com ps['fds'] = _count_fds(unit_instance['pid']) 5651914Szelenkov@nginx.com 5661914Szelenkov@nginx.com for name in ['controller', 'router']: 5671914Szelenkov@nginx.com ps = _fds_info[name] 5681914Szelenkov@nginx.com ps_pid = ps['pid'] 5691914Szelenkov@nginx.com ps['pid'] = pid_by_name(ps['name']) 5701914Szelenkov@nginx.com 5711914Szelenkov@nginx.com if not ps['skip']: 5721914Szelenkov@nginx.com fds_diff = waitforfds(lambda: _count_fds(ps['pid']) - ps['fds']) 5731914Szelenkov@nginx.com ps['fds'] += fds_diff 5741914Szelenkov@nginx.com 5751914Szelenkov@nginx.com if not option.restart: 5761914Szelenkov@nginx.com assert ps['pid'] == ps_pid, 'same pid %s' % name 5771914Szelenkov@nginx.com 5781914Szelenkov@nginx.com assert fds_diff <= option.fds_threshold, ( 5791914Szelenkov@nginx.com 'descriptors leak %s' % name 5801914Szelenkov@nginx.com ) 5811914Szelenkov@nginx.com 5821914Szelenkov@nginx.com else: 5831914Szelenkov@nginx.com ps['fds'] = _count_fds(ps['pid']) 5841803Szelenkov@nginx.com 5851848Szelenkov@nginx.com 5861844Szelenkov@nginx.comdef _count_fds(pid): 5871844Szelenkov@nginx.com procfile = '/proc/%s/fd' % pid 5881844Szelenkov@nginx.com if os.path.isdir(procfile): 5891844Szelenkov@nginx.com return len(os.listdir(procfile)) 5901844Szelenkov@nginx.com 5911844Szelenkov@nginx.com try: 5921844Szelenkov@nginx.com out = subprocess.check_output( 5931844Szelenkov@nginx.com ['procstat', '-f', pid], stderr=subprocess.STDOUT, 5941844Szelenkov@nginx.com ).decode() 5951844Szelenkov@nginx.com return len(out.splitlines()) 5961844Szelenkov@nginx.com 5971868Szelenkov@nginx.com except (FileNotFoundError, TypeError, subprocess.CalledProcessError): 5981844Szelenkov@nginx.com pass 5991844Szelenkov@nginx.com 6001844Szelenkov@nginx.com try: 6011844Szelenkov@nginx.com out = subprocess.check_output( 6021844Szelenkov@nginx.com ['lsof', '-n', '-p', pid], stderr=subprocess.STDOUT, 6031844Szelenkov@nginx.com ).decode() 6041844Szelenkov@nginx.com return len(out.splitlines()) 6051844Szelenkov@nginx.com 6061868Szelenkov@nginx.com except (FileNotFoundError, TypeError, subprocess.CalledProcessError): 6071844Szelenkov@nginx.com pass 6081844Szelenkov@nginx.com 6091844Szelenkov@nginx.com return 0 6101844Szelenkov@nginx.com 6111844Szelenkov@nginx.com 6121654Szelenkov@nginx.comdef run_process(target, *args): 6131654Szelenkov@nginx.com global _processes 6141654Szelenkov@nginx.com 6151654Szelenkov@nginx.com process = Process(target=target, args=args) 6161654Szelenkov@nginx.com process.start() 6171654Szelenkov@nginx.com 6181654Szelenkov@nginx.com _processes.append(process) 6191654Szelenkov@nginx.com 6201848Szelenkov@nginx.com 6211654Szelenkov@nginx.comdef stop_processes(): 6221654Szelenkov@nginx.com if not _processes: 6231654Szelenkov@nginx.com return 6241654Szelenkov@nginx.com 6251654Szelenkov@nginx.com fail = False 6261654Szelenkov@nginx.com for process in _processes: 6271654Szelenkov@nginx.com if process.is_alive(): 6281654Szelenkov@nginx.com process.terminate() 6291654Szelenkov@nginx.com process.join(timeout=15) 6301654Szelenkov@nginx.com 6311654Szelenkov@nginx.com if process.is_alive(): 6321654Szelenkov@nginx.com fail = True 6331654Szelenkov@nginx.com 6341654Szelenkov@nginx.com if fail: 6351654Szelenkov@nginx.com return 'Fail to stop process(es)' 6361654Szelenkov@nginx.com 6371654Szelenkov@nginx.com 6381844Szelenkov@nginx.comdef pid_by_name(name): 6391844Szelenkov@nginx.com output = subprocess.check_output(['ps', 'ax', '-O', 'ppid']).decode() 6401844Szelenkov@nginx.com m = re.search( 6411844Szelenkov@nginx.com r'\s*(\d+)\s*' + str(unit_instance['pid']) + r'.*' + name, output 6421844Szelenkov@nginx.com ) 6431844Szelenkov@nginx.com return None if m is None else m.group(1) 6441844Szelenkov@nginx.com 6451844Szelenkov@nginx.com 6461844Szelenkov@nginx.comdef find_proc(name, ps_output): 6471844Szelenkov@nginx.com return re.findall(str(unit_instance['pid']) + r'.*' + name, ps_output) 6481844Szelenkov@nginx.com 6491844Szelenkov@nginx.com 6501736Szelenkov@nginx.com@pytest.fixture() 6511736Szelenkov@nginx.comdef skip_alert(): 6521736Szelenkov@nginx.com def _skip(*alerts): 6531736Szelenkov@nginx.com option.skip_alerts.extend(alerts) 6541736Szelenkov@nginx.com 6551736Szelenkov@nginx.com return _skip 6561736Szelenkov@nginx.com 6571736Szelenkov@nginx.com 6581844Szelenkov@nginx.com@pytest.fixture() 6591844Szelenkov@nginx.comdef skip_fds_check(): 6601844Szelenkov@nginx.com def _skip(main=False, router=False, controller=False): 6611914Szelenkov@nginx.com _fds_info['main']['skip'] = main 6621914Szelenkov@nginx.com _fds_info['router']['skip'] = router 6631914Szelenkov@nginx.com _fds_info['controller']['skip'] = controller 6641844Szelenkov@nginx.com 6651844Szelenkov@nginx.com return _skip 6661844Szelenkov@nginx.com 6671844Szelenkov@nginx.com 6681654Szelenkov@nginx.com@pytest.fixture 6691654Szelenkov@nginx.comdef temp_dir(request): 6701654Szelenkov@nginx.com return unit_instance['temp_dir'] 6711654Szelenkov@nginx.com 6721848Szelenkov@nginx.com 6731596Szelenkov@nginx.com@pytest.fixture 6741596Szelenkov@nginx.comdef is_unsafe(request): 6751596Szelenkov@nginx.com return request.config.getoption("--unsafe") 6761596Szelenkov@nginx.com 6771848Szelenkov@nginx.com 6781596Szelenkov@nginx.com@pytest.fixture 6791596Szelenkov@nginx.comdef is_su(request): 6801596Szelenkov@nginx.com return os.geteuid() == 0 6811596Szelenkov@nginx.com 6821848Szelenkov@nginx.com 6831769St.nateldemoura@f5.com@pytest.fixture 6841769St.nateldemoura@f5.comdef unit_pid(request): 6851769St.nateldemoura@f5.com return unit_instance['process'].pid 6861769St.nateldemoura@f5.com 6871848Szelenkov@nginx.com 6881596Szelenkov@nginx.comdef pytest_sessionfinish(session): 6891803Szelenkov@nginx.com if not option.restart and option.save_log: 6901850Smax.romanov@nginx.com print('Path to unit.log:\n' + Log.get_path() + '\n') 6911803Szelenkov@nginx.com 6921803Szelenkov@nginx.com option.restart = True 6931803Szelenkov@nginx.com 6941596Szelenkov@nginx.com unit_stop() 6951864Szelenkov@nginx.com 6961864Szelenkov@nginx.com public_dir(option.cache_dir) 6971757St.nateldemoura@f5.com shutil.rmtree(option.cache_dir) 6981864Szelenkov@nginx.com 6991868Szelenkov@nginx.com if not option.save_log and os.path.isdir(option.temp_dir): 7001864Szelenkov@nginx.com public_dir(option.temp_dir) 7011864Szelenkov@nginx.com shutil.rmtree(option.temp_dir) 702