11596Szelenkov@nginx.comimport fcntl 21596Szelenkov@nginx.comimport os 31596Szelenkov@nginx.comimport platform 41635Szelenkov@nginx.comimport re 51621Szelenkov@nginx.comimport shutil 61596Szelenkov@nginx.comimport signal 7*1654Szelenkov@nginx.comimport socket 81596Szelenkov@nginx.comimport stat 91596Szelenkov@nginx.comimport subprocess 101596Szelenkov@nginx.comimport sys 111596Szelenkov@nginx.comimport tempfile 121596Szelenkov@nginx.comimport time 13*1654Szelenkov@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 = {} 50*1654Szelenkov@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 72*1654Szelenkov@nginx.comdef skip_alert(*alerts): 73*1654Szelenkov@nginx.com option.skip_alerts.extend(alerts) 74*1654Szelenkov@nginx.com 75*1654Szelenkov@nginx.com 761596Szelenkov@nginx.comdef pytest_generate_tests(metafunc): 771596Szelenkov@nginx.com cls = metafunc.cls 781596Szelenkov@nginx.com if not hasattr(cls, 'application_type'): 791596Szelenkov@nginx.com return 801596Szelenkov@nginx.com 811596Szelenkov@nginx.com type = cls.application_type 821596Szelenkov@nginx.com 831611Smax.romanov@nginx.com def generate_tests(versions): 841611Smax.romanov@nginx.com metafunc.fixturenames.append('tmp_ct') 851630Smax.romanov@nginx.com metafunc.parametrize('tmp_ct', versions) 861611Smax.romanov@nginx.com 871630Smax.romanov@nginx.com for version in versions: 881611Smax.romanov@nginx.com option.generated_tests[ 891630Smax.romanov@nginx.com metafunc.function.__name__ + '[{}]'.format(version) 901611Smax.romanov@nginx.com ] = (type + ' ' + version) 911611Smax.romanov@nginx.com 921596Szelenkov@nginx.com # take available module from option and generate tests for each version 931596Szelenkov@nginx.com 941611Smax.romanov@nginx.com for module, prereq_version in cls.prerequisites['modules'].items(): 951596Szelenkov@nginx.com if module in option.available['modules']: 961596Szelenkov@nginx.com available_versions = option.available['modules'][module] 971596Szelenkov@nginx.com 981596Szelenkov@nginx.com if prereq_version == 'all': 991611Smax.romanov@nginx.com generate_tests(available_versions) 1001596Szelenkov@nginx.com 1011596Szelenkov@nginx.com elif prereq_version == 'any': 1021596Szelenkov@nginx.com option.generated_tests[metafunc.function.__name__] = ( 1031596Szelenkov@nginx.com type + ' ' + available_versions[0] 1041596Szelenkov@nginx.com ) 1051611Smax.romanov@nginx.com elif callable(prereq_version): 1061611Smax.romanov@nginx.com generate_tests( 1071611Smax.romanov@nginx.com list(filter(prereq_version, available_versions)) 1081611Smax.romanov@nginx.com ) 1091611Smax.romanov@nginx.com 1101596Szelenkov@nginx.com else: 1111611Smax.romanov@nginx.com raise ValueError( 1121611Smax.romanov@nginx.com """ 1131611Smax.romanov@nginx.comUnexpected prerequisite version "%s" for module "%s" in %s. 1141611Smax.romanov@nginx.com'all', 'any' or callable expected.""" 1151611Smax.romanov@nginx.com % (str(prereq_version), module, str(cls)) 1161611Smax.romanov@nginx.com ) 1171596Szelenkov@nginx.com 1181596Szelenkov@nginx.com 1191596Szelenkov@nginx.comdef pytest_sessionstart(session): 1201596Szelenkov@nginx.com option.available = {'modules': {}, 'features': {}} 1211596Szelenkov@nginx.com 1221596Szelenkov@nginx.com unit = unit_run() 1231596Szelenkov@nginx.com 1241596Szelenkov@nginx.com # read unit.log 1251596Szelenkov@nginx.com 1261596Szelenkov@nginx.com for i in range(50): 1271596Szelenkov@nginx.com with open(unit['temp_dir'] + '/unit.log', 'r') as f: 1281596Szelenkov@nginx.com log = f.read() 1291596Szelenkov@nginx.com m = re.search('controller started', log) 1301596Szelenkov@nginx.com 1311596Szelenkov@nginx.com if m is None: 1321596Szelenkov@nginx.com time.sleep(0.1) 1331596Szelenkov@nginx.com else: 1341596Szelenkov@nginx.com break 1351596Szelenkov@nginx.com 1361596Szelenkov@nginx.com if m is None: 137*1654Szelenkov@nginx.com _print_log(log) 1381596Szelenkov@nginx.com exit("Unit is writing log too long") 1391596Szelenkov@nginx.com 1401596Szelenkov@nginx.com # discover available modules from unit.log 1411596Szelenkov@nginx.com 1421596Szelenkov@nginx.com for module in re.findall(r'module: ([a-zA-Z]+) (.*) ".*"$', log, re.M): 1431596Szelenkov@nginx.com if module[0] not in option.available['modules']: 1441596Szelenkov@nginx.com option.available['modules'][module[0]] = [module[1]] 1451596Szelenkov@nginx.com else: 1461596Szelenkov@nginx.com option.available['modules'][module[0]].append(module[1]) 1471596Szelenkov@nginx.com 1481621Szelenkov@nginx.com # discover modules from check 1491621Szelenkov@nginx.com 1501621Szelenkov@nginx.com option.available['modules']['openssl'] = check_openssl(unit['unitd']) 1511621Szelenkov@nginx.com option.available['modules']['go'] = check_go( 1521621Szelenkov@nginx.com option.current_dir, unit['temp_dir'], option.test_dir 1531621Szelenkov@nginx.com ) 1541621Szelenkov@nginx.com option.available['modules']['node'] = check_node(option.current_dir) 1551621Szelenkov@nginx.com 1561621Szelenkov@nginx.com # remove None values 1571621Szelenkov@nginx.com 1581621Szelenkov@nginx.com option.available['modules'] = { 1591621Szelenkov@nginx.com k: v for k, v in option.available['modules'].items() if v is not None 1601621Szelenkov@nginx.com } 1611621Szelenkov@nginx.com 1621596Szelenkov@nginx.com unit_stop() 1631596Szelenkov@nginx.com 164*1654Szelenkov@nginx.com shutil.rmtree(unit_instance['temp_dir']) 1651596Szelenkov@nginx.com 166*1654Szelenkov@nginx.com 167*1654Szelenkov@nginx.com@pytest.hookimpl(tryfirst=True, hookwrapper=True) 168*1654Szelenkov@nginx.comdef pytest_runtest_makereport(item, call): 169*1654Szelenkov@nginx.com # execute all other hooks to obtain the report object 170*1654Szelenkov@nginx.com outcome = yield 171*1654Szelenkov@nginx.com rep = outcome.get_result() 172*1654Szelenkov@nginx.com 173*1654Szelenkov@nginx.com # set a report attribute for each phase of a call, which can 174*1654Szelenkov@nginx.com # be "setup", "call", "teardown" 175*1654Szelenkov@nginx.com 176*1654Szelenkov@nginx.com setattr(item, "rep_" + rep.when, rep) 177*1654Szelenkov@nginx.com 178*1654Szelenkov@nginx.com 179*1654Szelenkov@nginx.com@pytest.fixture(autouse=True) 180*1654Szelenkov@nginx.comdef run(request): 181*1654Szelenkov@nginx.com unit = unit_run() 182*1654Szelenkov@nginx.com option.temp_dir = unit['temp_dir'] 183*1654Szelenkov@nginx.com 1841596Szelenkov@nginx.com option.skip_alerts = [ 1851596Szelenkov@nginx.com r'read signalfd\(4\) failed', 1861596Szelenkov@nginx.com r'sendmsg.+failed', 1871596Szelenkov@nginx.com r'recvmsg.+failed', 1881596Szelenkov@nginx.com ] 1891596Szelenkov@nginx.com option.skip_sanitizer = False 1901596Szelenkov@nginx.com 191*1654Szelenkov@nginx.com yield 192*1654Szelenkov@nginx.com 193*1654Szelenkov@nginx.com # stop unit 194*1654Szelenkov@nginx.com 195*1654Szelenkov@nginx.com error = unit_stop() 196*1654Szelenkov@nginx.com 197*1654Szelenkov@nginx.com if error: 198*1654Szelenkov@nginx.com _print_log() 199*1654Szelenkov@nginx.com 200*1654Szelenkov@nginx.com assert error is None, 'stop unit' 201*1654Szelenkov@nginx.com 202*1654Szelenkov@nginx.com # stop all processes 203*1654Szelenkov@nginx.com 204*1654Szelenkov@nginx.com error = stop_processes() 205*1654Szelenkov@nginx.com 206*1654Szelenkov@nginx.com if error: 207*1654Szelenkov@nginx.com _print_log() 208*1654Szelenkov@nginx.com 209*1654Szelenkov@nginx.com assert error is None, 'stop unit' 210*1654Szelenkov@nginx.com 211*1654Szelenkov@nginx.com # check unit.log for alerts 212*1654Szelenkov@nginx.com 213*1654Szelenkov@nginx.com _check_alerts() 214*1654Szelenkov@nginx.com 215*1654Szelenkov@nginx.com # print unit.log in case of error 216*1654Szelenkov@nginx.com 217*1654Szelenkov@nginx.com if request.node.rep_call.failed: 218*1654Szelenkov@nginx.com _print_log() 219*1654Szelenkov@nginx.com 220*1654Szelenkov@nginx.com # remove unit.log 221*1654Szelenkov@nginx.com 222*1654Szelenkov@nginx.com if not option.save_log: 223*1654Szelenkov@nginx.com shutil.rmtree(unit['temp_dir']) 224*1654Szelenkov@nginx.com 2251596Szelenkov@nginx.comdef unit_run(): 2261596Szelenkov@nginx.com global unit_instance 2271596Szelenkov@nginx.com build_dir = option.current_dir + '/build' 2281596Szelenkov@nginx.com unitd = build_dir + '/unitd' 2291596Szelenkov@nginx.com 2301596Szelenkov@nginx.com if not os.path.isfile(unitd): 2311596Szelenkov@nginx.com exit('Could not find unit') 2321596Szelenkov@nginx.com 2331596Szelenkov@nginx.com temp_dir = tempfile.mkdtemp(prefix='unit-test-') 2341596Szelenkov@nginx.com public_dir(temp_dir) 2351596Szelenkov@nginx.com 2361596Szelenkov@nginx.com if oct(stat.S_IMODE(os.stat(build_dir).st_mode)) != '0o777': 2371596Szelenkov@nginx.com public_dir(build_dir) 2381596Szelenkov@nginx.com 2391596Szelenkov@nginx.com os.mkdir(temp_dir + '/state') 2401596Szelenkov@nginx.com 2411596Szelenkov@nginx.com with open(temp_dir + '/unit.log', 'w') as log: 2421596Szelenkov@nginx.com unit_instance['process'] = subprocess.Popen( 2431596Szelenkov@nginx.com [ 2441596Szelenkov@nginx.com unitd, 2451596Szelenkov@nginx.com '--no-daemon', 2461596Szelenkov@nginx.com '--modules', 2471596Szelenkov@nginx.com build_dir, 2481596Szelenkov@nginx.com '--state', 2491596Szelenkov@nginx.com temp_dir + '/state', 2501596Szelenkov@nginx.com '--pid', 2511596Szelenkov@nginx.com temp_dir + '/unit.pid', 2521596Szelenkov@nginx.com '--log', 2531596Szelenkov@nginx.com temp_dir + '/unit.log', 2541596Szelenkov@nginx.com '--control', 2551596Szelenkov@nginx.com 'unix:' + temp_dir + '/control.unit.sock', 2561596Szelenkov@nginx.com '--tmp', 2571596Szelenkov@nginx.com temp_dir, 2581596Szelenkov@nginx.com ], 2591596Szelenkov@nginx.com stderr=log, 2601596Szelenkov@nginx.com ) 2611596Szelenkov@nginx.com 2621596Szelenkov@nginx.com if not waitforfiles(temp_dir + '/control.unit.sock'): 2631596Szelenkov@nginx.com _print_log() 2641596Szelenkov@nginx.com exit('Could not start unit') 2651596Szelenkov@nginx.com 2661596Szelenkov@nginx.com unit_instance['temp_dir'] = temp_dir 2671596Szelenkov@nginx.com unit_instance['log'] = temp_dir + '/unit.log' 2681596Szelenkov@nginx.com unit_instance['control_sock'] = temp_dir + '/control.unit.sock' 2691596Szelenkov@nginx.com unit_instance['unitd'] = unitd 2701596Szelenkov@nginx.com 2711596Szelenkov@nginx.com return unit_instance 2721596Szelenkov@nginx.com 2731596Szelenkov@nginx.com 2741596Szelenkov@nginx.comdef unit_stop(): 2751596Szelenkov@nginx.com p = unit_instance['process'] 2761596Szelenkov@nginx.com 2771596Szelenkov@nginx.com if p.poll() is not None: 2781596Szelenkov@nginx.com return 2791596Szelenkov@nginx.com 2801596Szelenkov@nginx.com p.send_signal(signal.SIGQUIT) 2811596Szelenkov@nginx.com 2821596Szelenkov@nginx.com try: 2831596Szelenkov@nginx.com retcode = p.wait(15) 2841596Szelenkov@nginx.com if retcode: 2851596Szelenkov@nginx.com return 'Child process terminated with code ' + str(retcode) 2861596Szelenkov@nginx.com except: 2871596Szelenkov@nginx.com p.kill() 2881596Szelenkov@nginx.com return 'Could not terminate unit' 2891596Szelenkov@nginx.com 2901596Szelenkov@nginx.comdef public_dir(path): 2911596Szelenkov@nginx.com os.chmod(path, 0o777) 2921596Szelenkov@nginx.com 2931596Szelenkov@nginx.com for root, dirs, files in os.walk(path): 2941596Szelenkov@nginx.com for d in dirs: 2951596Szelenkov@nginx.com os.chmod(os.path.join(root, d), 0o777) 2961596Szelenkov@nginx.com for f in files: 2971596Szelenkov@nginx.com os.chmod(os.path.join(root, f), 0o777) 2981596Szelenkov@nginx.com 2991596Szelenkov@nginx.comdef waitforfiles(*files): 3001596Szelenkov@nginx.com for i in range(50): 3011596Szelenkov@nginx.com wait = False 3021596Szelenkov@nginx.com ret = False 3031596Szelenkov@nginx.com 3041596Szelenkov@nginx.com for f in files: 3051596Szelenkov@nginx.com if not os.path.exists(f): 3061596Szelenkov@nginx.com wait = True 3071596Szelenkov@nginx.com break 3081596Szelenkov@nginx.com 3091596Szelenkov@nginx.com if wait: 3101596Szelenkov@nginx.com time.sleep(0.1) 3111596Szelenkov@nginx.com 3121596Szelenkov@nginx.com else: 3131596Szelenkov@nginx.com ret = True 3141596Szelenkov@nginx.com break 3151596Szelenkov@nginx.com 3161596Szelenkov@nginx.com return ret 3171596Szelenkov@nginx.com 3181596Szelenkov@nginx.com 3191596Szelenkov@nginx.com 320*1654Szelenkov@nginx.comdef _check_alerts(path=None): 321*1654Szelenkov@nginx.com if path is None: 322*1654Szelenkov@nginx.com path = unit_instance['log'] 3231596Szelenkov@nginx.com 324*1654Szelenkov@nginx.com with open(path, 'r', encoding='utf-8', errors='ignore') as f: 325*1654Szelenkov@nginx.com log = f.read() 326*1654Szelenkov@nginx.com 3271596Szelenkov@nginx.com found = False 3281596Szelenkov@nginx.com 3291596Szelenkov@nginx.com alerts = re.findall(r'.+\[alert\].+', log) 3301596Szelenkov@nginx.com 3311596Szelenkov@nginx.com if alerts: 3321596Szelenkov@nginx.com print('All alerts/sanitizer errors found in log:') 3331596Szelenkov@nginx.com [print(alert) for alert in alerts] 3341596Szelenkov@nginx.com found = True 3351596Szelenkov@nginx.com 3361596Szelenkov@nginx.com if option.skip_alerts: 3371596Szelenkov@nginx.com for skip in option.skip_alerts: 3381596Szelenkov@nginx.com alerts = [al for al in alerts if re.search(skip, al) is None] 3391596Szelenkov@nginx.com 3401596Szelenkov@nginx.com if alerts: 341*1654Szelenkov@nginx.com _print_log(log) 3421596Szelenkov@nginx.com assert not alerts, 'alert(s)' 3431596Szelenkov@nginx.com 3441596Szelenkov@nginx.com if not option.skip_sanitizer: 3451596Szelenkov@nginx.com sanitizer_errors = re.findall('.+Sanitizer.+', log) 3461596Szelenkov@nginx.com 3471596Szelenkov@nginx.com if sanitizer_errors: 348*1654Szelenkov@nginx.com _print_log(log) 3491596Szelenkov@nginx.com assert not sanitizer_errors, 'sanitizer error(s)' 3501596Szelenkov@nginx.com 3511596Szelenkov@nginx.com if found: 3521596Szelenkov@nginx.com print('skipped.') 3531596Szelenkov@nginx.com 3541596Szelenkov@nginx.com 355*1654Szelenkov@nginx.comdef _print_log(data=None): 356*1654Szelenkov@nginx.com path = unit_instance['log'] 3571596Szelenkov@nginx.com 3581621Szelenkov@nginx.com print('Path to unit.log:\n' + path + '\n') 3591596Szelenkov@nginx.com 3601596Szelenkov@nginx.com if option.print_log: 3611596Szelenkov@nginx.com os.set_blocking(sys.stdout.fileno(), True) 3621596Szelenkov@nginx.com sys.stdout.flush() 3631596Szelenkov@nginx.com 3641596Szelenkov@nginx.com if data is None: 3651621Szelenkov@nginx.com with open(path, 'r', encoding='utf-8', errors='ignore') as f: 3661596Szelenkov@nginx.com shutil.copyfileobj(f, sys.stdout) 3671596Szelenkov@nginx.com else: 3681596Szelenkov@nginx.com sys.stdout.write(data) 3691596Szelenkov@nginx.com 3701596Szelenkov@nginx.com 371*1654Szelenkov@nginx.comdef run_process(target, *args): 372*1654Szelenkov@nginx.com global _processes 373*1654Szelenkov@nginx.com 374*1654Szelenkov@nginx.com process = Process(target=target, args=args) 375*1654Szelenkov@nginx.com process.start() 376*1654Szelenkov@nginx.com 377*1654Szelenkov@nginx.com _processes.append(process) 378*1654Szelenkov@nginx.com 379*1654Szelenkov@nginx.comdef stop_processes(): 380*1654Szelenkov@nginx.com if not _processes: 381*1654Szelenkov@nginx.com return 382*1654Szelenkov@nginx.com 383*1654Szelenkov@nginx.com fail = False 384*1654Szelenkov@nginx.com for process in _processes: 385*1654Szelenkov@nginx.com if process.is_alive(): 386*1654Szelenkov@nginx.com process.terminate() 387*1654Szelenkov@nginx.com process.join(timeout=15) 388*1654Szelenkov@nginx.com 389*1654Szelenkov@nginx.com if process.is_alive(): 390*1654Szelenkov@nginx.com fail = True 391*1654Szelenkov@nginx.com 392*1654Szelenkov@nginx.com if fail: 393*1654Szelenkov@nginx.com return 'Fail to stop process(es)' 394*1654Szelenkov@nginx.com 395*1654Szelenkov@nginx.com 396*1654Szelenkov@nginx.comdef waitforsocket(port): 397*1654Szelenkov@nginx.com ret = False 398*1654Szelenkov@nginx.com 399*1654Szelenkov@nginx.com for i in range(50): 400*1654Szelenkov@nginx.com try: 401*1654Szelenkov@nginx.com sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 402*1654Szelenkov@nginx.com sock.connect(('127.0.0.1', port)) 403*1654Szelenkov@nginx.com ret = True 404*1654Szelenkov@nginx.com break 405*1654Szelenkov@nginx.com except: 406*1654Szelenkov@nginx.com sock.close() 407*1654Szelenkov@nginx.com time.sleep(0.1) 408*1654Szelenkov@nginx.com 409*1654Szelenkov@nginx.com sock.close() 410*1654Szelenkov@nginx.com 411*1654Szelenkov@nginx.com assert ret, 'socket connected' 412*1654Szelenkov@nginx.com 413*1654Szelenkov@nginx.com@pytest.fixture 414*1654Szelenkov@nginx.comdef temp_dir(request): 415*1654Szelenkov@nginx.com return unit_instance['temp_dir'] 416*1654Szelenkov@nginx.com 4171596Szelenkov@nginx.com@pytest.fixture 4181596Szelenkov@nginx.comdef is_unsafe(request): 4191596Szelenkov@nginx.com return request.config.getoption("--unsafe") 4201596Szelenkov@nginx.com 4211596Szelenkov@nginx.com@pytest.fixture 4221596Szelenkov@nginx.comdef is_su(request): 4231596Szelenkov@nginx.com return os.geteuid() == 0 4241596Szelenkov@nginx.com 4251596Szelenkov@nginx.comdef pytest_sessionfinish(session): 4261596Szelenkov@nginx.com unit_stop() 427