11596Szelenkov@nginx.comimport fcntl 21596Szelenkov@nginx.comimport os 31596Szelenkov@nginx.comimport platform 41635Szelenkov@nginx.comimport re 51621Szelenkov@nginx.comimport shutil 61596Szelenkov@nginx.comimport signal 71654Szelenkov@nginx.comimport socket 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 141596Szelenkov@nginx.com 151635Szelenkov@nginx.comimport pytest 161635Szelenkov@nginx.com 171621Szelenkov@nginx.comfrom unit.check.go import check_go 181621Szelenkov@nginx.comfrom unit.check.node import check_node 191621Szelenkov@nginx.comfrom unit.check.tls import check_openssl 201621Szelenkov@nginx.com 211596Szelenkov@nginx.com 221596Szelenkov@nginx.comdef pytest_addoption(parser): 231596Szelenkov@nginx.com parser.addoption( 241596Szelenkov@nginx.com "--detailed", 251596Szelenkov@nginx.com default=False, 261596Szelenkov@nginx.com action="store_true", 271596Szelenkov@nginx.com help="Detailed output for tests", 281596Szelenkov@nginx.com ) 291596Szelenkov@nginx.com parser.addoption( 301596Szelenkov@nginx.com "--print_log", 311596Szelenkov@nginx.com default=False, 321596Szelenkov@nginx.com action="store_true", 331596Szelenkov@nginx.com help="Print unit.log to stdout in case of errors", 341596Szelenkov@nginx.com ) 351596Szelenkov@nginx.com parser.addoption( 361596Szelenkov@nginx.com "--save_log", 371596Szelenkov@nginx.com default=False, 381596Szelenkov@nginx.com action="store_true", 391596Szelenkov@nginx.com help="Save unit.log after the test execution", 401596Szelenkov@nginx.com ) 411596Szelenkov@nginx.com parser.addoption( 421596Szelenkov@nginx.com "--unsafe", 431596Szelenkov@nginx.com default=False, 441596Szelenkov@nginx.com action="store_true", 451596Szelenkov@nginx.com help="Run unsafe tests", 461596Szelenkov@nginx.com ) 471596Szelenkov@nginx.com 481596Szelenkov@nginx.com 491596Szelenkov@nginx.comunit_instance = {} 501654Szelenkov@nginx.com_processes = [] 511596Szelenkov@nginx.comoption = None 521596Szelenkov@nginx.com 531596Szelenkov@nginx.com 541596Szelenkov@nginx.comdef pytest_configure(config): 551596Szelenkov@nginx.com global option 561596Szelenkov@nginx.com option = config.option 571596Szelenkov@nginx.com 581596Szelenkov@nginx.com option.generated_tests = {} 591596Szelenkov@nginx.com option.current_dir = os.path.abspath( 601596Szelenkov@nginx.com os.path.join(os.path.dirname(__file__), os.pardir) 611596Szelenkov@nginx.com ) 621596Szelenkov@nginx.com option.test_dir = option.current_dir + '/test' 631596Szelenkov@nginx.com option.architecture = platform.architecture()[0] 641596Szelenkov@nginx.com option.system = platform.system() 651596Szelenkov@nginx.com 661596Szelenkov@nginx.com # set stdout to non-blocking 671596Szelenkov@nginx.com 681596Szelenkov@nginx.com if option.detailed or option.print_log: 691596Szelenkov@nginx.com fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0) 701596Szelenkov@nginx.com 711596Szelenkov@nginx.com 721654Szelenkov@nginx.comdef skip_alert(*alerts): 731654Szelenkov@nginx.com option.skip_alerts.extend(alerts) 741654Szelenkov@nginx.com 751654Szelenkov@nginx.com 761596Szelenkov@nginx.comdef pytest_generate_tests(metafunc): 771596Szelenkov@nginx.com cls = metafunc.cls 781670Smax.romanov@nginx.com if (not hasattr(cls, 'application_type') 791670Smax.romanov@nginx.com or cls.application_type == None 801670Smax.romanov@nginx.com or cls.application_type == 'external'): 811596Szelenkov@nginx.com return 821596Szelenkov@nginx.com 831596Szelenkov@nginx.com type = cls.application_type 841596Szelenkov@nginx.com 851611Smax.romanov@nginx.com def generate_tests(versions): 861611Smax.romanov@nginx.com metafunc.fixturenames.append('tmp_ct') 871630Smax.romanov@nginx.com metafunc.parametrize('tmp_ct', versions) 881611Smax.romanov@nginx.com 891630Smax.romanov@nginx.com for version in versions: 901611Smax.romanov@nginx.com option.generated_tests[ 911630Smax.romanov@nginx.com metafunc.function.__name__ + '[{}]'.format(version) 921611Smax.romanov@nginx.com ] = (type + ' ' + version) 931611Smax.romanov@nginx.com 941596Szelenkov@nginx.com # take available module from option and generate tests for each version 951596Szelenkov@nginx.com 961611Smax.romanov@nginx.com for module, prereq_version in cls.prerequisites['modules'].items(): 971596Szelenkov@nginx.com if module in option.available['modules']: 981596Szelenkov@nginx.com available_versions = option.available['modules'][module] 991596Szelenkov@nginx.com 1001596Szelenkov@nginx.com if prereq_version == 'all': 1011611Smax.romanov@nginx.com generate_tests(available_versions) 1021596Szelenkov@nginx.com 1031596Szelenkov@nginx.com elif prereq_version == 'any': 1041596Szelenkov@nginx.com option.generated_tests[metafunc.function.__name__] = ( 1051596Szelenkov@nginx.com type + ' ' + available_versions[0] 1061596Szelenkov@nginx.com ) 1071611Smax.romanov@nginx.com elif callable(prereq_version): 1081611Smax.romanov@nginx.com generate_tests( 1091611Smax.romanov@nginx.com list(filter(prereq_version, available_versions)) 1101611Smax.romanov@nginx.com ) 1111611Smax.romanov@nginx.com 1121596Szelenkov@nginx.com else: 1131611Smax.romanov@nginx.com raise ValueError( 1141611Smax.romanov@nginx.com """ 1151611Smax.romanov@nginx.comUnexpected prerequisite version "%s" for module "%s" in %s. 1161611Smax.romanov@nginx.com'all', 'any' or callable expected.""" 1171611Smax.romanov@nginx.com % (str(prereq_version), module, str(cls)) 1181611Smax.romanov@nginx.com ) 1191596Szelenkov@nginx.com 1201596Szelenkov@nginx.com 1211596Szelenkov@nginx.comdef pytest_sessionstart(session): 1221596Szelenkov@nginx.com option.available = {'modules': {}, 'features': {}} 1231596Szelenkov@nginx.com 1241596Szelenkov@nginx.com unit = unit_run() 1251596Szelenkov@nginx.com 1261596Szelenkov@nginx.com # read unit.log 1271596Szelenkov@nginx.com 1281596Szelenkov@nginx.com for i in range(50): 1291596Szelenkov@nginx.com with open(unit['temp_dir'] + '/unit.log', 'r') as f: 1301596Szelenkov@nginx.com log = f.read() 1311596Szelenkov@nginx.com m = re.search('controller started', log) 1321596Szelenkov@nginx.com 1331596Szelenkov@nginx.com if m is None: 1341596Szelenkov@nginx.com time.sleep(0.1) 1351596Szelenkov@nginx.com else: 1361596Szelenkov@nginx.com break 1371596Szelenkov@nginx.com 1381596Szelenkov@nginx.com if m is None: 1391654Szelenkov@nginx.com _print_log(log) 1401596Szelenkov@nginx.com exit("Unit is writing log too long") 1411596Szelenkov@nginx.com 1421596Szelenkov@nginx.com # discover available modules from unit.log 1431596Szelenkov@nginx.com 1441596Szelenkov@nginx.com for module in re.findall(r'module: ([a-zA-Z]+) (.*) ".*"$', log, re.M): 145*1705Smax.romanov@nginx.com versions = option.available['modules'].setdefault(module[0], []) 146*1705Smax.romanov@nginx.com if module[1] not in versions: 147*1705Smax.romanov@nginx.com versions.append(module[1]) 1481596Szelenkov@nginx.com 1491621Szelenkov@nginx.com # discover modules from check 1501621Szelenkov@nginx.com 1511621Szelenkov@nginx.com option.available['modules']['openssl'] = check_openssl(unit['unitd']) 1521621Szelenkov@nginx.com option.available['modules']['go'] = check_go( 1531621Szelenkov@nginx.com option.current_dir, unit['temp_dir'], option.test_dir 1541621Szelenkov@nginx.com ) 1551621Szelenkov@nginx.com option.available['modules']['node'] = check_node(option.current_dir) 1561621Szelenkov@nginx.com 1571621Szelenkov@nginx.com # remove None values 1581621Szelenkov@nginx.com 1591621Szelenkov@nginx.com option.available['modules'] = { 1601621Szelenkov@nginx.com k: v for k, v in option.available['modules'].items() if v is not None 1611621Szelenkov@nginx.com } 1621621Szelenkov@nginx.com 1631596Szelenkov@nginx.com unit_stop() 1641596Szelenkov@nginx.com 1651654Szelenkov@nginx.com shutil.rmtree(unit_instance['temp_dir']) 1661596Szelenkov@nginx.com 1671654Szelenkov@nginx.com 1681654Szelenkov@nginx.com@pytest.hookimpl(tryfirst=True, hookwrapper=True) 1691654Szelenkov@nginx.comdef pytest_runtest_makereport(item, call): 1701654Szelenkov@nginx.com # execute all other hooks to obtain the report object 1711654Szelenkov@nginx.com outcome = yield 1721654Szelenkov@nginx.com rep = outcome.get_result() 1731654Szelenkov@nginx.com 1741654Szelenkov@nginx.com # set a report attribute for each phase of a call, which can 1751654Szelenkov@nginx.com # be "setup", "call", "teardown" 1761654Szelenkov@nginx.com 1771654Szelenkov@nginx.com setattr(item, "rep_" + rep.when, rep) 1781654Szelenkov@nginx.com 1791654Szelenkov@nginx.com 1801654Szelenkov@nginx.com@pytest.fixture(autouse=True) 1811654Szelenkov@nginx.comdef run(request): 1821654Szelenkov@nginx.com unit = unit_run() 1831654Szelenkov@nginx.com option.temp_dir = unit['temp_dir'] 1841654Szelenkov@nginx.com 1851596Szelenkov@nginx.com option.skip_alerts = [ 1861596Szelenkov@nginx.com r'read signalfd\(4\) failed', 1871596Szelenkov@nginx.com r'sendmsg.+failed', 1881596Szelenkov@nginx.com r'recvmsg.+failed', 1891596Szelenkov@nginx.com ] 1901596Szelenkov@nginx.com option.skip_sanitizer = False 1911596Szelenkov@nginx.com 1921654Szelenkov@nginx.com yield 1931654Szelenkov@nginx.com 1941654Szelenkov@nginx.com # stop unit 1951654Szelenkov@nginx.com 1961654Szelenkov@nginx.com error = unit_stop() 1971654Szelenkov@nginx.com 1981654Szelenkov@nginx.com if error: 1991654Szelenkov@nginx.com _print_log() 2001654Szelenkov@nginx.com 2011654Szelenkov@nginx.com assert error is None, 'stop unit' 2021654Szelenkov@nginx.com 2031654Szelenkov@nginx.com # stop all processes 2041654Szelenkov@nginx.com 2051654Szelenkov@nginx.com error = stop_processes() 2061654Szelenkov@nginx.com 2071654Szelenkov@nginx.com if error: 2081654Szelenkov@nginx.com _print_log() 2091654Szelenkov@nginx.com 2101654Szelenkov@nginx.com assert error is None, 'stop unit' 2111654Szelenkov@nginx.com 2121654Szelenkov@nginx.com # check unit.log for alerts 2131654Szelenkov@nginx.com 2141654Szelenkov@nginx.com _check_alerts() 2151654Szelenkov@nginx.com 2161654Szelenkov@nginx.com # print unit.log in case of error 2171654Szelenkov@nginx.com 2181654Szelenkov@nginx.com if request.node.rep_call.failed: 2191654Szelenkov@nginx.com _print_log() 2201654Szelenkov@nginx.com 2211654Szelenkov@nginx.com # remove unit.log 2221654Szelenkov@nginx.com 2231654Szelenkov@nginx.com if not option.save_log: 2241654Szelenkov@nginx.com shutil.rmtree(unit['temp_dir']) 2251654Szelenkov@nginx.com 2261596Szelenkov@nginx.comdef unit_run(): 2271596Szelenkov@nginx.com global unit_instance 2281596Szelenkov@nginx.com build_dir = option.current_dir + '/build' 2291596Szelenkov@nginx.com unitd = build_dir + '/unitd' 2301596Szelenkov@nginx.com 2311596Szelenkov@nginx.com if not os.path.isfile(unitd): 2321596Szelenkov@nginx.com exit('Could not find unit') 2331596Szelenkov@nginx.com 2341596Szelenkov@nginx.com temp_dir = tempfile.mkdtemp(prefix='unit-test-') 2351596Szelenkov@nginx.com public_dir(temp_dir) 2361596Szelenkov@nginx.com 2371596Szelenkov@nginx.com if oct(stat.S_IMODE(os.stat(build_dir).st_mode)) != '0o777': 2381596Szelenkov@nginx.com public_dir(build_dir) 2391596Szelenkov@nginx.com 2401596Szelenkov@nginx.com os.mkdir(temp_dir + '/state') 2411596Szelenkov@nginx.com 2421596Szelenkov@nginx.com with open(temp_dir + '/unit.log', 'w') as log: 2431596Szelenkov@nginx.com unit_instance['process'] = subprocess.Popen( 2441596Szelenkov@nginx.com [ 2451596Szelenkov@nginx.com unitd, 2461596Szelenkov@nginx.com '--no-daemon', 2471596Szelenkov@nginx.com '--modules', 2481596Szelenkov@nginx.com build_dir, 2491596Szelenkov@nginx.com '--state', 2501596Szelenkov@nginx.com temp_dir + '/state', 2511596Szelenkov@nginx.com '--pid', 2521596Szelenkov@nginx.com temp_dir + '/unit.pid', 2531596Szelenkov@nginx.com '--log', 2541596Szelenkov@nginx.com temp_dir + '/unit.log', 2551596Szelenkov@nginx.com '--control', 2561596Szelenkov@nginx.com 'unix:' + temp_dir + '/control.unit.sock', 2571596Szelenkov@nginx.com '--tmp', 2581596Szelenkov@nginx.com temp_dir, 2591596Szelenkov@nginx.com ], 2601596Szelenkov@nginx.com stderr=log, 2611596Szelenkov@nginx.com ) 2621596Szelenkov@nginx.com 2631596Szelenkov@nginx.com if not waitforfiles(temp_dir + '/control.unit.sock'): 2641596Szelenkov@nginx.com _print_log() 2651596Szelenkov@nginx.com exit('Could not start unit') 2661596Szelenkov@nginx.com 2671596Szelenkov@nginx.com unit_instance['temp_dir'] = temp_dir 2681596Szelenkov@nginx.com unit_instance['log'] = temp_dir + '/unit.log' 2691596Szelenkov@nginx.com unit_instance['control_sock'] = temp_dir + '/control.unit.sock' 2701596Szelenkov@nginx.com unit_instance['unitd'] = unitd 2711596Szelenkov@nginx.com 2721596Szelenkov@nginx.com return unit_instance 2731596Szelenkov@nginx.com 2741596Szelenkov@nginx.com 2751596Szelenkov@nginx.comdef unit_stop(): 2761596Szelenkov@nginx.com p = unit_instance['process'] 2771596Szelenkov@nginx.com 2781596Szelenkov@nginx.com if p.poll() is not None: 2791596Szelenkov@nginx.com return 2801596Szelenkov@nginx.com 2811596Szelenkov@nginx.com p.send_signal(signal.SIGQUIT) 2821596Szelenkov@nginx.com 2831596Szelenkov@nginx.com try: 2841596Szelenkov@nginx.com retcode = p.wait(15) 2851596Szelenkov@nginx.com if retcode: 2861596Szelenkov@nginx.com return 'Child process terminated with code ' + str(retcode) 2871596Szelenkov@nginx.com except: 2881596Szelenkov@nginx.com p.kill() 2891596Szelenkov@nginx.com return 'Could not terminate unit' 2901596Szelenkov@nginx.com 2911596Szelenkov@nginx.comdef public_dir(path): 2921596Szelenkov@nginx.com os.chmod(path, 0o777) 2931596Szelenkov@nginx.com 2941596Szelenkov@nginx.com for root, dirs, files in os.walk(path): 2951596Szelenkov@nginx.com for d in dirs: 2961596Szelenkov@nginx.com os.chmod(os.path.join(root, d), 0o777) 2971596Szelenkov@nginx.com for f in files: 2981596Szelenkov@nginx.com os.chmod(os.path.join(root, f), 0o777) 2991596Szelenkov@nginx.com 3001596Szelenkov@nginx.comdef waitforfiles(*files): 3011596Szelenkov@nginx.com for i in range(50): 3021596Szelenkov@nginx.com wait = False 3031596Szelenkov@nginx.com ret = False 3041596Szelenkov@nginx.com 3051596Szelenkov@nginx.com for f in files: 3061596Szelenkov@nginx.com if not os.path.exists(f): 3071596Szelenkov@nginx.com wait = True 3081596Szelenkov@nginx.com break 3091596Szelenkov@nginx.com 3101596Szelenkov@nginx.com if wait: 3111596Szelenkov@nginx.com time.sleep(0.1) 3121596Szelenkov@nginx.com 3131596Szelenkov@nginx.com else: 3141596Szelenkov@nginx.com ret = True 3151596Szelenkov@nginx.com break 3161596Szelenkov@nginx.com 3171596Szelenkov@nginx.com return ret 3181596Szelenkov@nginx.com 3191596Szelenkov@nginx.com 3201596Szelenkov@nginx.com 3211654Szelenkov@nginx.comdef _check_alerts(path=None): 3221654Szelenkov@nginx.com if path is None: 3231654Szelenkov@nginx.com path = unit_instance['log'] 3241596Szelenkov@nginx.com 3251654Szelenkov@nginx.com with open(path, 'r', encoding='utf-8', errors='ignore') as f: 3261654Szelenkov@nginx.com log = f.read() 3271654Szelenkov@nginx.com 3281596Szelenkov@nginx.com found = False 3291596Szelenkov@nginx.com 3301596Szelenkov@nginx.com alerts = re.findall(r'.+\[alert\].+', log) 3311596Szelenkov@nginx.com 3321596Szelenkov@nginx.com if alerts: 3331596Szelenkov@nginx.com print('All alerts/sanitizer errors found in log:') 3341596Szelenkov@nginx.com [print(alert) for alert in alerts] 3351596Szelenkov@nginx.com found = True 3361596Szelenkov@nginx.com 3371596Szelenkov@nginx.com if option.skip_alerts: 3381596Szelenkov@nginx.com for skip in option.skip_alerts: 3391596Szelenkov@nginx.com alerts = [al for al in alerts if re.search(skip, al) is None] 3401596Szelenkov@nginx.com 3411596Szelenkov@nginx.com if alerts: 3421654Szelenkov@nginx.com _print_log(log) 3431596Szelenkov@nginx.com assert not alerts, 'alert(s)' 3441596Szelenkov@nginx.com 3451596Szelenkov@nginx.com if not option.skip_sanitizer: 3461596Szelenkov@nginx.com sanitizer_errors = re.findall('.+Sanitizer.+', log) 3471596Szelenkov@nginx.com 3481596Szelenkov@nginx.com if sanitizer_errors: 3491654Szelenkov@nginx.com _print_log(log) 3501596Szelenkov@nginx.com assert not sanitizer_errors, 'sanitizer error(s)' 3511596Szelenkov@nginx.com 3521596Szelenkov@nginx.com if found: 3531596Szelenkov@nginx.com print('skipped.') 3541596Szelenkov@nginx.com 3551596Szelenkov@nginx.com 3561654Szelenkov@nginx.comdef _print_log(data=None): 3571654Szelenkov@nginx.com path = unit_instance['log'] 3581596Szelenkov@nginx.com 3591621Szelenkov@nginx.com print('Path to unit.log:\n' + path + '\n') 3601596Szelenkov@nginx.com 3611596Szelenkov@nginx.com if option.print_log: 3621596Szelenkov@nginx.com os.set_blocking(sys.stdout.fileno(), True) 3631596Szelenkov@nginx.com sys.stdout.flush() 3641596Szelenkov@nginx.com 3651596Szelenkov@nginx.com if data is None: 3661621Szelenkov@nginx.com with open(path, 'r', encoding='utf-8', errors='ignore') as f: 3671596Szelenkov@nginx.com shutil.copyfileobj(f, sys.stdout) 3681596Szelenkov@nginx.com else: 3691596Szelenkov@nginx.com sys.stdout.write(data) 3701596Szelenkov@nginx.com 3711596Szelenkov@nginx.com 3721654Szelenkov@nginx.comdef run_process(target, *args): 3731654Szelenkov@nginx.com global _processes 3741654Szelenkov@nginx.com 3751654Szelenkov@nginx.com process = Process(target=target, args=args) 3761654Szelenkov@nginx.com process.start() 3771654Szelenkov@nginx.com 3781654Szelenkov@nginx.com _processes.append(process) 3791654Szelenkov@nginx.com 3801654Szelenkov@nginx.comdef stop_processes(): 3811654Szelenkov@nginx.com if not _processes: 3821654Szelenkov@nginx.com return 3831654Szelenkov@nginx.com 3841654Szelenkov@nginx.com fail = False 3851654Szelenkov@nginx.com for process in _processes: 3861654Szelenkov@nginx.com if process.is_alive(): 3871654Szelenkov@nginx.com process.terminate() 3881654Szelenkov@nginx.com process.join(timeout=15) 3891654Szelenkov@nginx.com 3901654Szelenkov@nginx.com if process.is_alive(): 3911654Szelenkov@nginx.com fail = True 3921654Szelenkov@nginx.com 3931654Szelenkov@nginx.com if fail: 3941654Szelenkov@nginx.com return 'Fail to stop process(es)' 3951654Szelenkov@nginx.com 3961654Szelenkov@nginx.com 3971654Szelenkov@nginx.comdef waitforsocket(port): 3981654Szelenkov@nginx.com ret = False 3991654Szelenkov@nginx.com 4001654Szelenkov@nginx.com for i in range(50): 4011654Szelenkov@nginx.com try: 4021654Szelenkov@nginx.com sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4031654Szelenkov@nginx.com sock.connect(('127.0.0.1', port)) 4041654Szelenkov@nginx.com ret = True 4051654Szelenkov@nginx.com break 4061654Szelenkov@nginx.com except: 4071654Szelenkov@nginx.com sock.close() 4081654Szelenkov@nginx.com time.sleep(0.1) 4091654Szelenkov@nginx.com 4101654Szelenkov@nginx.com sock.close() 4111654Szelenkov@nginx.com 4121654Szelenkov@nginx.com assert ret, 'socket connected' 4131654Szelenkov@nginx.com 4141654Szelenkov@nginx.com@pytest.fixture 4151654Szelenkov@nginx.comdef temp_dir(request): 4161654Szelenkov@nginx.com return unit_instance['temp_dir'] 4171654Szelenkov@nginx.com 4181596Szelenkov@nginx.com@pytest.fixture 4191596Szelenkov@nginx.comdef is_unsafe(request): 4201596Szelenkov@nginx.com return request.config.getoption("--unsafe") 4211596Szelenkov@nginx.com 4221596Szelenkov@nginx.com@pytest.fixture 4231596Szelenkov@nginx.comdef is_su(request): 4241596Szelenkov@nginx.com return os.geteuid() == 0 4251596Szelenkov@nginx.com 4261596Szelenkov@nginx.comdef pytest_sessionfinish(session): 4271596Szelenkov@nginx.com unit_stop() 428