1*1596Szelenkov@nginx.comimport fcntl 2*1596Szelenkov@nginx.comimport os 3*1596Szelenkov@nginx.comimport platform 4*1596Szelenkov@nginx.comimport pytest 5*1596Szelenkov@nginx.comimport signal 6*1596Szelenkov@nginx.comimport stat 7*1596Szelenkov@nginx.comimport subprocess 8*1596Szelenkov@nginx.comimport sys 9*1596Szelenkov@nginx.comimport re 10*1596Szelenkov@nginx.comimport tempfile 11*1596Szelenkov@nginx.comimport time 12*1596Szelenkov@nginx.com 13*1596Szelenkov@nginx.com 14*1596Szelenkov@nginx.comdef pytest_addoption(parser): 15*1596Szelenkov@nginx.com parser.addoption( 16*1596Szelenkov@nginx.com "--detailed", 17*1596Szelenkov@nginx.com default=False, 18*1596Szelenkov@nginx.com action="store_true", 19*1596Szelenkov@nginx.com help="Detailed output for tests", 20*1596Szelenkov@nginx.com ) 21*1596Szelenkov@nginx.com parser.addoption( 22*1596Szelenkov@nginx.com "--print_log", 23*1596Szelenkov@nginx.com default=False, 24*1596Szelenkov@nginx.com action="store_true", 25*1596Szelenkov@nginx.com help="Print unit.log to stdout in case of errors", 26*1596Szelenkov@nginx.com ) 27*1596Szelenkov@nginx.com parser.addoption( 28*1596Szelenkov@nginx.com "--save_log", 29*1596Szelenkov@nginx.com default=False, 30*1596Szelenkov@nginx.com action="store_true", 31*1596Szelenkov@nginx.com help="Save unit.log after the test execution", 32*1596Szelenkov@nginx.com ) 33*1596Szelenkov@nginx.com parser.addoption( 34*1596Szelenkov@nginx.com "--unsafe", 35*1596Szelenkov@nginx.com default=False, 36*1596Szelenkov@nginx.com action="store_true", 37*1596Szelenkov@nginx.com help="Run unsafe tests", 38*1596Szelenkov@nginx.com ) 39*1596Szelenkov@nginx.com 40*1596Szelenkov@nginx.com 41*1596Szelenkov@nginx.comunit_instance = {} 42*1596Szelenkov@nginx.comoption = None 43*1596Szelenkov@nginx.com 44*1596Szelenkov@nginx.com 45*1596Szelenkov@nginx.comdef pytest_configure(config): 46*1596Szelenkov@nginx.com global option 47*1596Szelenkov@nginx.com option = config.option 48*1596Szelenkov@nginx.com 49*1596Szelenkov@nginx.com option.generated_tests = {} 50*1596Szelenkov@nginx.com option.current_dir = os.path.abspath( 51*1596Szelenkov@nginx.com os.path.join(os.path.dirname(__file__), os.pardir) 52*1596Szelenkov@nginx.com ) 53*1596Szelenkov@nginx.com option.test_dir = option.current_dir + '/test' 54*1596Szelenkov@nginx.com option.architecture = platform.architecture()[0] 55*1596Szelenkov@nginx.com option.system = platform.system() 56*1596Szelenkov@nginx.com 57*1596Szelenkov@nginx.com # set stdout to non-blocking 58*1596Szelenkov@nginx.com 59*1596Szelenkov@nginx.com if option.detailed or option.print_log: 60*1596Szelenkov@nginx.com fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0) 61*1596Szelenkov@nginx.com 62*1596Szelenkov@nginx.com 63*1596Szelenkov@nginx.comdef pytest_generate_tests(metafunc): 64*1596Szelenkov@nginx.com cls = metafunc.cls 65*1596Szelenkov@nginx.com if not hasattr(cls, 'application_type'): 66*1596Szelenkov@nginx.com return 67*1596Szelenkov@nginx.com 68*1596Szelenkov@nginx.com type = cls.application_type 69*1596Szelenkov@nginx.com 70*1596Szelenkov@nginx.com # take available module from option and generate tests for each version 71*1596Szelenkov@nginx.com 72*1596Szelenkov@nginx.com for module in cls.prerequisites['modules']: 73*1596Szelenkov@nginx.com if module in option.available['modules']: 74*1596Szelenkov@nginx.com prereq_version = cls.prerequisites['modules'][module] 75*1596Szelenkov@nginx.com available_versions = option.available['modules'][module] 76*1596Szelenkov@nginx.com 77*1596Szelenkov@nginx.com if prereq_version == 'all': 78*1596Szelenkov@nginx.com metafunc.fixturenames.append('tmp_ct') 79*1596Szelenkov@nginx.com metafunc.parametrize('tmp_ct', range(len(available_versions))) 80*1596Szelenkov@nginx.com 81*1596Szelenkov@nginx.com for i in range(len(available_versions)): 82*1596Szelenkov@nginx.com version = available_versions[i] 83*1596Szelenkov@nginx.com option.generated_tests[ 84*1596Szelenkov@nginx.com metafunc.function.__name__ + '[{}]'.format(i) 85*1596Szelenkov@nginx.com ] = (type + ' ' + version) 86*1596Szelenkov@nginx.com elif prereq_version == 'any': 87*1596Szelenkov@nginx.com option.generated_tests[metafunc.function.__name__] = ( 88*1596Szelenkov@nginx.com type + ' ' + available_versions[0] 89*1596Szelenkov@nginx.com ) 90*1596Szelenkov@nginx.com else: 91*1596Szelenkov@nginx.com for version in available_versions: 92*1596Szelenkov@nginx.com if version.startswith(prereq_version): 93*1596Szelenkov@nginx.com option.generated_tests[metafunc.function.__name__] = ( 94*1596Szelenkov@nginx.com type + ' ' + version 95*1596Szelenkov@nginx.com ) 96*1596Szelenkov@nginx.com 97*1596Szelenkov@nginx.com 98*1596Szelenkov@nginx.comdef pytest_sessionstart(session): 99*1596Szelenkov@nginx.com option.available = {'modules': {}, 'features': {}} 100*1596Szelenkov@nginx.com 101*1596Szelenkov@nginx.com unit = unit_run() 102*1596Szelenkov@nginx.com 103*1596Szelenkov@nginx.com # read unit.log 104*1596Szelenkov@nginx.com 105*1596Szelenkov@nginx.com for i in range(50): 106*1596Szelenkov@nginx.com with open(unit['temp_dir'] + '/unit.log', 'r') as f: 107*1596Szelenkov@nginx.com log = f.read() 108*1596Szelenkov@nginx.com m = re.search('controller started', log) 109*1596Szelenkov@nginx.com 110*1596Szelenkov@nginx.com if m is None: 111*1596Szelenkov@nginx.com time.sleep(0.1) 112*1596Szelenkov@nginx.com else: 113*1596Szelenkov@nginx.com break 114*1596Szelenkov@nginx.com 115*1596Szelenkov@nginx.com if m is None: 116*1596Szelenkov@nginx.com _print_log() 117*1596Szelenkov@nginx.com exit("Unit is writing log too long") 118*1596Szelenkov@nginx.com 119*1596Szelenkov@nginx.com # discover available modules from unit.log 120*1596Szelenkov@nginx.com 121*1596Szelenkov@nginx.com for module in re.findall(r'module: ([a-zA-Z]+) (.*) ".*"$', log, re.M): 122*1596Szelenkov@nginx.com if module[0] not in option.available['modules']: 123*1596Szelenkov@nginx.com option.available['modules'][module[0]] = [module[1]] 124*1596Szelenkov@nginx.com else: 125*1596Szelenkov@nginx.com option.available['modules'][module[0]].append(module[1]) 126*1596Szelenkov@nginx.com 127*1596Szelenkov@nginx.com unit_stop() 128*1596Szelenkov@nginx.com 129*1596Szelenkov@nginx.com 130*1596Szelenkov@nginx.comdef setup_method(self): 131*1596Szelenkov@nginx.com option.skip_alerts = [ 132*1596Szelenkov@nginx.com r'read signalfd\(4\) failed', 133*1596Szelenkov@nginx.com r'sendmsg.+failed', 134*1596Szelenkov@nginx.com r'recvmsg.+failed', 135*1596Szelenkov@nginx.com ] 136*1596Szelenkov@nginx.com option.skip_sanitizer = False 137*1596Szelenkov@nginx.com 138*1596Szelenkov@nginx.comdef unit_run(): 139*1596Szelenkov@nginx.com global unit_instance 140*1596Szelenkov@nginx.com build_dir = option.current_dir + '/build' 141*1596Szelenkov@nginx.com unitd = build_dir + '/unitd' 142*1596Szelenkov@nginx.com 143*1596Szelenkov@nginx.com if not os.path.isfile(unitd): 144*1596Szelenkov@nginx.com exit('Could not find unit') 145*1596Szelenkov@nginx.com 146*1596Szelenkov@nginx.com temp_dir = tempfile.mkdtemp(prefix='unit-test-') 147*1596Szelenkov@nginx.com public_dir(temp_dir) 148*1596Szelenkov@nginx.com 149*1596Szelenkov@nginx.com if oct(stat.S_IMODE(os.stat(build_dir).st_mode)) != '0o777': 150*1596Szelenkov@nginx.com public_dir(build_dir) 151*1596Szelenkov@nginx.com 152*1596Szelenkov@nginx.com os.mkdir(temp_dir + '/state') 153*1596Szelenkov@nginx.com 154*1596Szelenkov@nginx.com with open(temp_dir + '/unit.log', 'w') as log: 155*1596Szelenkov@nginx.com unit_instance['process'] = subprocess.Popen( 156*1596Szelenkov@nginx.com [ 157*1596Szelenkov@nginx.com unitd, 158*1596Szelenkov@nginx.com '--no-daemon', 159*1596Szelenkov@nginx.com '--modules', 160*1596Szelenkov@nginx.com build_dir, 161*1596Szelenkov@nginx.com '--state', 162*1596Szelenkov@nginx.com temp_dir + '/state', 163*1596Szelenkov@nginx.com '--pid', 164*1596Szelenkov@nginx.com temp_dir + '/unit.pid', 165*1596Szelenkov@nginx.com '--log', 166*1596Szelenkov@nginx.com temp_dir + '/unit.log', 167*1596Szelenkov@nginx.com '--control', 168*1596Szelenkov@nginx.com 'unix:' + temp_dir + '/control.unit.sock', 169*1596Szelenkov@nginx.com '--tmp', 170*1596Szelenkov@nginx.com temp_dir, 171*1596Szelenkov@nginx.com ], 172*1596Szelenkov@nginx.com stderr=log, 173*1596Szelenkov@nginx.com ) 174*1596Szelenkov@nginx.com 175*1596Szelenkov@nginx.com if not waitforfiles(temp_dir + '/control.unit.sock'): 176*1596Szelenkov@nginx.com _print_log() 177*1596Szelenkov@nginx.com exit('Could not start unit') 178*1596Szelenkov@nginx.com 179*1596Szelenkov@nginx.com # dumb (TODO: remove) 180*1596Szelenkov@nginx.com option.skip_alerts = [ 181*1596Szelenkov@nginx.com r'read signalfd\(4\) failed', 182*1596Szelenkov@nginx.com r'sendmsg.+failed', 183*1596Szelenkov@nginx.com r'recvmsg.+failed', 184*1596Szelenkov@nginx.com ] 185*1596Szelenkov@nginx.com option.skip_sanitizer = False 186*1596Szelenkov@nginx.com 187*1596Szelenkov@nginx.com unit_instance['temp_dir'] = temp_dir 188*1596Szelenkov@nginx.com unit_instance['log'] = temp_dir + '/unit.log' 189*1596Szelenkov@nginx.com unit_instance['control_sock'] = temp_dir + '/control.unit.sock' 190*1596Szelenkov@nginx.com unit_instance['unitd'] = unitd 191*1596Szelenkov@nginx.com 192*1596Szelenkov@nginx.com return unit_instance 193*1596Szelenkov@nginx.com 194*1596Szelenkov@nginx.com 195*1596Szelenkov@nginx.comdef unit_stop(): 196*1596Szelenkov@nginx.com p = unit_instance['process'] 197*1596Szelenkov@nginx.com 198*1596Szelenkov@nginx.com if p.poll() is not None: 199*1596Szelenkov@nginx.com return 200*1596Szelenkov@nginx.com 201*1596Szelenkov@nginx.com p.send_signal(signal.SIGQUIT) 202*1596Szelenkov@nginx.com 203*1596Szelenkov@nginx.com try: 204*1596Szelenkov@nginx.com retcode = p.wait(15) 205*1596Szelenkov@nginx.com if retcode: 206*1596Szelenkov@nginx.com return 'Child process terminated with code ' + str(retcode) 207*1596Szelenkov@nginx.com except: 208*1596Szelenkov@nginx.com p.kill() 209*1596Szelenkov@nginx.com return 'Could not terminate unit' 210*1596Szelenkov@nginx.com 211*1596Szelenkov@nginx.com 212*1596Szelenkov@nginx.comdef public_dir(path): 213*1596Szelenkov@nginx.com os.chmod(path, 0o777) 214*1596Szelenkov@nginx.com 215*1596Szelenkov@nginx.com for root, dirs, files in os.walk(path): 216*1596Szelenkov@nginx.com for d in dirs: 217*1596Szelenkov@nginx.com os.chmod(os.path.join(root, d), 0o777) 218*1596Szelenkov@nginx.com for f in files: 219*1596Szelenkov@nginx.com os.chmod(os.path.join(root, f), 0o777) 220*1596Szelenkov@nginx.com 221*1596Szelenkov@nginx.comdef waitforfiles(*files): 222*1596Szelenkov@nginx.com for i in range(50): 223*1596Szelenkov@nginx.com wait = False 224*1596Szelenkov@nginx.com ret = False 225*1596Szelenkov@nginx.com 226*1596Szelenkov@nginx.com for f in files: 227*1596Szelenkov@nginx.com if not os.path.exists(f): 228*1596Szelenkov@nginx.com wait = True 229*1596Szelenkov@nginx.com break 230*1596Szelenkov@nginx.com 231*1596Szelenkov@nginx.com if wait: 232*1596Szelenkov@nginx.com time.sleep(0.1) 233*1596Szelenkov@nginx.com 234*1596Szelenkov@nginx.com else: 235*1596Szelenkov@nginx.com ret = True 236*1596Szelenkov@nginx.com break 237*1596Szelenkov@nginx.com 238*1596Szelenkov@nginx.com return ret 239*1596Szelenkov@nginx.com 240*1596Szelenkov@nginx.com 241*1596Szelenkov@nginx.comdef skip_alert(*alerts): 242*1596Szelenkov@nginx.com option.skip_alerts.extend(alerts) 243*1596Szelenkov@nginx.com 244*1596Szelenkov@nginx.com 245*1596Szelenkov@nginx.comdef _check_alerts(log): 246*1596Szelenkov@nginx.com found = False 247*1596Szelenkov@nginx.com 248*1596Szelenkov@nginx.com alerts = re.findall(r'.+\[alert\].+', log) 249*1596Szelenkov@nginx.com 250*1596Szelenkov@nginx.com if alerts: 251*1596Szelenkov@nginx.com print('All alerts/sanitizer errors found in log:') 252*1596Szelenkov@nginx.com [print(alert) for alert in alerts] 253*1596Szelenkov@nginx.com found = True 254*1596Szelenkov@nginx.com 255*1596Szelenkov@nginx.com if option.skip_alerts: 256*1596Szelenkov@nginx.com for skip in option.skip_alerts: 257*1596Szelenkov@nginx.com alerts = [al for al in alerts if re.search(skip, al) is None] 258*1596Szelenkov@nginx.com 259*1596Szelenkov@nginx.com if alerts: 260*1596Szelenkov@nginx.com _print_log(log) 261*1596Szelenkov@nginx.com assert not alerts, 'alert(s)' 262*1596Szelenkov@nginx.com 263*1596Szelenkov@nginx.com if not option.skip_sanitizer: 264*1596Szelenkov@nginx.com sanitizer_errors = re.findall('.+Sanitizer.+', log) 265*1596Szelenkov@nginx.com 266*1596Szelenkov@nginx.com if sanitizer_errors: 267*1596Szelenkov@nginx.com _print_log(log) 268*1596Szelenkov@nginx.com assert not sanitizer_errors, 'sanitizer error(s)' 269*1596Szelenkov@nginx.com 270*1596Szelenkov@nginx.com if found: 271*1596Szelenkov@nginx.com print('skipped.') 272*1596Szelenkov@nginx.com 273*1596Szelenkov@nginx.com 274*1596Szelenkov@nginx.comdef _print_log(data=None): 275*1596Szelenkov@nginx.com unit_log = unit_instance['log'] 276*1596Szelenkov@nginx.com 277*1596Szelenkov@nginx.com print('Path to unit.log:\n' + unit_log + '\n') 278*1596Szelenkov@nginx.com 279*1596Szelenkov@nginx.com if option.print_log: 280*1596Szelenkov@nginx.com os.set_blocking(sys.stdout.fileno(), True) 281*1596Szelenkov@nginx.com sys.stdout.flush() 282*1596Szelenkov@nginx.com 283*1596Szelenkov@nginx.com if data is None: 284*1596Szelenkov@nginx.com with open(unit_log, 'r', encoding='utf-8', errors='ignore') as f: 285*1596Szelenkov@nginx.com shutil.copyfileobj(f, sys.stdout) 286*1596Szelenkov@nginx.com else: 287*1596Szelenkov@nginx.com sys.stdout.write(data) 288*1596Szelenkov@nginx.com 289*1596Szelenkov@nginx.com 290*1596Szelenkov@nginx.com@pytest.fixture 291*1596Szelenkov@nginx.comdef is_unsafe(request): 292*1596Szelenkov@nginx.com return request.config.getoption("--unsafe") 293*1596Szelenkov@nginx.com 294*1596Szelenkov@nginx.com@pytest.fixture 295*1596Szelenkov@nginx.comdef is_su(request): 296*1596Szelenkov@nginx.com return os.geteuid() == 0 297*1596Szelenkov@nginx.com 298*1596Szelenkov@nginx.comdef pytest_sessionfinish(session): 299*1596Szelenkov@nginx.com unit_stop() 300