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): 1622073Szelenkov@nginx.com generate_tests(list(filter(prereq_version, available_versions))) 1631611Smax.romanov@nginx.com 1641596Szelenkov@nginx.com else: 1651611Smax.romanov@nginx.com raise ValueError( 1661611Smax.romanov@nginx.com """ 1671611Smax.romanov@nginx.comUnexpected prerequisite version "%s" for module "%s" in %s. 1681611Smax.romanov@nginx.com'all', 'any' or callable expected.""" 1691611Smax.romanov@nginx.com % (str(prereq_version), module, str(cls)) 1701611Smax.romanov@nginx.com ) 1711596Szelenkov@nginx.com 1721596Szelenkov@nginx.com 1731596Szelenkov@nginx.comdef pytest_sessionstart(session): 1741596Szelenkov@nginx.com option.available = {'modules': {}, 'features': {}} 1751596Szelenkov@nginx.com 1761596Szelenkov@nginx.com unit = unit_run() 1771596Szelenkov@nginx.com 1781596Szelenkov@nginx.com # read unit.log 1791596Szelenkov@nginx.com 1801596Szelenkov@nginx.com for i in range(50): 1811850Smax.romanov@nginx.com with open(Log.get_path(), 'r') as f: 1821596Szelenkov@nginx.com log = f.read() 1831596Szelenkov@nginx.com m = re.search('controller started', log) 1841596Szelenkov@nginx.com 1851596Szelenkov@nginx.com if m is None: 1861596Szelenkov@nginx.com time.sleep(0.1) 1871596Szelenkov@nginx.com else: 1881596Szelenkov@nginx.com break 1891596Szelenkov@nginx.com 1901596Szelenkov@nginx.com if m is None: 1911654Szelenkov@nginx.com _print_log(log) 1921596Szelenkov@nginx.com exit("Unit is writing log too long") 1931596Szelenkov@nginx.com 1941596Szelenkov@nginx.com # discover available modules from unit.log 1951596Szelenkov@nginx.com 1961596Szelenkov@nginx.com for module in re.findall(r'module: ([a-zA-Z]+) (.*) ".*"$', log, re.M): 1971705Smax.romanov@nginx.com versions = option.available['modules'].setdefault(module[0], []) 1981705Smax.romanov@nginx.com if module[1] not in versions: 1991705Smax.romanov@nginx.com versions.append(module[1]) 2001596Szelenkov@nginx.com 2011621Szelenkov@nginx.com # discover modules from check 2021621Szelenkov@nginx.com 2031621Szelenkov@nginx.com option.available['modules']['openssl'] = check_openssl(unit['unitd']) 2042062Smax.romanov@nginx.com option.available['modules']['go'] = check_go() 2051621Szelenkov@nginx.com option.available['modules']['node'] = check_node(option.current_dir) 2061807Szelenkov@nginx.com option.available['modules']['regex'] = check_regex(unit['unitd']) 2071621Szelenkov@nginx.com 2081621Szelenkov@nginx.com # remove None values 2091621Szelenkov@nginx.com 2101621Szelenkov@nginx.com option.available['modules'] = { 2111621Szelenkov@nginx.com k: v for k, v in option.available['modules'].items() if v is not None 2121621Szelenkov@nginx.com } 2131621Szelenkov@nginx.com 2141858Szelenkov@nginx.com check_chroot() 2151740Szelenkov@nginx.com check_isolation() 2161740Szelenkov@nginx.com 2171805Szelenkov@nginx.com _clear_conf(unit['temp_dir'] + '/control.unit.sock') 2181803Szelenkov@nginx.com 2191596Szelenkov@nginx.com unit_stop() 2201596Szelenkov@nginx.com 2211730Szelenkov@nginx.com _check_alerts() 2221730Szelenkov@nginx.com 2231803Szelenkov@nginx.com if option.restart: 2241803Szelenkov@nginx.com shutil.rmtree(unit_instance['temp_dir']) 2251596Szelenkov@nginx.com 2261848Szelenkov@nginx.com 2271654Szelenkov@nginx.com@pytest.hookimpl(tryfirst=True, hookwrapper=True) 2281654Szelenkov@nginx.comdef pytest_runtest_makereport(item, call): 2291654Szelenkov@nginx.com # execute all other hooks to obtain the report object 2301654Szelenkov@nginx.com outcome = yield 2311654Szelenkov@nginx.com rep = outcome.get_result() 2321654Szelenkov@nginx.com 2331654Szelenkov@nginx.com # set a report attribute for each phase of a call, which can 2341654Szelenkov@nginx.com # be "setup", "call", "teardown" 2351654Szelenkov@nginx.com 2361654Szelenkov@nginx.com setattr(item, "rep_" + rep.when, rep) 2371654Szelenkov@nginx.com 2381654Szelenkov@nginx.com 2391741Szelenkov@nginx.com@pytest.fixture(scope='class', autouse=True) 2401741Szelenkov@nginx.comdef check_prerequisites(request): 2411741Szelenkov@nginx.com cls = request.cls 2421741Szelenkov@nginx.com missed = [] 2431741Szelenkov@nginx.com 2441741Szelenkov@nginx.com # check modules 2451741Szelenkov@nginx.com 2461741Szelenkov@nginx.com if 'modules' in cls.prerequisites: 2471741Szelenkov@nginx.com available_modules = list(option.available['modules'].keys()) 2481741Szelenkov@nginx.com 2491741Szelenkov@nginx.com for module in cls.prerequisites['modules']: 2501741Szelenkov@nginx.com if module in available_modules: 2511741Szelenkov@nginx.com continue 2521741Szelenkov@nginx.com 2531741Szelenkov@nginx.com missed.append(module) 2541741Szelenkov@nginx.com 2551741Szelenkov@nginx.com if missed: 2561741Szelenkov@nginx.com pytest.skip('Unit has no ' + ', '.join(missed) + ' module(s)') 2571741Szelenkov@nginx.com 2581741Szelenkov@nginx.com # check features 2591741Szelenkov@nginx.com 2601741Szelenkov@nginx.com if 'features' in cls.prerequisites: 2611741Szelenkov@nginx.com available_features = list(option.available['features'].keys()) 2621741Szelenkov@nginx.com 2631741Szelenkov@nginx.com for feature in cls.prerequisites['features']: 2641741Szelenkov@nginx.com if feature in available_features: 2651741Szelenkov@nginx.com continue 2661741Szelenkov@nginx.com 2671741Szelenkov@nginx.com missed.append(feature) 2681741Szelenkov@nginx.com 2691741Szelenkov@nginx.com if missed: 2701741Szelenkov@nginx.com pytest.skip(', '.join(missed) + ' feature(s) not supported') 2711741Szelenkov@nginx.com 2721741Szelenkov@nginx.com 2731654Szelenkov@nginx.com@pytest.fixture(autouse=True) 2741654Szelenkov@nginx.comdef run(request): 2751654Szelenkov@nginx.com unit = unit_run() 2761654Szelenkov@nginx.com 2771596Szelenkov@nginx.com option.skip_alerts = [ 2781596Szelenkov@nginx.com r'read signalfd\(4\) failed', 2791596Szelenkov@nginx.com r'sendmsg.+failed', 2801596Szelenkov@nginx.com r'recvmsg.+failed', 2811596Szelenkov@nginx.com ] 2821596Szelenkov@nginx.com option.skip_sanitizer = False 2831596Szelenkov@nginx.com 2841914Szelenkov@nginx.com _fds_info['main']['skip'] = False 2851914Szelenkov@nginx.com _fds_info['router']['skip'] = False 2861914Szelenkov@nginx.com _fds_info['controller']['skip'] = False 2871844Szelenkov@nginx.com 2881654Szelenkov@nginx.com yield 2891654Szelenkov@nginx.com 2901654Szelenkov@nginx.com # stop unit 2911654Szelenkov@nginx.com 2921803Szelenkov@nginx.com error_stop_unit = unit_stop() 2931803Szelenkov@nginx.com error_stop_processes = stop_processes() 2941803Szelenkov@nginx.com 2951803Szelenkov@nginx.com # prepare log 2961803Szelenkov@nginx.com 2971850Smax.romanov@nginx.com with Log.open(encoding='utf-8') as f: 2981803Szelenkov@nginx.com log = f.read() 2991850Smax.romanov@nginx.com Log.set_pos(f.tell()) 3001654Szelenkov@nginx.com 3011803Szelenkov@nginx.com if not option.save_log and option.restart: 3021803Szelenkov@nginx.com shutil.rmtree(unit['temp_dir']) 3031850Smax.romanov@nginx.com Log.set_pos(0) 3041803Szelenkov@nginx.com 3051803Szelenkov@nginx.com # clean temp_dir before the next test 3061654Szelenkov@nginx.com 3071803Szelenkov@nginx.com if not option.restart: 3081914Szelenkov@nginx.com _clear_conf(unit['temp_dir'] + '/control.unit.sock', log=log) 3091803Szelenkov@nginx.com 3101803Szelenkov@nginx.com for item in os.listdir(unit['temp_dir']): 3111803Szelenkov@nginx.com if item not in [ 3121803Szelenkov@nginx.com 'control.unit.sock', 3131803Szelenkov@nginx.com 'state', 3141803Szelenkov@nginx.com 'unit.pid', 3151803Szelenkov@nginx.com 'unit.log', 3161803Szelenkov@nginx.com ]: 3171803Szelenkov@nginx.com path = os.path.join(unit['temp_dir'], item) 3181654Szelenkov@nginx.com 3191803Szelenkov@nginx.com public_dir(path) 3201654Szelenkov@nginx.com 3212073Szelenkov@nginx.com if os.path.isfile(path) or stat.S_ISSOCK(os.stat(path).st_mode): 3221803Szelenkov@nginx.com os.remove(path) 3231803Szelenkov@nginx.com else: 3241933Smax.romanov@nginx.com for attempt in range(10): 3251933Smax.romanov@nginx.com try: 3261933Smax.romanov@nginx.com shutil.rmtree(path) 3271933Smax.romanov@nginx.com break 3281933Smax.romanov@nginx.com except OSError as err: 3291933Smax.romanov@nginx.com if err.errno != 16: 3301933Smax.romanov@nginx.com raise 3311933Smax.romanov@nginx.com time.sleep(1) 3321654Szelenkov@nginx.com 3331914Szelenkov@nginx.com # check descriptors 3341844Szelenkov@nginx.com 3351914Szelenkov@nginx.com _check_fds(log=log) 3361844Szelenkov@nginx.com 337*2074Szelenkov@nginx.com # check processes id's and amount 338*2074Szelenkov@nginx.com 339*2074Szelenkov@nginx.com _check_processes() 340*2074Szelenkov@nginx.com 3411654Szelenkov@nginx.com # print unit.log in case of error 3421654Szelenkov@nginx.com 3431706Smax.romanov@nginx.com if hasattr(request.node, 'rep_call') and request.node.rep_call.failed: 3441803Szelenkov@nginx.com _print_log(log) 3451803Szelenkov@nginx.com 3461803Szelenkov@nginx.com if error_stop_unit or error_stop_processes: 3471803Szelenkov@nginx.com _print_log(log) 3481654Szelenkov@nginx.com 3491803Szelenkov@nginx.com # check unit.log for errors 3501654Szelenkov@nginx.com 3511803Szelenkov@nginx.com assert error_stop_unit is None, 'stop unit' 3521803Szelenkov@nginx.com assert error_stop_processes is None, 'stop processes' 3531803Szelenkov@nginx.com 3541803Szelenkov@nginx.com _check_alerts(log=log) 3551654Szelenkov@nginx.com 3561848Szelenkov@nginx.com 3571986Szelenkov@nginx.comdef unit_run(state_dir=None): 3581596Szelenkov@nginx.com global unit_instance 3591803Szelenkov@nginx.com 3601803Szelenkov@nginx.com if not option.restart and 'unitd' in unit_instance: 3611803Szelenkov@nginx.com return unit_instance 3621803Szelenkov@nginx.com 3631596Szelenkov@nginx.com build_dir = option.current_dir + '/build' 3641596Szelenkov@nginx.com unitd = build_dir + '/unitd' 3651596Szelenkov@nginx.com 3661596Szelenkov@nginx.com if not os.path.isfile(unitd): 3671596Szelenkov@nginx.com exit('Could not find unit') 3681596Szelenkov@nginx.com 3691596Szelenkov@nginx.com temp_dir = tempfile.mkdtemp(prefix='unit-test-') 3701596Szelenkov@nginx.com public_dir(temp_dir) 3711596Szelenkov@nginx.com 3721596Szelenkov@nginx.com if oct(stat.S_IMODE(os.stat(build_dir).st_mode)) != '0o777': 3731596Szelenkov@nginx.com public_dir(build_dir) 3741596Szelenkov@nginx.com 3751986Szelenkov@nginx.com state = temp_dir + '/state' if state_dir is None else state_dir 3761986Szelenkov@nginx.com if not os.path.isdir(state): 3771986Szelenkov@nginx.com os.mkdir(state) 3781596Szelenkov@nginx.com 3791761Sdefan@nginx.com unitd_args = [ 3801761Sdefan@nginx.com unitd, 3811761Sdefan@nginx.com '--no-daemon', 3821761Sdefan@nginx.com '--modules', 3831761Sdefan@nginx.com build_dir, 3841761Sdefan@nginx.com '--state', 3851986Szelenkov@nginx.com state, 3861761Sdefan@nginx.com '--pid', 3871761Sdefan@nginx.com temp_dir + '/unit.pid', 3881761Sdefan@nginx.com '--log', 3891761Sdefan@nginx.com temp_dir + '/unit.log', 3901761Sdefan@nginx.com '--control', 3911761Sdefan@nginx.com 'unix:' + temp_dir + '/control.unit.sock', 3921761Sdefan@nginx.com '--tmp', 3931761Sdefan@nginx.com temp_dir, 3941761Sdefan@nginx.com ] 3951761Sdefan@nginx.com 3961761Sdefan@nginx.com if option.user: 3971761Sdefan@nginx.com unitd_args.extend(['--user', option.user]) 3981761Sdefan@nginx.com 3991596Szelenkov@nginx.com with open(temp_dir + '/unit.log', 'w') as log: 4001761Sdefan@nginx.com unit_instance['process'] = subprocess.Popen(unitd_args, stderr=log) 4011596Szelenkov@nginx.com 4021934Smax.romanov@nginx.com Log.temp_dir = temp_dir 4031934Smax.romanov@nginx.com 4041596Szelenkov@nginx.com if not waitforfiles(temp_dir + '/control.unit.sock'): 4051596Szelenkov@nginx.com _print_log() 4061596Szelenkov@nginx.com exit('Could not start unit') 4071596Szelenkov@nginx.com 4081596Szelenkov@nginx.com unit_instance['temp_dir'] = temp_dir 4091596Szelenkov@nginx.com unit_instance['control_sock'] = temp_dir + '/control.unit.sock' 4101596Szelenkov@nginx.com unit_instance['unitd'] = unitd 4111596Szelenkov@nginx.com 4121850Smax.romanov@nginx.com option.temp_dir = temp_dir 4131850Smax.romanov@nginx.com 4141844Szelenkov@nginx.com with open(temp_dir + '/unit.pid', 'r') as f: 4151844Szelenkov@nginx.com unit_instance['pid'] = f.read().rstrip() 4161844Szelenkov@nginx.com 4171986Szelenkov@nginx.com if state_dir is None: 4181986Szelenkov@nginx.com _clear_conf(unit_instance['temp_dir'] + '/control.unit.sock') 4191844Szelenkov@nginx.com 4201914Szelenkov@nginx.com _fds_info['main']['fds'] = _count_fds(unit_instance['pid']) 4211844Szelenkov@nginx.com 4221914Szelenkov@nginx.com router = _fds_info['router'] 4231844Szelenkov@nginx.com router['pid'] = pid_by_name(router['name']) 4241844Szelenkov@nginx.com router['fds'] = _count_fds(router['pid']) 4251844Szelenkov@nginx.com 4261914Szelenkov@nginx.com controller = _fds_info['controller'] 4271844Szelenkov@nginx.com controller['pid'] = pid_by_name(controller['name']) 4281844Szelenkov@nginx.com controller['fds'] = _count_fds(controller['pid']) 4291844Szelenkov@nginx.com 4301596Szelenkov@nginx.com return unit_instance 4311596Szelenkov@nginx.com 4321596Szelenkov@nginx.com 4331596Szelenkov@nginx.comdef unit_stop(): 4341803Szelenkov@nginx.com if not option.restart: 4351803Szelenkov@nginx.com if inspect.stack()[1].function.startswith('test_'): 4361803Szelenkov@nginx.com pytest.skip('no restart mode') 4371803Szelenkov@nginx.com 4381803Szelenkov@nginx.com return 4391803Szelenkov@nginx.com 440*2074Szelenkov@nginx.com # check zombies 441*2074Szelenkov@nginx.com 442*2074Szelenkov@nginx.com out = subprocess.check_output( 443*2074Szelenkov@nginx.com ['ps', 'ax', '-o', 'state', '-o', 'ppid'] 444*2074Szelenkov@nginx.com ).decode() 445*2074Szelenkov@nginx.com z_ppids = re.findall(r'Z\s*(\d+)', out) 446*2074Szelenkov@nginx.com assert unit_instance['pid'] not in z_ppids, 'no zombies' 447*2074Szelenkov@nginx.com 448*2074Szelenkov@nginx.com # terminate unit 449*2074Szelenkov@nginx.com 4501596Szelenkov@nginx.com p = unit_instance['process'] 4511596Szelenkov@nginx.com 4521596Szelenkov@nginx.com if p.poll() is not None: 4531596Szelenkov@nginx.com return 4541596Szelenkov@nginx.com 4551596Szelenkov@nginx.com p.send_signal(signal.SIGQUIT) 4561596Szelenkov@nginx.com 4571596Szelenkov@nginx.com try: 4581596Szelenkov@nginx.com retcode = p.wait(15) 4591596Szelenkov@nginx.com if retcode: 4601596Szelenkov@nginx.com return 'Child process terminated with code ' + str(retcode) 4611706Smax.romanov@nginx.com 4621706Smax.romanov@nginx.com except KeyboardInterrupt: 4631706Smax.romanov@nginx.com p.kill() 4641706Smax.romanov@nginx.com raise 4651706Smax.romanov@nginx.com 4661596Szelenkov@nginx.com except: 4671596Szelenkov@nginx.com p.kill() 4681596Szelenkov@nginx.com return 'Could not terminate unit' 4691596Szelenkov@nginx.com 4701596Szelenkov@nginx.com 4711914Szelenkov@nginx.com@print_log_on_assert 4721914Szelenkov@nginx.comdef _check_alerts(*, log=None): 4731803Szelenkov@nginx.com if log is None: 4741850Smax.romanov@nginx.com with Log.open(encoding='utf-8') as f: 4751803Szelenkov@nginx.com log = f.read() 4761654Szelenkov@nginx.com 4771596Szelenkov@nginx.com found = False 4781596Szelenkov@nginx.com 4791596Szelenkov@nginx.com alerts = re.findall(r'.+\[alert\].+', log) 4801596Szelenkov@nginx.com 4811596Szelenkov@nginx.com if alerts: 4821736Szelenkov@nginx.com print('\nAll alerts/sanitizer errors found in log:') 4831596Szelenkov@nginx.com [print(alert) for alert in alerts] 4841596Szelenkov@nginx.com found = True 4851596Szelenkov@nginx.com 4861596Szelenkov@nginx.com if option.skip_alerts: 4871596Szelenkov@nginx.com for skip in option.skip_alerts: 4881596Szelenkov@nginx.com alerts = [al for al in alerts if re.search(skip, al) is None] 4891596Szelenkov@nginx.com 4901914Szelenkov@nginx.com assert not alerts, 'alert(s)' 4911596Szelenkov@nginx.com 4921596Szelenkov@nginx.com if not option.skip_sanitizer: 4931596Szelenkov@nginx.com sanitizer_errors = re.findall('.+Sanitizer.+', log) 4941596Szelenkov@nginx.com 4951914Szelenkov@nginx.com assert not sanitizer_errors, 'sanitizer error(s)' 4961596Szelenkov@nginx.com 4971596Szelenkov@nginx.com if found: 4981596Szelenkov@nginx.com print('skipped.') 4991596Szelenkov@nginx.com 5001596Szelenkov@nginx.com 5011934Smax.romanov@nginx.comdef _print_log(log=None): 5021850Smax.romanov@nginx.com path = Log.get_path() 5031596Szelenkov@nginx.com 5041621Szelenkov@nginx.com print('Path to unit.log:\n' + path + '\n') 5051596Szelenkov@nginx.com 5061596Szelenkov@nginx.com if option.print_log: 5071596Szelenkov@nginx.com os.set_blocking(sys.stdout.fileno(), True) 5081596Szelenkov@nginx.com sys.stdout.flush() 5091596Szelenkov@nginx.com 5101914Szelenkov@nginx.com if log is None: 5111621Szelenkov@nginx.com with open(path, 'r', encoding='utf-8', errors='ignore') as f: 5121596Szelenkov@nginx.com shutil.copyfileobj(f, sys.stdout) 5131596Szelenkov@nginx.com else: 5141914Szelenkov@nginx.com sys.stdout.write(log) 5151596Szelenkov@nginx.com 5161596Szelenkov@nginx.com 5171914Szelenkov@nginx.com@print_log_on_assert 5181914Szelenkov@nginx.comdef _clear_conf(sock, *, log=None): 5191805Szelenkov@nginx.com resp = http.put( 5201803Szelenkov@nginx.com url='/config', 5211803Szelenkov@nginx.com sock_type='unix', 5221803Szelenkov@nginx.com addr=sock, 5231803Szelenkov@nginx.com body=json.dumps({"listeners": {}, "applications": {}}), 5241803Szelenkov@nginx.com )['body'] 5251803Szelenkov@nginx.com 5261914Szelenkov@nginx.com assert 'success' in resp, 'clear conf' 5271805Szelenkov@nginx.com 5281808Szelenkov@nginx.com if 'openssl' not in option.available['modules']: 5291808Szelenkov@nginx.com return 5301808Szelenkov@nginx.com 5311805Szelenkov@nginx.com try: 5321848Szelenkov@nginx.com certs = json.loads( 5332073Szelenkov@nginx.com http.get(url='/certificates', sock_type='unix', addr=sock)['body'] 5341848Szelenkov@nginx.com ).keys() 5351805Szelenkov@nginx.com 5361805Szelenkov@nginx.com except json.JSONDecodeError: 5371805Szelenkov@nginx.com pytest.fail('Can\'t parse certificates list.') 5381805Szelenkov@nginx.com 5391805Szelenkov@nginx.com for cert in certs: 5401805Szelenkov@nginx.com resp = http.delete( 5412073Szelenkov@nginx.com url='/certificates/' + cert, 5422073Szelenkov@nginx.com sock_type='unix', 5432073Szelenkov@nginx.com addr=sock, 5441805Szelenkov@nginx.com )['body'] 5451805Szelenkov@nginx.com 5461914Szelenkov@nginx.com assert 'success' in resp, 'remove certificate' 5471914Szelenkov@nginx.com 5481914Szelenkov@nginx.com 549*2074Szelenkov@nginx.comdef _check_processes(): 550*2074Szelenkov@nginx.com router_pid = _fds_info['router']['pid'] 551*2074Szelenkov@nginx.com controller_pid = _fds_info['controller']['pid'] 552*2074Szelenkov@nginx.com unit_pid = unit_instance['pid'] 553*2074Szelenkov@nginx.com 554*2074Szelenkov@nginx.com for i in range(600): 555*2074Szelenkov@nginx.com out = ( 556*2074Szelenkov@nginx.com subprocess.check_output( 557*2074Szelenkov@nginx.com ['ps', '-ax', '-o', 'pid', '-o', 'ppid', '-o', 'command'] 558*2074Szelenkov@nginx.com ) 559*2074Szelenkov@nginx.com .decode() 560*2074Szelenkov@nginx.com .splitlines() 561*2074Szelenkov@nginx.com ) 562*2074Szelenkov@nginx.com out = [l for l in out if unit_pid in l] 563*2074Szelenkov@nginx.com 564*2074Szelenkov@nginx.com if len(out) <= 3: 565*2074Szelenkov@nginx.com break 566*2074Szelenkov@nginx.com 567*2074Szelenkov@nginx.com time.sleep(0.1) 568*2074Szelenkov@nginx.com 569*2074Szelenkov@nginx.com assert len(out) == 3, 'main, router, and controller expected' 570*2074Szelenkov@nginx.com 571*2074Szelenkov@nginx.com out = [l for l in out if 'unit: main' not in l] 572*2074Szelenkov@nginx.com assert len(out) == 2, 'one main' 573*2074Szelenkov@nginx.com 574*2074Szelenkov@nginx.com out = [ 575*2074Szelenkov@nginx.com l 576*2074Szelenkov@nginx.com for l in out 577*2074Szelenkov@nginx.com if re.search(router_pid + r'\s+' + unit_pid + r'.*unit: router', l) 578*2074Szelenkov@nginx.com is None 579*2074Szelenkov@nginx.com ] 580*2074Szelenkov@nginx.com assert len(out) == 1, 'one router' 581*2074Szelenkov@nginx.com 582*2074Szelenkov@nginx.com out = [ 583*2074Szelenkov@nginx.com l 584*2074Szelenkov@nginx.com for l in out 585*2074Szelenkov@nginx.com if re.search( 586*2074Szelenkov@nginx.com controller_pid + r'\s+' + unit_pid + r'.*unit: controller', l 587*2074Szelenkov@nginx.com ) 588*2074Szelenkov@nginx.com is None 589*2074Szelenkov@nginx.com ] 590*2074Szelenkov@nginx.com assert len(out) == 0, 'one controller' 591*2074Szelenkov@nginx.com 592*2074Szelenkov@nginx.com 5931914Szelenkov@nginx.com@print_log_on_assert 5941914Szelenkov@nginx.comdef _check_fds(*, log=None): 5951914Szelenkov@nginx.com def waitforfds(diff): 5961914Szelenkov@nginx.com for i in range(600): 5971914Szelenkov@nginx.com fds_diff = diff() 5981914Szelenkov@nginx.com 5991914Szelenkov@nginx.com if fds_diff <= option.fds_threshold: 6001914Szelenkov@nginx.com break 6011914Szelenkov@nginx.com 6021914Szelenkov@nginx.com time.sleep(0.1) 6031914Szelenkov@nginx.com 6041914Szelenkov@nginx.com return fds_diff 6051914Szelenkov@nginx.com 6061914Szelenkov@nginx.com ps = _fds_info['main'] 6071914Szelenkov@nginx.com if not ps['skip']: 6081914Szelenkov@nginx.com fds_diff = waitforfds( 6091914Szelenkov@nginx.com lambda: _count_fds(unit_instance['pid']) - ps['fds'] 6101914Szelenkov@nginx.com ) 6111914Szelenkov@nginx.com ps['fds'] += fds_diff 6121914Szelenkov@nginx.com 6132073Szelenkov@nginx.com assert fds_diff <= option.fds_threshold, 'descriptors leak main process' 6141914Szelenkov@nginx.com 6151914Szelenkov@nginx.com else: 6161914Szelenkov@nginx.com ps['fds'] = _count_fds(unit_instance['pid']) 6171914Szelenkov@nginx.com 6181914Szelenkov@nginx.com for name in ['controller', 'router']: 6191914Szelenkov@nginx.com ps = _fds_info[name] 6201914Szelenkov@nginx.com ps_pid = ps['pid'] 6211914Szelenkov@nginx.com ps['pid'] = pid_by_name(ps['name']) 6221914Szelenkov@nginx.com 6231914Szelenkov@nginx.com if not ps['skip']: 6241914Szelenkov@nginx.com fds_diff = waitforfds(lambda: _count_fds(ps['pid']) - ps['fds']) 6251914Szelenkov@nginx.com ps['fds'] += fds_diff 6261914Szelenkov@nginx.com 6271914Szelenkov@nginx.com if not option.restart: 6281914Szelenkov@nginx.com assert ps['pid'] == ps_pid, 'same pid %s' % name 6291914Szelenkov@nginx.com 6301914Szelenkov@nginx.com assert fds_diff <= option.fds_threshold, ( 6311914Szelenkov@nginx.com 'descriptors leak %s' % name 6321914Szelenkov@nginx.com ) 6331914Szelenkov@nginx.com 6341914Szelenkov@nginx.com else: 6351914Szelenkov@nginx.com ps['fds'] = _count_fds(ps['pid']) 6361803Szelenkov@nginx.com 6371848Szelenkov@nginx.com 6381844Szelenkov@nginx.comdef _count_fds(pid): 6391844Szelenkov@nginx.com procfile = '/proc/%s/fd' % pid 6401844Szelenkov@nginx.com if os.path.isdir(procfile): 6411844Szelenkov@nginx.com return len(os.listdir(procfile)) 6421844Szelenkov@nginx.com 6431844Szelenkov@nginx.com try: 6441844Szelenkov@nginx.com out = subprocess.check_output( 6452073Szelenkov@nginx.com ['procstat', '-f', pid], 6462073Szelenkov@nginx.com stderr=subprocess.STDOUT, 6471844Szelenkov@nginx.com ).decode() 6481844Szelenkov@nginx.com return len(out.splitlines()) 6491844Szelenkov@nginx.com 6501868Szelenkov@nginx.com except (FileNotFoundError, TypeError, subprocess.CalledProcessError): 6511844Szelenkov@nginx.com pass 6521844Szelenkov@nginx.com 6531844Szelenkov@nginx.com try: 6541844Szelenkov@nginx.com out = subprocess.check_output( 6552073Szelenkov@nginx.com ['lsof', '-n', '-p', pid], 6562073Szelenkov@nginx.com stderr=subprocess.STDOUT, 6571844Szelenkov@nginx.com ).decode() 6581844Szelenkov@nginx.com return len(out.splitlines()) 6591844Szelenkov@nginx.com 6601868Szelenkov@nginx.com except (FileNotFoundError, TypeError, subprocess.CalledProcessError): 6611844Szelenkov@nginx.com pass 6621844Szelenkov@nginx.com 6631844Szelenkov@nginx.com return 0 6641844Szelenkov@nginx.com 6651844Szelenkov@nginx.com 6661654Szelenkov@nginx.comdef run_process(target, *args): 6671654Szelenkov@nginx.com global _processes 6681654Szelenkov@nginx.com 6691654Szelenkov@nginx.com process = Process(target=target, args=args) 6701654Szelenkov@nginx.com process.start() 6711654Szelenkov@nginx.com 6721654Szelenkov@nginx.com _processes.append(process) 6731654Szelenkov@nginx.com 6741848Szelenkov@nginx.com 6751654Szelenkov@nginx.comdef stop_processes(): 6761654Szelenkov@nginx.com if not _processes: 6771654Szelenkov@nginx.com return 6781654Szelenkov@nginx.com 6791654Szelenkov@nginx.com fail = False 6801654Szelenkov@nginx.com for process in _processes: 6811654Szelenkov@nginx.com if process.is_alive(): 6821654Szelenkov@nginx.com process.terminate() 6831654Szelenkov@nginx.com process.join(timeout=15) 6841654Szelenkov@nginx.com 6851654Szelenkov@nginx.com if process.is_alive(): 6861654Szelenkov@nginx.com fail = True 6871654Szelenkov@nginx.com 6881654Szelenkov@nginx.com if fail: 6891654Szelenkov@nginx.com return 'Fail to stop process(es)' 6901654Szelenkov@nginx.com 6911654Szelenkov@nginx.com 6921844Szelenkov@nginx.comdef pid_by_name(name): 6931844Szelenkov@nginx.com output = subprocess.check_output(['ps', 'ax', '-O', 'ppid']).decode() 6941844Szelenkov@nginx.com m = re.search( 6951844Szelenkov@nginx.com r'\s*(\d+)\s*' + str(unit_instance['pid']) + r'.*' + name, output 6961844Szelenkov@nginx.com ) 6971844Szelenkov@nginx.com return None if m is None else m.group(1) 6981844Szelenkov@nginx.com 6991844Szelenkov@nginx.com 7001844Szelenkov@nginx.comdef find_proc(name, ps_output): 7011844Szelenkov@nginx.com return re.findall(str(unit_instance['pid']) + r'.*' + name, ps_output) 7021844Szelenkov@nginx.com 7031844Szelenkov@nginx.com 7041736Szelenkov@nginx.com@pytest.fixture() 7051736Szelenkov@nginx.comdef skip_alert(): 7061736Szelenkov@nginx.com def _skip(*alerts): 7071736Szelenkov@nginx.com option.skip_alerts.extend(alerts) 7081736Szelenkov@nginx.com 7091736Szelenkov@nginx.com return _skip 7101736Szelenkov@nginx.com 7111736Szelenkov@nginx.com 7121844Szelenkov@nginx.com@pytest.fixture() 7131844Szelenkov@nginx.comdef skip_fds_check(): 7141844Szelenkov@nginx.com def _skip(main=False, router=False, controller=False): 7151914Szelenkov@nginx.com _fds_info['main']['skip'] = main 7161914Szelenkov@nginx.com _fds_info['router']['skip'] = router 7171914Szelenkov@nginx.com _fds_info['controller']['skip'] = controller 7181844Szelenkov@nginx.com 7191844Szelenkov@nginx.com return _skip 7201844Szelenkov@nginx.com 7211844Szelenkov@nginx.com 7221654Szelenkov@nginx.com@pytest.fixture 7231654Szelenkov@nginx.comdef temp_dir(request): 7241654Szelenkov@nginx.com return unit_instance['temp_dir'] 7251654Szelenkov@nginx.com 7261848Szelenkov@nginx.com 7271596Szelenkov@nginx.com@pytest.fixture 7281596Szelenkov@nginx.comdef is_unsafe(request): 7291596Szelenkov@nginx.com return request.config.getoption("--unsafe") 7301596Szelenkov@nginx.com 7311848Szelenkov@nginx.com 7321596Szelenkov@nginx.com@pytest.fixture 7331596Szelenkov@nginx.comdef is_su(request): 7341596Szelenkov@nginx.com return os.geteuid() == 0 7351596Szelenkov@nginx.com 7361848Szelenkov@nginx.com 7371769St.nateldemoura@f5.com@pytest.fixture 7381769St.nateldemoura@f5.comdef unit_pid(request): 7391769St.nateldemoura@f5.com return unit_instance['process'].pid 7401769St.nateldemoura@f5.com 7411848Szelenkov@nginx.com 7421596Szelenkov@nginx.comdef pytest_sessionfinish(session): 7431803Szelenkov@nginx.com if not option.restart and option.save_log: 7441850Smax.romanov@nginx.com print('Path to unit.log:\n' + Log.get_path() + '\n') 7451803Szelenkov@nginx.com 7461803Szelenkov@nginx.com option.restart = True 7471803Szelenkov@nginx.com 7481596Szelenkov@nginx.com unit_stop() 7491864Szelenkov@nginx.com 7501864Szelenkov@nginx.com public_dir(option.cache_dir) 7511757St.nateldemoura@f5.com shutil.rmtree(option.cache_dir) 7521864Szelenkov@nginx.com 7531868Szelenkov@nginx.com if not option.save_log and os.path.isdir(option.temp_dir): 7541864Szelenkov@nginx.com public_dir(option.temp_dir) 7551864Szelenkov@nginx.com shutil.rmtree(option.temp_dir) 756