11596Szelenkov@nginx.comimport fcntl 21803Szelenkov@nginx.comimport inspect 31803Szelenkov@nginx.comimport json 41596Szelenkov@nginx.comimport os 51635Szelenkov@nginx.comimport re 61621Szelenkov@nginx.comimport shutil 71596Szelenkov@nginx.comimport signal 81596Szelenkov@nginx.comimport stat 91596Szelenkov@nginx.comimport subprocess 101596Szelenkov@nginx.comimport sys 111596Szelenkov@nginx.comimport tempfile 121596Szelenkov@nginx.comimport time 131654Szelenkov@nginx.comfrom multiprocessing import Process 14*2616Szelenkov@nginx.comfrom pathlib import Path 151596Szelenkov@nginx.com 161635Szelenkov@nginx.comimport pytest 17*2616Szelenkov@nginx.com 18*2616Szelenkov@nginx.comfrom unit.check.check_prerequisites import check_prerequisites 192488Szelenkov@nginx.comfrom unit.check.discover_available import discover_available 202491Szelenkov@nginx.comfrom unit.http import HTTP1 211902Szelenkov@nginx.comfrom unit.log import Log 222481Szelenkov@nginx.comfrom unit.log import print_log_on_assert 231730Szelenkov@nginx.comfrom unit.option import option 242187Szelenkov@nginx.comfrom unit.status import Status 252264Szelenkov@nginx.comfrom unit.utils import check_findmnt 261735Szelenkov@nginx.comfrom unit.utils import public_dir 271735Szelenkov@nginx.comfrom unit.utils import waitforfiles 282264Szelenkov@nginx.comfrom unit.utils import waitforunmount 291621Szelenkov@nginx.com 301596Szelenkov@nginx.com 311596Szelenkov@nginx.comdef pytest_addoption(parser): 321596Szelenkov@nginx.com parser.addoption( 331596Szelenkov@nginx.com "--detailed", 341596Szelenkov@nginx.com default=False, 351596Szelenkov@nginx.com action="store_true", 361596Szelenkov@nginx.com help="Detailed output for tests", 371596Szelenkov@nginx.com ) 381596Szelenkov@nginx.com parser.addoption( 391743Szelenkov@nginx.com "--print-log", 401596Szelenkov@nginx.com default=False, 411596Szelenkov@nginx.com action="store_true", 421596Szelenkov@nginx.com help="Print unit.log to stdout in case of errors", 431596Szelenkov@nginx.com ) 441596Szelenkov@nginx.com parser.addoption( 451743Szelenkov@nginx.com "--save-log", 461596Szelenkov@nginx.com default=False, 471596Szelenkov@nginx.com action="store_true", 481596Szelenkov@nginx.com help="Save unit.log after the test execution", 491596Szelenkov@nginx.com ) 501596Szelenkov@nginx.com parser.addoption( 511596Szelenkov@nginx.com "--unsafe", 521596Szelenkov@nginx.com default=False, 531596Szelenkov@nginx.com action="store_true", 541596Szelenkov@nginx.com help="Run unsafe tests", 551596Szelenkov@nginx.com ) 561761Sdefan@nginx.com parser.addoption( 571761Sdefan@nginx.com "--user", 581761Sdefan@nginx.com type=str, 591761Sdefan@nginx.com help="Default user for non-privileged processes of unitd", 601761Sdefan@nginx.com ) 611803Szelenkov@nginx.com parser.addoption( 621844Szelenkov@nginx.com "--fds-threshold", 631844Szelenkov@nginx.com type=int, 641844Szelenkov@nginx.com default=0, 651844Szelenkov@nginx.com help="File descriptors threshold", 661844Szelenkov@nginx.com ) 671844Szelenkov@nginx.com parser.addoption( 681803Szelenkov@nginx.com "--restart", 691803Szelenkov@nginx.com default=False, 701803Szelenkov@nginx.com action="store_true", 711803Szelenkov@nginx.com help="Force Unit to restart after every test", 721803Szelenkov@nginx.com ) 731596Szelenkov@nginx.com 741596Szelenkov@nginx.com 751596Szelenkov@nginx.comunit_instance = {} 761654Szelenkov@nginx.com_processes = [] 771914Szelenkov@nginx.com_fds_info = { 781844Szelenkov@nginx.com 'main': {'fds': 0, 'skip': False}, 791844Szelenkov@nginx.com 'router': {'name': 'unit: router', 'pid': -1, 'fds': 0, 'skip': False}, 801844Szelenkov@nginx.com 'controller': { 811844Szelenkov@nginx.com 'name': 'unit: controller', 821844Szelenkov@nginx.com 'pid': -1, 831844Szelenkov@nginx.com 'fds': 0, 841844Szelenkov@nginx.com 'skip': False, 851844Szelenkov@nginx.com }, 861844Szelenkov@nginx.com} 872491Szelenkov@nginx.comhttp = HTTP1() 882264Szelenkov@nginx.comis_findmnt = check_findmnt() 891596Szelenkov@nginx.com 901848Szelenkov@nginx.com 911596Szelenkov@nginx.comdef pytest_configure(config): 921730Szelenkov@nginx.com option.config = config.option 931730Szelenkov@nginx.com 941730Szelenkov@nginx.com option.detailed = config.option.detailed 951844Szelenkov@nginx.com option.fds_threshold = config.option.fds_threshold 961730Szelenkov@nginx.com option.print_log = config.option.print_log 971730Szelenkov@nginx.com option.save_log = config.option.save_log 981730Szelenkov@nginx.com option.unsafe = config.option.unsafe 991761Sdefan@nginx.com option.user = config.option.user 1001803Szelenkov@nginx.com option.restart = config.option.restart 1011596Szelenkov@nginx.com 1021596Szelenkov@nginx.com option.generated_tests = {} 1031596Szelenkov@nginx.com option.current_dir = os.path.abspath( 1041596Szelenkov@nginx.com os.path.join(os.path.dirname(__file__), os.pardir) 1051596Szelenkov@nginx.com ) 1062330Szelenkov@nginx.com option.test_dir = f'{option.current_dir}/test' 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 1171596Szelenkov@nginx.comdef pytest_generate_tests(metafunc): 1182491Szelenkov@nginx.com module = metafunc.module 1191848Szelenkov@nginx.com if ( 1202491Szelenkov@nginx.com not hasattr(module, 'client') 1212491Szelenkov@nginx.com or not hasattr(module.client, 'application_type') 1222491Szelenkov@nginx.com or module.client.application_type is None 1232491Szelenkov@nginx.com or module.client.application_type == 'external' 1241848Szelenkov@nginx.com ): 1251596Szelenkov@nginx.com return 1261596Szelenkov@nginx.com 1272491Szelenkov@nginx.com app_type = module.client.application_type 1281596Szelenkov@nginx.com 1291611Smax.romanov@nginx.com def generate_tests(versions): 1302488Szelenkov@nginx.com if not versions: 1312488Szelenkov@nginx.com pytest.skip('no available module versions') 1322488Szelenkov@nginx.com 1331611Smax.romanov@nginx.com metafunc.fixturenames.append('tmp_ct') 1341630Smax.romanov@nginx.com metafunc.parametrize('tmp_ct', versions) 1351611Smax.romanov@nginx.com 1361630Smax.romanov@nginx.com for version in versions: 1371611Smax.romanov@nginx.com option.generated_tests[ 1382330Szelenkov@nginx.com f'{metafunc.function.__name__} [{version}]' 1392491Szelenkov@nginx.com ] = f'{app_type} {version}' 1401611Smax.romanov@nginx.com 1411596Szelenkov@nginx.com # take available module from option and generate tests for each version 1421596Szelenkov@nginx.com 1432488Szelenkov@nginx.com available_modules = option.available['modules'] 1441596Szelenkov@nginx.com 1452488Szelenkov@nginx.com for module, version in metafunc.module.prerequisites['modules'].items(): 1462488Szelenkov@nginx.com if module in available_modules and available_modules[module]: 1472488Szelenkov@nginx.com available_versions = available_modules[module] 1482488Szelenkov@nginx.com 1492488Szelenkov@nginx.com if version == 'all': 1501611Smax.romanov@nginx.com generate_tests(available_versions) 1511596Szelenkov@nginx.com 1522488Szelenkov@nginx.com elif version == 'any': 1532330Szelenkov@nginx.com option.generated_tests[ 1542330Szelenkov@nginx.com metafunc.function.__name__ 1552491Szelenkov@nginx.com ] = f'{app_type} {available_versions[0]}' 1562488Szelenkov@nginx.com elif callable(version): 1572488Szelenkov@nginx.com generate_tests(list(filter(version, available_versions))) 1581611Smax.romanov@nginx.com 1591596Szelenkov@nginx.com else: 1601611Smax.romanov@nginx.com raise ValueError( 1612330Szelenkov@nginx.com f''' 1622488Szelenkov@nginx.comUnexpected prerequisite version "{version}" for module "{module}". 1632488Szelenkov@nginx.com'all', 'any' or callable expected.''' 1641611Smax.romanov@nginx.com ) 1651596Szelenkov@nginx.com 1661596Szelenkov@nginx.com 1672477Szelenkov@nginx.comdef pytest_sessionstart(): 1681596Szelenkov@nginx.com unit = unit_run() 1691596Szelenkov@nginx.com 1702488Szelenkov@nginx.com discover_available(unit) 1711621Szelenkov@nginx.com 1722488Szelenkov@nginx.com _clear_conf() 1731803Szelenkov@nginx.com 1741596Szelenkov@nginx.com unit_stop() 1751596Szelenkov@nginx.com 1762481Szelenkov@nginx.com Log.check_alerts() 1771730Szelenkov@nginx.com 1781803Szelenkov@nginx.com if option.restart: 1792488Szelenkov@nginx.com shutil.rmtree(unit['temp_dir']) 1802370Szelenkov@nginx.com else: 1812370Szelenkov@nginx.com _clear_temp_dir() 1821596Szelenkov@nginx.com 1831848Szelenkov@nginx.com 1841654Szelenkov@nginx.com@pytest.hookimpl(tryfirst=True, hookwrapper=True) 1852477Szelenkov@nginx.comdef pytest_runtest_makereport(item): 1861654Szelenkov@nginx.com # execute all other hooks to obtain the report object 1871654Szelenkov@nginx.com outcome = yield 1881654Szelenkov@nginx.com rep = outcome.get_result() 1891654Szelenkov@nginx.com 1901654Szelenkov@nginx.com # set a report attribute for each phase of a call, which can 1911654Szelenkov@nginx.com # be "setup", "call", "teardown" 1921654Szelenkov@nginx.com 1932330Szelenkov@nginx.com setattr(item, f'rep_{rep.when}', rep) 1941654Szelenkov@nginx.com 1951654Szelenkov@nginx.com 1962488Szelenkov@nginx.com@pytest.fixture(scope='module', autouse=True) 1972488Szelenkov@nginx.comdef check_prerequisites_module(request): 1982488Szelenkov@nginx.com if hasattr(request.module, 'prerequisites'): 1992488Szelenkov@nginx.com check_prerequisites(request.module.prerequisites) 2001741Szelenkov@nginx.com 2011741Szelenkov@nginx.com 2021654Szelenkov@nginx.com@pytest.fixture(autouse=True) 2031654Szelenkov@nginx.comdef run(request): 2041654Szelenkov@nginx.com unit = unit_run() 2051654Szelenkov@nginx.com 2061596Szelenkov@nginx.com option.skip_alerts = [ 2071596Szelenkov@nginx.com r'read signalfd\(4\) failed', 2081596Szelenkov@nginx.com r'sendmsg.+failed', 2091596Szelenkov@nginx.com r'recvmsg.+failed', 2101596Szelenkov@nginx.com ] 2111596Szelenkov@nginx.com option.skip_sanitizer = False 2121596Szelenkov@nginx.com 2131914Szelenkov@nginx.com _fds_info['main']['skip'] = False 2141914Szelenkov@nginx.com _fds_info['router']['skip'] = False 2151914Szelenkov@nginx.com _fds_info['controller']['skip'] = False 2161844Szelenkov@nginx.com 2171654Szelenkov@nginx.com yield 2181654Szelenkov@nginx.com 2191654Szelenkov@nginx.com # stop unit 2201654Szelenkov@nginx.com 2211803Szelenkov@nginx.com error_stop_unit = unit_stop() 2221803Szelenkov@nginx.com error_stop_processes = stop_processes() 2231803Szelenkov@nginx.com 2241803Szelenkov@nginx.com # prepare log 2251803Szelenkov@nginx.com 2262488Szelenkov@nginx.com with Log.open() as f: 2271803Szelenkov@nginx.com log = f.read() 2281850Smax.romanov@nginx.com Log.set_pos(f.tell()) 2291654Szelenkov@nginx.com 2301803Szelenkov@nginx.com if not option.save_log and option.restart: 2311803Szelenkov@nginx.com shutil.rmtree(unit['temp_dir']) 2321850Smax.romanov@nginx.com Log.set_pos(0) 2331803Szelenkov@nginx.com 2341803Szelenkov@nginx.com # clean temp_dir before the next test 2351654Szelenkov@nginx.com 2361803Szelenkov@nginx.com if not option.restart: 2372488Szelenkov@nginx.com _clear_conf(log=log) 2382370Szelenkov@nginx.com _clear_temp_dir() 2391654Szelenkov@nginx.com 2401914Szelenkov@nginx.com # check descriptors 2411844Szelenkov@nginx.com 2421914Szelenkov@nginx.com _check_fds(log=log) 2431844Szelenkov@nginx.com 2442074Szelenkov@nginx.com # check processes id's and amount 2452074Szelenkov@nginx.com 2462074Szelenkov@nginx.com _check_processes() 2472074Szelenkov@nginx.com 2481654Szelenkov@nginx.com # print unit.log in case of error 2491654Szelenkov@nginx.com 2501706Smax.romanov@nginx.com if hasattr(request.node, 'rep_call') and request.node.rep_call.failed: 2512481Szelenkov@nginx.com Log.print_log(log) 2521803Szelenkov@nginx.com 2531803Szelenkov@nginx.com if error_stop_unit or error_stop_processes: 2542481Szelenkov@nginx.com Log.print_log(log) 2551654Szelenkov@nginx.com 2561803Szelenkov@nginx.com # check unit.log for errors 2571654Szelenkov@nginx.com 2581803Szelenkov@nginx.com assert error_stop_unit is None, 'stop unit' 2591803Szelenkov@nginx.com assert error_stop_processes is None, 'stop processes' 2601803Szelenkov@nginx.com 2612481Szelenkov@nginx.com Log.check_alerts(log=log) 2621654Szelenkov@nginx.com 2631848Szelenkov@nginx.com 2641986Szelenkov@nginx.comdef unit_run(state_dir=None): 2651596Szelenkov@nginx.com global unit_instance 2661803Szelenkov@nginx.com 2671803Szelenkov@nginx.com if not option.restart and 'unitd' in unit_instance: 2681803Szelenkov@nginx.com return unit_instance 2691803Szelenkov@nginx.com 270*2616Szelenkov@nginx.com builddir = f'{option.current_dir}/build' 271*2616Szelenkov@nginx.com libdir = f'{builddir}/lib' 2722397Salx@nginx.com modulesdir = f'{libdir}/unit/modules' 273*2616Szelenkov@nginx.com sbindir = f'{builddir}/sbin' 274*2616Szelenkov@nginx.com unitd = f'{sbindir}/unitd' 2751596Szelenkov@nginx.com 276*2616Szelenkov@nginx.com if not Path(unitd).is_file(): 277*2616Szelenkov@nginx.com sys.exit('Could not find unit') 2781596Szelenkov@nginx.com 279*2616Szelenkov@nginx.com temporary_dir = tempfile.mkdtemp(prefix='unit-test-') 280*2616Szelenkov@nginx.com option.temp_dir = temporary_dir 281*2616Szelenkov@nginx.com public_dir(temporary_dir) 2821596Szelenkov@nginx.com 283*2616Szelenkov@nginx.com if oct(stat.S_IMODE(Path(builddir).stat().st_mode)) != '0o777': 2842397Salx@nginx.com public_dir(builddir) 2851596Szelenkov@nginx.com 286*2616Szelenkov@nginx.com statedir = f'{temporary_dir}/state' if state_dir is None else state_dir 287*2616Szelenkov@nginx.com Path(statedir).mkdir(exist_ok=True) 2881596Szelenkov@nginx.com 289*2616Szelenkov@nginx.com control_sock = f'{temporary_dir}/control.unit.sock' 2902330Szelenkov@nginx.com 2911761Sdefan@nginx.com unitd_args = [ 2921761Sdefan@nginx.com unitd, 2931761Sdefan@nginx.com '--no-daemon', 2942314Salx.manpages@gmail.com '--modulesdir', 2952397Salx@nginx.com modulesdir, 2962396Salx@nginx.com '--statedir', 2972397Salx@nginx.com statedir, 2981761Sdefan@nginx.com '--pid', 299*2616Szelenkov@nginx.com f'{temporary_dir}/unit.pid', 3001761Sdefan@nginx.com '--log', 301*2616Szelenkov@nginx.com f'{temporary_dir}/unit.log', 3021761Sdefan@nginx.com '--control', 303*2616Szelenkov@nginx.com f'unix:{temporary_dir}/control.unit.sock', 3042314Salx.manpages@gmail.com '--tmpdir', 305*2616Szelenkov@nginx.com temporary_dir, 3061761Sdefan@nginx.com ] 3071761Sdefan@nginx.com 3081761Sdefan@nginx.com if option.user: 3091761Sdefan@nginx.com unitd_args.extend(['--user', option.user]) 3101761Sdefan@nginx.com 311*2616Szelenkov@nginx.com with open(f'{temporary_dir}/unit.log', 'w', encoding='utf-8') as log: 3121761Sdefan@nginx.com unit_instance['process'] = subprocess.Popen(unitd_args, stderr=log) 3131596Szelenkov@nginx.com 3142330Szelenkov@nginx.com if not waitforfiles(control_sock): 3152481Szelenkov@nginx.com Log.print_log() 316*2616Szelenkov@nginx.com sys.exit('Could not start unit') 3171596Szelenkov@nginx.com 318*2616Szelenkov@nginx.com unit_instance['temp_dir'] = temporary_dir 3192330Szelenkov@nginx.com unit_instance['control_sock'] = control_sock 3201596Szelenkov@nginx.com unit_instance['unitd'] = unitd 3211596Szelenkov@nginx.com 322*2616Szelenkov@nginx.com unit_instance['pid'] = ( 323*2616Szelenkov@nginx.com Path(f'{temporary_dir}/unit.pid').read_text(encoding='utf-8').rstrip() 324*2616Szelenkov@nginx.com ) 3251844Szelenkov@nginx.com 3261986Szelenkov@nginx.com if state_dir is None: 3272488Szelenkov@nginx.com _clear_conf() 3281844Szelenkov@nginx.com 3291914Szelenkov@nginx.com _fds_info['main']['fds'] = _count_fds(unit_instance['pid']) 3301844Szelenkov@nginx.com 3311914Szelenkov@nginx.com router = _fds_info['router'] 3321844Szelenkov@nginx.com router['pid'] = pid_by_name(router['name']) 3331844Szelenkov@nginx.com router['fds'] = _count_fds(router['pid']) 3341844Szelenkov@nginx.com 3351914Szelenkov@nginx.com controller = _fds_info['controller'] 3361844Szelenkov@nginx.com controller['pid'] = pid_by_name(controller['name']) 3371844Szelenkov@nginx.com controller['fds'] = _count_fds(controller['pid']) 3381844Szelenkov@nginx.com 3392187Szelenkov@nginx.com Status._check_zeros() 3402187Szelenkov@nginx.com 3411596Szelenkov@nginx.com return unit_instance 3421596Szelenkov@nginx.com 3431596Szelenkov@nginx.com 3441596Szelenkov@nginx.comdef unit_stop(): 3451803Szelenkov@nginx.com if not option.restart: 3461803Szelenkov@nginx.com if inspect.stack()[1].function.startswith('test_'): 3471803Szelenkov@nginx.com pytest.skip('no restart mode') 3481803Szelenkov@nginx.com 3491803Szelenkov@nginx.com return 3501803Szelenkov@nginx.com 3512074Szelenkov@nginx.com # check zombies 3522074Szelenkov@nginx.com 3532074Szelenkov@nginx.com out = subprocess.check_output( 3542074Szelenkov@nginx.com ['ps', 'ax', '-o', 'state', '-o', 'ppid'] 3552074Szelenkov@nginx.com ).decode() 3562074Szelenkov@nginx.com z_ppids = re.findall(r'Z\s*(\d+)', out) 3572074Szelenkov@nginx.com assert unit_instance['pid'] not in z_ppids, 'no zombies' 3582074Szelenkov@nginx.com 3592074Szelenkov@nginx.com # terminate unit 3602074Szelenkov@nginx.com 3611596Szelenkov@nginx.com p = unit_instance['process'] 3621596Szelenkov@nginx.com 3631596Szelenkov@nginx.com if p.poll() is not None: 3641596Szelenkov@nginx.com return 3651596Szelenkov@nginx.com 3661596Szelenkov@nginx.com p.send_signal(signal.SIGQUIT) 3671596Szelenkov@nginx.com 3681596Szelenkov@nginx.com try: 3691596Szelenkov@nginx.com retcode = p.wait(15) 3701596Szelenkov@nginx.com if retcode: 3712330Szelenkov@nginx.com return f'Child process terminated with code {retcode}' 3721706Smax.romanov@nginx.com 3731706Smax.romanov@nginx.com except KeyboardInterrupt: 3741706Smax.romanov@nginx.com p.kill() 3751706Smax.romanov@nginx.com raise 3761706Smax.romanov@nginx.com 3771596Szelenkov@nginx.com except: 3781596Szelenkov@nginx.com p.kill() 3791596Szelenkov@nginx.com return 'Could not terminate unit' 3801596Szelenkov@nginx.com 3811596Szelenkov@nginx.com 3821914Szelenkov@nginx.com@print_log_on_assert 3832488Szelenkov@nginx.comdef _clear_conf(*, log=None): 3842488Szelenkov@nginx.com sock = unit_instance['control_sock'] 3852488Szelenkov@nginx.com 3861805Szelenkov@nginx.com resp = http.put( 3871803Szelenkov@nginx.com url='/config', 3881803Szelenkov@nginx.com sock_type='unix', 3891803Szelenkov@nginx.com addr=sock, 3901803Szelenkov@nginx.com body=json.dumps({"listeners": {}, "applications": {}}), 3911803Szelenkov@nginx.com )['body'] 3921803Szelenkov@nginx.com 3931914Szelenkov@nginx.com assert 'success' in resp, 'clear conf' 3941805Szelenkov@nginx.com 3952459Szelenkov@nginx.com def get(url): 3962459Szelenkov@nginx.com return http.get(url=url, sock_type='unix', addr=sock)['body'] 3972459Szelenkov@nginx.com 3982459Szelenkov@nginx.com def delete(url): 3992459Szelenkov@nginx.com return http.delete(url=url, sock_type='unix', addr=sock)['body'] 4001808Szelenkov@nginx.com 4012488Szelenkov@nginx.com if ( 4022488Szelenkov@nginx.com 'openssl' in option.available['modules'] 4032488Szelenkov@nginx.com and option.available['modules']['openssl'] 4042488Szelenkov@nginx.com ): 4052459Szelenkov@nginx.com try: 4062459Szelenkov@nginx.com certs = json.loads(get('/certificates')).keys() 4072459Szelenkov@nginx.com 4082459Szelenkov@nginx.com except json.JSONDecodeError: 4092459Szelenkov@nginx.com pytest.fail("Can't parse certificates list.") 4101805Szelenkov@nginx.com 4112459Szelenkov@nginx.com for cert in certs: 4122459Szelenkov@nginx.com assert 'success' in delete(f'/certificates/{cert}'), 'delete cert' 4131805Szelenkov@nginx.com 4142488Szelenkov@nginx.com if ( 4152488Szelenkov@nginx.com 'njs' in option.available['modules'] 4162488Szelenkov@nginx.com and option.available['modules']['njs'] 4172488Szelenkov@nginx.com ): 4182459Szelenkov@nginx.com try: 4192459Szelenkov@nginx.com scripts = json.loads(get('/js_modules')).keys() 4201805Szelenkov@nginx.com 4212459Szelenkov@nginx.com except json.JSONDecodeError: 4222459Szelenkov@nginx.com pytest.fail("Can't parse njs modules list.") 4231914Szelenkov@nginx.com 4242459Szelenkov@nginx.com for script in scripts: 4252459Szelenkov@nginx.com assert 'success' in delete(f'/js_modules/{script}'), 'delete script' 4261914Szelenkov@nginx.com 4272482Szelenkov@nginx.com 4282370Szelenkov@nginx.comdef _clear_temp_dir(): 429*2616Szelenkov@nginx.com temporary_dir = unit_instance['temp_dir'] 4302370Szelenkov@nginx.com 431*2616Szelenkov@nginx.com if is_findmnt and not waitforunmount(temporary_dir, timeout=600): 432*2616Szelenkov@nginx.com sys.exit('Could not unmount filesystems in tmpdir ({temporary_dir}).') 4332370Szelenkov@nginx.com 434*2616Szelenkov@nginx.com for item in Path(temporary_dir).iterdir(): 435*2616Szelenkov@nginx.com if item.name not in [ 4362370Szelenkov@nginx.com 'control.unit.sock', 4372370Szelenkov@nginx.com 'state', 4382370Szelenkov@nginx.com 'unit.pid', 4392370Szelenkov@nginx.com 'unit.log', 4402370Szelenkov@nginx.com ]: 441*2616Szelenkov@nginx.com 442*2616Szelenkov@nginx.com public_dir(item) 443*2616Szelenkov@nginx.com 444*2616Szelenkov@nginx.com if item.is_file() or stat.S_ISSOCK(item.stat().st_mode): 445*2616Szelenkov@nginx.com item.unlink() 4462370Szelenkov@nginx.com else: 4472477Szelenkov@nginx.com for _ in range(10): 4482370Szelenkov@nginx.com try: 449*2616Szelenkov@nginx.com shutil.rmtree(item) 4502370Szelenkov@nginx.com break 4512370Szelenkov@nginx.com except OSError as err: 4522501Szelenkov@nginx.com # OSError: [Errno 16] Device or resource busy 4532501Szelenkov@nginx.com # OSError: [Errno 39] Directory not empty 4542501Szelenkov@nginx.com if err.errno not in [16, 39]: 4552370Szelenkov@nginx.com raise 4562370Szelenkov@nginx.com time.sleep(1) 4572370Szelenkov@nginx.com 4582370Szelenkov@nginx.com 4592074Szelenkov@nginx.comdef _check_processes(): 4602074Szelenkov@nginx.com router_pid = _fds_info['router']['pid'] 4612074Szelenkov@nginx.com controller_pid = _fds_info['controller']['pid'] 462*2616Szelenkov@nginx.com main_pid = unit_instance['pid'] 4632074Szelenkov@nginx.com 4642477Szelenkov@nginx.com for _ in range(600): 4652074Szelenkov@nginx.com out = ( 4662074Szelenkov@nginx.com subprocess.check_output( 4672074Szelenkov@nginx.com ['ps', '-ax', '-o', 'pid', '-o', 'ppid', '-o', 'command'] 4682074Szelenkov@nginx.com ) 4692074Szelenkov@nginx.com .decode() 4702074Szelenkov@nginx.com .splitlines() 4712074Szelenkov@nginx.com ) 472*2616Szelenkov@nginx.com out = [l for l in out if main_pid in l] 4732074Szelenkov@nginx.com 4742074Szelenkov@nginx.com if len(out) <= 3: 4752074Szelenkov@nginx.com break 4762074Szelenkov@nginx.com 4772074Szelenkov@nginx.com time.sleep(0.1) 4782074Szelenkov@nginx.com 4792238Szelenkov@nginx.com if option.restart: 4802238Szelenkov@nginx.com assert len(out) == 0, 'all termimated' 4812238Szelenkov@nginx.com return 4822238Szelenkov@nginx.com 4832074Szelenkov@nginx.com assert len(out) == 3, 'main, router, and controller expected' 4842074Szelenkov@nginx.com 4852074Szelenkov@nginx.com out = [l for l in out if 'unit: main' not in l] 4862074Szelenkov@nginx.com assert len(out) == 2, 'one main' 4872074Szelenkov@nginx.com 4882074Szelenkov@nginx.com out = [ 4892074Szelenkov@nginx.com l 4902074Szelenkov@nginx.com for l in out 491*2616Szelenkov@nginx.com if re.search(fr'{router_pid}\s+{main_pid}.*unit: router', l) is None 4922074Szelenkov@nginx.com ] 4932074Szelenkov@nginx.com assert len(out) == 1, 'one router' 4942074Szelenkov@nginx.com 4952074Szelenkov@nginx.com out = [ 4962074Szelenkov@nginx.com l 4972074Szelenkov@nginx.com for l in out 498*2616Szelenkov@nginx.com if re.search(fr'{controller_pid}\s+{main_pid}.*unit: controller', l) 4992074Szelenkov@nginx.com is None 5002074Szelenkov@nginx.com ] 5012074Szelenkov@nginx.com assert len(out) == 0, 'one controller' 5022074Szelenkov@nginx.com 5032074Szelenkov@nginx.com 5041914Szelenkov@nginx.com@print_log_on_assert 5051914Szelenkov@nginx.comdef _check_fds(*, log=None): 5061914Szelenkov@nginx.com def waitforfds(diff): 5072477Szelenkov@nginx.com for _ in range(600): 5081914Szelenkov@nginx.com fds_diff = diff() 5091914Szelenkov@nginx.com 5101914Szelenkov@nginx.com if fds_diff <= option.fds_threshold: 5111914Szelenkov@nginx.com break 5121914Szelenkov@nginx.com 5131914Szelenkov@nginx.com time.sleep(0.1) 5141914Szelenkov@nginx.com 5151914Szelenkov@nginx.com return fds_diff 5161914Szelenkov@nginx.com 5171914Szelenkov@nginx.com ps = _fds_info['main'] 5181914Szelenkov@nginx.com if not ps['skip']: 5191914Szelenkov@nginx.com fds_diff = waitforfds( 5201914Szelenkov@nginx.com lambda: _count_fds(unit_instance['pid']) - ps['fds'] 5211914Szelenkov@nginx.com ) 5221914Szelenkov@nginx.com ps['fds'] += fds_diff 5231914Szelenkov@nginx.com 5242073Szelenkov@nginx.com assert fds_diff <= option.fds_threshold, 'descriptors leak main process' 5251914Szelenkov@nginx.com 5261914Szelenkov@nginx.com else: 5271914Szelenkov@nginx.com ps['fds'] = _count_fds(unit_instance['pid']) 5281914Szelenkov@nginx.com 5291914Szelenkov@nginx.com for name in ['controller', 'router']: 5301914Szelenkov@nginx.com ps = _fds_info[name] 5311914Szelenkov@nginx.com ps_pid = ps['pid'] 5321914Szelenkov@nginx.com ps['pid'] = pid_by_name(ps['name']) 5331914Szelenkov@nginx.com 5341914Szelenkov@nginx.com if not ps['skip']: 5351914Szelenkov@nginx.com fds_diff = waitforfds(lambda: _count_fds(ps['pid']) - ps['fds']) 5361914Szelenkov@nginx.com ps['fds'] += fds_diff 5371914Szelenkov@nginx.com 5381914Szelenkov@nginx.com if not option.restart: 5392330Szelenkov@nginx.com assert ps['pid'] == ps_pid, f'same pid {name}' 5401914Szelenkov@nginx.com 5412330Szelenkov@nginx.com assert fds_diff <= option.fds_threshold, f'descriptors leak {name}' 5421914Szelenkov@nginx.com 5431914Szelenkov@nginx.com else: 5441914Szelenkov@nginx.com ps['fds'] = _count_fds(ps['pid']) 5451803Szelenkov@nginx.com 5461848Szelenkov@nginx.com 5471844Szelenkov@nginx.comdef _count_fds(pid): 548*2616Szelenkov@nginx.com procfile = Path(f'/proc/{pid}/fd') 549*2616Szelenkov@nginx.com if procfile.is_dir(): 550*2616Szelenkov@nginx.com return len(list(procfile.iterdir())) 5511844Szelenkov@nginx.com 5521844Szelenkov@nginx.com try: 5531844Szelenkov@nginx.com out = subprocess.check_output( 5542073Szelenkov@nginx.com ['procstat', '-f', pid], 5552073Szelenkov@nginx.com stderr=subprocess.STDOUT, 5561844Szelenkov@nginx.com ).decode() 5571844Szelenkov@nginx.com return len(out.splitlines()) 5581844Szelenkov@nginx.com 5591868Szelenkov@nginx.com except (FileNotFoundError, TypeError, subprocess.CalledProcessError): 5601844Szelenkov@nginx.com pass 5611844Szelenkov@nginx.com 5621844Szelenkov@nginx.com try: 5631844Szelenkov@nginx.com out = subprocess.check_output( 5642073Szelenkov@nginx.com ['lsof', '-n', '-p', pid], 5652073Szelenkov@nginx.com stderr=subprocess.STDOUT, 5661844Szelenkov@nginx.com ).decode() 5671844Szelenkov@nginx.com return len(out.splitlines()) 5681844Szelenkov@nginx.com 5691868Szelenkov@nginx.com except (FileNotFoundError, TypeError, subprocess.CalledProcessError): 5701844Szelenkov@nginx.com pass 5711844Szelenkov@nginx.com 5721844Szelenkov@nginx.com return 0 5731844Szelenkov@nginx.com 5741844Szelenkov@nginx.com 5751654Szelenkov@nginx.comdef run_process(target, *args): 5761654Szelenkov@nginx.com global _processes 5771654Szelenkov@nginx.com 5781654Szelenkov@nginx.com process = Process(target=target, args=args) 5791654Szelenkov@nginx.com process.start() 5801654Szelenkov@nginx.com 5811654Szelenkov@nginx.com _processes.append(process) 5821654Szelenkov@nginx.com 5831848Szelenkov@nginx.com 5841654Szelenkov@nginx.comdef stop_processes(): 5851654Szelenkov@nginx.com if not _processes: 5861654Szelenkov@nginx.com return 5871654Szelenkov@nginx.com 5881654Szelenkov@nginx.com fail = False 5891654Szelenkov@nginx.com for process in _processes: 5901654Szelenkov@nginx.com if process.is_alive(): 5911654Szelenkov@nginx.com process.terminate() 5921654Szelenkov@nginx.com process.join(timeout=15) 5931654Szelenkov@nginx.com 5941654Szelenkov@nginx.com if process.is_alive(): 5951654Szelenkov@nginx.com fail = True 5961654Szelenkov@nginx.com 5971654Szelenkov@nginx.com if fail: 5981654Szelenkov@nginx.com return 'Fail to stop process(es)' 5991654Szelenkov@nginx.com 6001654Szelenkov@nginx.com 6011844Szelenkov@nginx.comdef pid_by_name(name): 6021844Szelenkov@nginx.com output = subprocess.check_output(['ps', 'ax', '-O', 'ppid']).decode() 6032330Szelenkov@nginx.com m = re.search(fr'\s*(\d+)\s*{unit_instance["pid"]}.*{name}', output) 6041844Szelenkov@nginx.com return None if m is None else m.group(1) 6051844Szelenkov@nginx.com 6061844Szelenkov@nginx.com 6071844Szelenkov@nginx.comdef find_proc(name, ps_output): 6082330Szelenkov@nginx.com return re.findall(f'{unit_instance["pid"]}.*{name}', ps_output) 6091844Szelenkov@nginx.com 6101844Szelenkov@nginx.com 6112482Szelenkov@nginx.comdef pytest_sessionfinish(): 6122482Szelenkov@nginx.com if not option.restart and option.save_log: 6132482Szelenkov@nginx.com Log.print_path() 6142482Szelenkov@nginx.com 6152482Szelenkov@nginx.com option.restart = True 6162482Szelenkov@nginx.com 6172482Szelenkov@nginx.com unit_stop() 6182482Szelenkov@nginx.com 6192482Szelenkov@nginx.com public_dir(option.cache_dir) 6202482Szelenkov@nginx.com shutil.rmtree(option.cache_dir) 6212482Szelenkov@nginx.com 622*2616Szelenkov@nginx.com if not option.save_log and Path(option.temp_dir).is_dir(): 6232482Szelenkov@nginx.com public_dir(option.temp_dir) 6242482Szelenkov@nginx.com shutil.rmtree(option.temp_dir) 6252482Szelenkov@nginx.com 6262482Szelenkov@nginx.com 6272482Szelenkov@nginx.com@pytest.fixture 6282482Szelenkov@nginx.comdef date_to_sec_epoch(): 6292482Szelenkov@nginx.com def _date_to_sec_epoch(date, template='%a, %d %b %Y %X %Z'): 6302482Szelenkov@nginx.com return time.mktime(time.strptime(date, template)) 6312482Szelenkov@nginx.com 6322482Szelenkov@nginx.com return _date_to_sec_epoch 6332482Szelenkov@nginx.com 6342482Szelenkov@nginx.com 6352482Szelenkov@nginx.com@pytest.fixture 6362482Szelenkov@nginx.comdef findall(): 6372488Szelenkov@nginx.com def _findall(*args, **kwargs): 6382488Szelenkov@nginx.com return Log.findall(*args, **kwargs) 6392482Szelenkov@nginx.com 6402482Szelenkov@nginx.com return _findall 6412482Szelenkov@nginx.com 6422482Szelenkov@nginx.com 6432482Szelenkov@nginx.com@pytest.fixture 6442482Szelenkov@nginx.comdef is_su(): 6452482Szelenkov@nginx.com return option.is_privileged 6462482Szelenkov@nginx.com 6472482Szelenkov@nginx.com 6482482Szelenkov@nginx.com@pytest.fixture 6492482Szelenkov@nginx.comdef is_unsafe(request): 6502482Szelenkov@nginx.com return request.config.getoption("--unsafe") 6512482Szelenkov@nginx.com 6522482Szelenkov@nginx.com 6532482Szelenkov@nginx.com@pytest.fixture 6542488Szelenkov@nginx.comdef require(): 6552488Szelenkov@nginx.com return check_prerequisites 6562488Szelenkov@nginx.com 6572488Szelenkov@nginx.com 6582488Szelenkov@nginx.com@pytest.fixture 6592482Szelenkov@nginx.comdef search_in_file(): 6602482Szelenkov@nginx.com def _search_in_file(pattern, name='unit.log', flags=re.M): 6612482Szelenkov@nginx.com return re.search(pattern, Log.read(name), flags) 6622482Szelenkov@nginx.com 6632482Szelenkov@nginx.com return _search_in_file 6642482Szelenkov@nginx.com 6652482Szelenkov@nginx.com 6662482Szelenkov@nginx.com@pytest.fixture 6672482Szelenkov@nginx.comdef sec_epoch(): 6682482Szelenkov@nginx.com return time.mktime(time.gmtime()) 6692482Szelenkov@nginx.com 6702482Szelenkov@nginx.com 6711736Szelenkov@nginx.com@pytest.fixture() 6721736Szelenkov@nginx.comdef skip_alert(): 6731736Szelenkov@nginx.com def _skip(*alerts): 6741736Szelenkov@nginx.com option.skip_alerts.extend(alerts) 6751736Szelenkov@nginx.com 6761736Szelenkov@nginx.com return _skip 6771736Szelenkov@nginx.com 6781736Szelenkov@nginx.com 6791844Szelenkov@nginx.com@pytest.fixture() 6801844Szelenkov@nginx.comdef skip_fds_check(): 6811844Szelenkov@nginx.com def _skip(main=False, router=False, controller=False): 6821914Szelenkov@nginx.com _fds_info['main']['skip'] = main 6831914Szelenkov@nginx.com _fds_info['router']['skip'] = router 6841914Szelenkov@nginx.com _fds_info['controller']['skip'] = controller 6851844Szelenkov@nginx.com 6861844Szelenkov@nginx.com return _skip 6871844Szelenkov@nginx.com 6881844Szelenkov@nginx.com 6892482Szelenkov@nginx.com@pytest.fixture() 6902482Szelenkov@nginx.comdef system(): 6912482Szelenkov@nginx.com return option.system 6921654Szelenkov@nginx.com 6931848Szelenkov@nginx.com 6941596Szelenkov@nginx.com@pytest.fixture 6952482Szelenkov@nginx.comdef temp_dir(): 6962482Szelenkov@nginx.com return unit_instance['temp_dir'] 6971596Szelenkov@nginx.com 6981848Szelenkov@nginx.com 6991769St.nateldemoura@f5.com@pytest.fixture 7002477Szelenkov@nginx.comdef unit_pid(): 7011769St.nateldemoura@f5.com return unit_instance['process'].pid 7021769St.nateldemoura@f5.com 7031848Szelenkov@nginx.com 7042482Szelenkov@nginx.com@pytest.fixture 7052482Szelenkov@nginx.comdef wait_for_record(): 7062488Szelenkov@nginx.com def _wait_for_record(*args, **kwargs): 7072488Szelenkov@nginx.com return Log.wait_for_record(*args, **kwargs) 7082488Szelenkov@nginx.com 7092482Szelenkov@nginx.com return _wait_for_record 710