xref: /unit/test/conftest.py (revision 1705)
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