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 201730Szelenkov@nginx.comfrom unit.option import option 21*1735Szelenkov@nginx.comfrom unit.utils import public_dir 22*1735Szelenkov@nginx.comfrom unit.utils import waitforfiles 231621Szelenkov@nginx.com 241596Szelenkov@nginx.com 251596Szelenkov@nginx.comdef pytest_addoption(parser): 261596Szelenkov@nginx.com parser.addoption( 271596Szelenkov@nginx.com "--detailed", 281596Szelenkov@nginx.com default=False, 291596Szelenkov@nginx.com action="store_true", 301596Szelenkov@nginx.com help="Detailed output for tests", 311596Szelenkov@nginx.com ) 321596Szelenkov@nginx.com parser.addoption( 331596Szelenkov@nginx.com "--print_log", 341596Szelenkov@nginx.com default=False, 351596Szelenkov@nginx.com action="store_true", 361596Szelenkov@nginx.com help="Print unit.log to stdout in case of errors", 371596Szelenkov@nginx.com ) 381596Szelenkov@nginx.com parser.addoption( 391596Szelenkov@nginx.com "--save_log", 401596Szelenkov@nginx.com default=False, 411596Szelenkov@nginx.com action="store_true", 421596Szelenkov@nginx.com help="Save unit.log after the test execution", 431596Szelenkov@nginx.com ) 441596Szelenkov@nginx.com parser.addoption( 451596Szelenkov@nginx.com "--unsafe", 461596Szelenkov@nginx.com default=False, 471596Szelenkov@nginx.com action="store_true", 481596Szelenkov@nginx.com help="Run unsafe tests", 491596Szelenkov@nginx.com ) 501596Szelenkov@nginx.com 511596Szelenkov@nginx.com 521596Szelenkov@nginx.comunit_instance = {} 531654Szelenkov@nginx.com_processes = [] 541596Szelenkov@nginx.com 551596Szelenkov@nginx.comdef pytest_configure(config): 561730Szelenkov@nginx.com option.config = config.option 571730Szelenkov@nginx.com 581730Szelenkov@nginx.com option.detailed = config.option.detailed 591730Szelenkov@nginx.com option.print_log = config.option.print_log 601730Szelenkov@nginx.com option.save_log = config.option.save_log 611730Szelenkov@nginx.com option.unsafe = config.option.unsafe 621596Szelenkov@nginx.com 631596Szelenkov@nginx.com option.generated_tests = {} 641596Szelenkov@nginx.com option.current_dir = os.path.abspath( 651596Szelenkov@nginx.com os.path.join(os.path.dirname(__file__), os.pardir) 661596Szelenkov@nginx.com ) 671596Szelenkov@nginx.com option.test_dir = option.current_dir + '/test' 681596Szelenkov@nginx.com option.architecture = platform.architecture()[0] 691596Szelenkov@nginx.com option.system = platform.system() 701596Szelenkov@nginx.com 711596Szelenkov@nginx.com # set stdout to non-blocking 721596Szelenkov@nginx.com 731596Szelenkov@nginx.com if option.detailed or option.print_log: 741596Szelenkov@nginx.com fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0) 751596Szelenkov@nginx.com 761596Szelenkov@nginx.com 771654Szelenkov@nginx.comdef skip_alert(*alerts): 781654Szelenkov@nginx.com option.skip_alerts.extend(alerts) 791654Szelenkov@nginx.com 801654Szelenkov@nginx.com 811596Szelenkov@nginx.comdef pytest_generate_tests(metafunc): 821596Szelenkov@nginx.com cls = metafunc.cls 831670Smax.romanov@nginx.com if (not hasattr(cls, 'application_type') 841670Smax.romanov@nginx.com or cls.application_type == None 851670Smax.romanov@nginx.com or cls.application_type == 'external'): 861596Szelenkov@nginx.com return 871596Szelenkov@nginx.com 881596Szelenkov@nginx.com type = cls.application_type 891596Szelenkov@nginx.com 901611Smax.romanov@nginx.com def generate_tests(versions): 911611Smax.romanov@nginx.com metafunc.fixturenames.append('tmp_ct') 921630Smax.romanov@nginx.com metafunc.parametrize('tmp_ct', versions) 931611Smax.romanov@nginx.com 941630Smax.romanov@nginx.com for version in versions: 951611Smax.romanov@nginx.com option.generated_tests[ 961630Smax.romanov@nginx.com metafunc.function.__name__ + '[{}]'.format(version) 971611Smax.romanov@nginx.com ] = (type + ' ' + version) 981611Smax.romanov@nginx.com 991596Szelenkov@nginx.com # take available module from option and generate tests for each version 1001596Szelenkov@nginx.com 1011611Smax.romanov@nginx.com for module, prereq_version in cls.prerequisites['modules'].items(): 1021596Szelenkov@nginx.com if module in option.available['modules']: 1031596Szelenkov@nginx.com available_versions = option.available['modules'][module] 1041596Szelenkov@nginx.com 1051596Szelenkov@nginx.com if prereq_version == 'all': 1061611Smax.romanov@nginx.com generate_tests(available_versions) 1071596Szelenkov@nginx.com 1081596Szelenkov@nginx.com elif prereq_version == 'any': 1091596Szelenkov@nginx.com option.generated_tests[metafunc.function.__name__] = ( 1101596Szelenkov@nginx.com type + ' ' + available_versions[0] 1111596Szelenkov@nginx.com ) 1121611Smax.romanov@nginx.com elif callable(prereq_version): 1131611Smax.romanov@nginx.com generate_tests( 1141611Smax.romanov@nginx.com list(filter(prereq_version, available_versions)) 1151611Smax.romanov@nginx.com ) 1161611Smax.romanov@nginx.com 1171596Szelenkov@nginx.com else: 1181611Smax.romanov@nginx.com raise ValueError( 1191611Smax.romanov@nginx.com """ 1201611Smax.romanov@nginx.comUnexpected prerequisite version "%s" for module "%s" in %s. 1211611Smax.romanov@nginx.com'all', 'any' or callable expected.""" 1221611Smax.romanov@nginx.com % (str(prereq_version), module, str(cls)) 1231611Smax.romanov@nginx.com ) 1241596Szelenkov@nginx.com 1251596Szelenkov@nginx.com 1261596Szelenkov@nginx.comdef pytest_sessionstart(session): 1271596Szelenkov@nginx.com option.available = {'modules': {}, 'features': {}} 1281596Szelenkov@nginx.com 1291596Szelenkov@nginx.com unit = unit_run() 1301596Szelenkov@nginx.com 1311596Szelenkov@nginx.com # read unit.log 1321596Szelenkov@nginx.com 1331596Szelenkov@nginx.com for i in range(50): 1341596Szelenkov@nginx.com with open(unit['temp_dir'] + '/unit.log', 'r') as f: 1351596Szelenkov@nginx.com log = f.read() 1361596Szelenkov@nginx.com m = re.search('controller started', log) 1371596Szelenkov@nginx.com 1381596Szelenkov@nginx.com if m is None: 1391596Szelenkov@nginx.com time.sleep(0.1) 1401596Szelenkov@nginx.com else: 1411596Szelenkov@nginx.com break 1421596Szelenkov@nginx.com 1431596Szelenkov@nginx.com if m is None: 1441654Szelenkov@nginx.com _print_log(log) 1451596Szelenkov@nginx.com exit("Unit is writing log too long") 1461596Szelenkov@nginx.com 1471596Szelenkov@nginx.com # discover available modules from unit.log 1481596Szelenkov@nginx.com 1491596Szelenkov@nginx.com for module in re.findall(r'module: ([a-zA-Z]+) (.*) ".*"$', log, re.M): 1501705Smax.romanov@nginx.com versions = option.available['modules'].setdefault(module[0], []) 1511705Smax.romanov@nginx.com if module[1] not in versions: 1521705Smax.romanov@nginx.com versions.append(module[1]) 1531596Szelenkov@nginx.com 1541621Szelenkov@nginx.com # discover modules from check 1551621Szelenkov@nginx.com 1561621Szelenkov@nginx.com option.available['modules']['openssl'] = check_openssl(unit['unitd']) 1571621Szelenkov@nginx.com option.available['modules']['go'] = check_go( 1581621Szelenkov@nginx.com option.current_dir, unit['temp_dir'], option.test_dir 1591621Szelenkov@nginx.com ) 1601621Szelenkov@nginx.com option.available['modules']['node'] = check_node(option.current_dir) 1611621Szelenkov@nginx.com 1621621Szelenkov@nginx.com # remove None values 1631621Szelenkov@nginx.com 1641621Szelenkov@nginx.com option.available['modules'] = { 1651621Szelenkov@nginx.com k: v for k, v in option.available['modules'].items() if v is not None 1661621Szelenkov@nginx.com } 1671621Szelenkov@nginx.com 1681596Szelenkov@nginx.com unit_stop() 1691596Szelenkov@nginx.com 1701730Szelenkov@nginx.com _check_alerts() 1711730Szelenkov@nginx.com 1721654Szelenkov@nginx.com shutil.rmtree(unit_instance['temp_dir']) 1731596Szelenkov@nginx.com 1741654Szelenkov@nginx.com 1751654Szelenkov@nginx.com@pytest.hookimpl(tryfirst=True, hookwrapper=True) 1761654Szelenkov@nginx.comdef pytest_runtest_makereport(item, call): 1771654Szelenkov@nginx.com # execute all other hooks to obtain the report object 1781654Szelenkov@nginx.com outcome = yield 1791654Szelenkov@nginx.com rep = outcome.get_result() 1801654Szelenkov@nginx.com 1811654Szelenkov@nginx.com # set a report attribute for each phase of a call, which can 1821654Szelenkov@nginx.com # be "setup", "call", "teardown" 1831654Szelenkov@nginx.com 1841654Szelenkov@nginx.com setattr(item, "rep_" + rep.when, rep) 1851654Szelenkov@nginx.com 1861654Szelenkov@nginx.com 1871654Szelenkov@nginx.com@pytest.fixture(autouse=True) 1881654Szelenkov@nginx.comdef run(request): 1891654Szelenkov@nginx.com unit = unit_run() 1901654Szelenkov@nginx.com option.temp_dir = unit['temp_dir'] 1911654Szelenkov@nginx.com 1921596Szelenkov@nginx.com option.skip_alerts = [ 1931596Szelenkov@nginx.com r'read signalfd\(4\) failed', 1941596Szelenkov@nginx.com r'sendmsg.+failed', 1951596Szelenkov@nginx.com r'recvmsg.+failed', 1961596Szelenkov@nginx.com ] 1971596Szelenkov@nginx.com option.skip_sanitizer = False 1981596Szelenkov@nginx.com 1991654Szelenkov@nginx.com yield 2001654Szelenkov@nginx.com 2011654Szelenkov@nginx.com # stop unit 2021654Szelenkov@nginx.com 2031654Szelenkov@nginx.com error = unit_stop() 2041654Szelenkov@nginx.com 2051654Szelenkov@nginx.com if error: 2061654Szelenkov@nginx.com _print_log() 2071654Szelenkov@nginx.com 2081654Szelenkov@nginx.com assert error is None, 'stop unit' 2091654Szelenkov@nginx.com 2101654Szelenkov@nginx.com # stop all processes 2111654Szelenkov@nginx.com 2121654Szelenkov@nginx.com error = stop_processes() 2131654Szelenkov@nginx.com 2141654Szelenkov@nginx.com if error: 2151654Szelenkov@nginx.com _print_log() 2161654Szelenkov@nginx.com 2171654Szelenkov@nginx.com assert error is None, 'stop unit' 2181654Szelenkov@nginx.com 2191654Szelenkov@nginx.com # check unit.log for alerts 2201654Szelenkov@nginx.com 2211654Szelenkov@nginx.com _check_alerts() 2221654Szelenkov@nginx.com 2231654Szelenkov@nginx.com # print unit.log in case of error 2241654Szelenkov@nginx.com 2251706Smax.romanov@nginx.com if hasattr(request.node, 'rep_call') and request.node.rep_call.failed: 2261654Szelenkov@nginx.com _print_log() 2271654Szelenkov@nginx.com 2281654Szelenkov@nginx.com # remove unit.log 2291654Szelenkov@nginx.com 2301654Szelenkov@nginx.com if not option.save_log: 2311654Szelenkov@nginx.com shutil.rmtree(unit['temp_dir']) 2321654Szelenkov@nginx.com 2331596Szelenkov@nginx.comdef unit_run(): 2341596Szelenkov@nginx.com global unit_instance 2351596Szelenkov@nginx.com build_dir = option.current_dir + '/build' 2361596Szelenkov@nginx.com unitd = build_dir + '/unitd' 2371596Szelenkov@nginx.com 2381596Szelenkov@nginx.com if not os.path.isfile(unitd): 2391596Szelenkov@nginx.com exit('Could not find unit') 2401596Szelenkov@nginx.com 2411596Szelenkov@nginx.com temp_dir = tempfile.mkdtemp(prefix='unit-test-') 2421596Szelenkov@nginx.com public_dir(temp_dir) 2431596Szelenkov@nginx.com 2441596Szelenkov@nginx.com if oct(stat.S_IMODE(os.stat(build_dir).st_mode)) != '0o777': 2451596Szelenkov@nginx.com public_dir(build_dir) 2461596Szelenkov@nginx.com 2471596Szelenkov@nginx.com os.mkdir(temp_dir + '/state') 2481596Szelenkov@nginx.com 2491596Szelenkov@nginx.com with open(temp_dir + '/unit.log', 'w') as log: 2501596Szelenkov@nginx.com unit_instance['process'] = subprocess.Popen( 2511596Szelenkov@nginx.com [ 2521596Szelenkov@nginx.com unitd, 2531596Szelenkov@nginx.com '--no-daemon', 2541596Szelenkov@nginx.com '--modules', 2551596Szelenkov@nginx.com build_dir, 2561596Szelenkov@nginx.com '--state', 2571596Szelenkov@nginx.com temp_dir + '/state', 2581596Szelenkov@nginx.com '--pid', 2591596Szelenkov@nginx.com temp_dir + '/unit.pid', 2601596Szelenkov@nginx.com '--log', 2611596Szelenkov@nginx.com temp_dir + '/unit.log', 2621596Szelenkov@nginx.com '--control', 2631596Szelenkov@nginx.com 'unix:' + temp_dir + '/control.unit.sock', 2641596Szelenkov@nginx.com '--tmp', 2651596Szelenkov@nginx.com temp_dir, 2661596Szelenkov@nginx.com ], 2671596Szelenkov@nginx.com stderr=log, 2681596Szelenkov@nginx.com ) 2691596Szelenkov@nginx.com 2701596Szelenkov@nginx.com if not waitforfiles(temp_dir + '/control.unit.sock'): 2711596Szelenkov@nginx.com _print_log() 2721596Szelenkov@nginx.com exit('Could not start unit') 2731596Szelenkov@nginx.com 2741596Szelenkov@nginx.com unit_instance['temp_dir'] = temp_dir 2751596Szelenkov@nginx.com unit_instance['log'] = temp_dir + '/unit.log' 2761596Szelenkov@nginx.com unit_instance['control_sock'] = temp_dir + '/control.unit.sock' 2771596Szelenkov@nginx.com unit_instance['unitd'] = unitd 2781596Szelenkov@nginx.com 2791596Szelenkov@nginx.com return unit_instance 2801596Szelenkov@nginx.com 2811596Szelenkov@nginx.com 2821596Szelenkov@nginx.comdef unit_stop(): 2831596Szelenkov@nginx.com p = unit_instance['process'] 2841596Szelenkov@nginx.com 2851596Szelenkov@nginx.com if p.poll() is not None: 2861596Szelenkov@nginx.com return 2871596Szelenkov@nginx.com 2881596Szelenkov@nginx.com p.send_signal(signal.SIGQUIT) 2891596Szelenkov@nginx.com 2901596Szelenkov@nginx.com try: 2911596Szelenkov@nginx.com retcode = p.wait(15) 2921596Szelenkov@nginx.com if retcode: 2931596Szelenkov@nginx.com return 'Child process terminated with code ' + str(retcode) 2941706Smax.romanov@nginx.com 2951706Smax.romanov@nginx.com except KeyboardInterrupt: 2961706Smax.romanov@nginx.com p.kill() 2971706Smax.romanov@nginx.com raise 2981706Smax.romanov@nginx.com 2991596Szelenkov@nginx.com except: 3001596Szelenkov@nginx.com p.kill() 3011596Szelenkov@nginx.com return 'Could not terminate unit' 3021596Szelenkov@nginx.com 3031596Szelenkov@nginx.com 3041596Szelenkov@nginx.com 3051654Szelenkov@nginx.comdef _check_alerts(path=None): 3061654Szelenkov@nginx.com if path is None: 3071654Szelenkov@nginx.com path = unit_instance['log'] 3081596Szelenkov@nginx.com 3091654Szelenkov@nginx.com with open(path, 'r', encoding='utf-8', errors='ignore') as f: 3101654Szelenkov@nginx.com log = f.read() 3111654Szelenkov@nginx.com 3121596Szelenkov@nginx.com found = False 3131596Szelenkov@nginx.com 3141596Szelenkov@nginx.com alerts = re.findall(r'.+\[alert\].+', log) 3151596Szelenkov@nginx.com 3161596Szelenkov@nginx.com if alerts: 3171596Szelenkov@nginx.com print('All alerts/sanitizer errors found in log:') 3181596Szelenkov@nginx.com [print(alert) for alert in alerts] 3191596Szelenkov@nginx.com found = True 3201596Szelenkov@nginx.com 3211596Szelenkov@nginx.com if option.skip_alerts: 3221596Szelenkov@nginx.com for skip in option.skip_alerts: 3231596Szelenkov@nginx.com alerts = [al for al in alerts if re.search(skip, al) is None] 3241596Szelenkov@nginx.com 3251596Szelenkov@nginx.com if alerts: 3261654Szelenkov@nginx.com _print_log(log) 3271596Szelenkov@nginx.com assert not alerts, 'alert(s)' 3281596Szelenkov@nginx.com 3291596Szelenkov@nginx.com if not option.skip_sanitizer: 3301596Szelenkov@nginx.com sanitizer_errors = re.findall('.+Sanitizer.+', log) 3311596Szelenkov@nginx.com 3321596Szelenkov@nginx.com if sanitizer_errors: 3331654Szelenkov@nginx.com _print_log(log) 3341596Szelenkov@nginx.com assert not sanitizer_errors, 'sanitizer error(s)' 3351596Szelenkov@nginx.com 3361596Szelenkov@nginx.com if found: 3371596Szelenkov@nginx.com print('skipped.') 3381596Szelenkov@nginx.com 3391596Szelenkov@nginx.com 3401654Szelenkov@nginx.comdef _print_log(data=None): 3411654Szelenkov@nginx.com path = unit_instance['log'] 3421596Szelenkov@nginx.com 3431621Szelenkov@nginx.com print('Path to unit.log:\n' + path + '\n') 3441596Szelenkov@nginx.com 3451596Szelenkov@nginx.com if option.print_log: 3461596Szelenkov@nginx.com os.set_blocking(sys.stdout.fileno(), True) 3471596Szelenkov@nginx.com sys.stdout.flush() 3481596Szelenkov@nginx.com 3491596Szelenkov@nginx.com if data is None: 3501621Szelenkov@nginx.com with open(path, 'r', encoding='utf-8', errors='ignore') as f: 3511596Szelenkov@nginx.com shutil.copyfileobj(f, sys.stdout) 3521596Szelenkov@nginx.com else: 3531596Szelenkov@nginx.com sys.stdout.write(data) 3541596Szelenkov@nginx.com 3551596Szelenkov@nginx.com 3561654Szelenkov@nginx.comdef run_process(target, *args): 3571654Szelenkov@nginx.com global _processes 3581654Szelenkov@nginx.com 3591654Szelenkov@nginx.com process = Process(target=target, args=args) 3601654Szelenkov@nginx.com process.start() 3611654Szelenkov@nginx.com 3621654Szelenkov@nginx.com _processes.append(process) 3631654Szelenkov@nginx.com 3641654Szelenkov@nginx.comdef stop_processes(): 3651654Szelenkov@nginx.com if not _processes: 3661654Szelenkov@nginx.com return 3671654Szelenkov@nginx.com 3681654Szelenkov@nginx.com fail = False 3691654Szelenkov@nginx.com for process in _processes: 3701654Szelenkov@nginx.com if process.is_alive(): 3711654Szelenkov@nginx.com process.terminate() 3721654Szelenkov@nginx.com process.join(timeout=15) 3731654Szelenkov@nginx.com 3741654Szelenkov@nginx.com if process.is_alive(): 3751654Szelenkov@nginx.com fail = True 3761654Szelenkov@nginx.com 3771654Szelenkov@nginx.com if fail: 3781654Szelenkov@nginx.com return 'Fail to stop process(es)' 3791654Szelenkov@nginx.com 3801654Szelenkov@nginx.com 3811654Szelenkov@nginx.com@pytest.fixture 3821654Szelenkov@nginx.comdef temp_dir(request): 3831654Szelenkov@nginx.com return unit_instance['temp_dir'] 3841654Szelenkov@nginx.com 3851596Szelenkov@nginx.com@pytest.fixture 3861596Szelenkov@nginx.comdef is_unsafe(request): 3871596Szelenkov@nginx.com return request.config.getoption("--unsafe") 3881596Szelenkov@nginx.com 3891596Szelenkov@nginx.com@pytest.fixture 3901596Szelenkov@nginx.comdef is_su(request): 3911596Szelenkov@nginx.com return os.geteuid() == 0 3921596Szelenkov@nginx.com 3931596Szelenkov@nginx.comdef pytest_sessionfinish(session): 3941596Szelenkov@nginx.com unit_stop() 395