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