xref: /unit/test/test_php_application.py (revision 2616:ab2896c980ab)
1import getpass
2import os
3import re
4import shutil
5import signal
6import time
7from pathlib import Path
8
9import pytest
10
11from unit.applications.lang.php import ApplicationPHP
12from unit.option import option
13
14prerequisites = {'modules': {'php': 'all'}}
15
16client = ApplicationPHP()
17
18
19def before_disable_functions():
20    body = client.get()['body']
21
22    assert re.search(r'time: \d+', body), 'disable_functions before time'
23    assert re.search(r'exec: \/\w+', body), 'disable_functions before exec'
24
25
26def check_opcache():
27    resp = client.get()
28    assert resp['status'] == 200, 'status'
29
30    headers = resp['headers']
31    if 'X-OPcache' in headers and headers['X-OPcache'] == '-1':
32        pytest.skip('opcache is not supported')
33
34    return resp
35
36
37def run_php_application_cwd_root_tests():
38    assert 'success' in client.conf_delete('applications/cwd/working_directory')
39
40    script_cwd = f'{option.test_dir}/php/cwd'
41
42    resp = client.get()
43    assert resp['status'] == 200, 'status ok'
44    assert resp['body'] == script_cwd, 'default cwd'
45
46    assert 'success' in client.conf(
47        f'"{option.test_dir}"',
48        'applications/cwd/working_directory',
49    )
50
51    resp = client.get()
52    assert resp['status'] == 200, 'status ok'
53    assert resp['body'] == script_cwd, 'wdir cwd'
54
55    resp = client.get(url='/?chdir=/')
56    assert resp['status'] == 200, 'status ok'
57    assert resp['body'] == '/', 'cwd after chdir'
58
59    # cwd must be restored
60
61    resp = client.get()
62    assert resp['status'] == 200, 'status ok'
63    assert resp['body'] == script_cwd, 'cwd restored'
64
65    resp = client.get(url='/subdir/')
66    assert resp['body'] == f'{script_cwd}/subdir', 'cwd subdir'
67
68
69def run_php_application_cwd_script_tests():
70    client.load('cwd')
71
72    script_cwd = f'{option.test_dir}/php/cwd'
73
74    assert 'success' in client.conf_delete('applications/cwd/working_directory')
75
76    assert 'success' in client.conf('"index.php"', 'applications/cwd/script')
77
78    assert client.get()['body'] == script_cwd, 'default cwd'
79
80    assert client.get(url='/?chdir=/')['body'] == '/', 'cwd after chdir'
81
82    # cwd must be restored
83    assert client.get()['body'] == script_cwd, 'cwd restored'
84
85
86def set_opcache(app, val):
87    assert 'success' in client.conf(
88        {"admin": {"opcache.enable": val, "opcache.enable_cli": val}},
89        f'applications/{app}/options',
90    )
91
92    r = check_opcache()
93    assert r['headers']['X-OPcache'] == val, 'opcache value'
94
95
96def set_preload(preload):
97    Path(f'{option.temp_dir}/php.ini').write_text(
98        f"""opcache.preload = {option.test_dir}/php/opcache/preload\
99/{preload}
100opcache.preload_user = {option.user or getpass.getuser()}
101""",
102        encoding='utf-8',
103    )
104
105    assert 'success' in client.conf(
106        {"file": f"{option.temp_dir}/php.ini"},
107        'applications/opcache/options',
108    )
109
110
111def test_php_application_variables(date_to_sec_epoch, sec_epoch):
112    client.load('variables')
113
114    body = 'Test body string.'
115
116    resp = client.post(
117        headers={
118            'Host': 'localhost',
119            'Content-Type': 'text/html',
120            'Custom-Header': 'blah',
121            'Connection': 'close',
122        },
123        body=body,
124        url='/index.php/blah?var=val',
125    )
126
127    assert resp['status'] == 200, 'status'
128    headers = resp['headers']
129    header_server = headers.pop('Server')
130    assert re.search(r'Unit/[\d\.]+', header_server), 'server header'
131    assert (
132        headers.pop('Server-Software') == header_server
133    ), 'server software header'
134
135    date = headers.pop('Date')
136    assert date[-4:] == ' GMT', 'date header timezone'
137    assert abs(date_to_sec_epoch(date) - sec_epoch) < 5, 'date header'
138
139    if 'X-Powered-By' in headers:
140        headers.pop('X-Powered-By')
141
142    headers.pop('Content-type')
143    assert headers == {
144        'Connection': 'close',
145        'Content-Length': str(len(body)),
146        'Request-Method': 'POST',
147        'Path-Info': '/blah',
148        'Request-Uri': '/index.php/blah?var=val',
149        'Http-Host': 'localhost',
150        'Server-Protocol': 'HTTP/1.1',
151        'Custom-Header': 'blah',
152    }, 'headers'
153    assert resp['body'] == body, 'body'
154
155
156def test_php_application_query_string():
157    client.load('query_string')
158
159    resp = client.get(url='/?var1=val1&var2=val2')
160
161    assert (
162        resp['headers']['Query-String'] == 'var1=val1&var2=val2'
163    ), 'query string'
164
165
166def test_php_application_query_string_empty():
167    client.load('query_string')
168
169    resp = client.get(url='/?')
170
171    assert resp['status'] == 200, 'query string empty status'
172    assert resp['headers']['Query-String'] == '', 'query string empty'
173
174
175def test_php_application_query_string_rewrite():
176    assert 'success' in client.conf(
177        {
178            "listeners": {"*:8080": {"pass": "routes"}},
179            "routes": [
180                {
181                    "action": {
182                        "rewrite": "/new",
183                        "pass": "applications/query_string",
184                    },
185                },
186            ],
187            "applications": {
188                "query_string": {
189                    "type": client.get_application_type(),
190                    "processes": {"spare": 0},
191                    "root": f"{option.test_dir}/php/query_string",
192                    "script": "index.php",
193                }
194            },
195        },
196    )
197
198    assert client.get(url='/old')['status'] == 200
199
200    resp = client.get(url='/old?arg=val')
201    assert resp['status'] == 200
202    assert resp['headers']['Query-String'] == 'arg=val'
203
204
205def test_php_application_fastcgi_finish_request(findall, unit_pid):
206    client.load('fastcgi_finish_request')
207
208    assert 'success' in client.conf(
209        {"admin": {"auto_globals_jit": "1"}},
210        'applications/fastcgi_finish_request/options',
211    )
212
213    assert client.get()['body'] == '0123'
214
215    os.kill(unit_pid, signal.SIGUSR1)
216
217    errs = findall(r'Error in fastcgi_finish_request')
218
219    assert len(errs) == 0, 'no error'
220
221
222def test_php_application_fastcgi_finish_request_2(findall, unit_pid):
223    client.load('fastcgi_finish_request')
224
225    assert 'success' in client.conf(
226        {"admin": {"auto_globals_jit": "1"}},
227        'applications/fastcgi_finish_request/options',
228    )
229
230    resp = client.get(url='/?skip')
231    assert resp['status'] == 200
232    assert resp['body'] == ''
233
234    os.kill(unit_pid, signal.SIGUSR1)
235
236    errs = findall(r'Error in fastcgi_finish_request')
237
238    assert len(errs) == 0, 'no error'
239
240
241def test_php_application_query_string_absent():
242    client.load('query_string')
243
244    resp = client.get()
245
246    assert resp['status'] == 200, 'query string absent status'
247    assert resp['headers']['Query-String'] == '', 'query string absent'
248
249
250def test_php_application_phpinfo():
251    client.load('phpinfo')
252
253    resp = client.get()
254
255    assert resp['status'] == 200, 'status'
256    assert resp['body'] != '', 'body not empty'
257
258
259def test_php_application_header_status():
260    client.load('header')
261
262    assert (
263        client.get(
264            headers={
265                'Host': 'localhost',
266                'Connection': 'close',
267                'X-Header': 'HTTP/1.1 404 Not Found',
268            }
269        )['status']
270        == 404
271    ), 'status'
272
273    assert (
274        client.get(
275            headers={
276                'Host': 'localhost',
277                'Connection': 'close',
278                'X-Header': 'http/1.1 404 Not Found',
279            }
280        )['status']
281        == 404
282    ), 'status case insensitive'
283
284    assert (
285        client.get(
286            headers={
287                'Host': 'localhost',
288                'Connection': 'close',
289                'X-Header': 'HTTP/ 404 Not Found',
290            }
291        )['status']
292        == 404
293    ), 'status version empty'
294
295
296def test_php_application_404():
297    client.load('404')
298
299    resp = client.get()
300
301    assert resp['status'] == 404, '404 status'
302    assert re.search(r'<title>404 Not Found</title>', resp['body']), '404 body'
303
304
305def test_php_application_keepalive_body():
306    client.load('mirror')
307
308    assert client.get()['status'] == 200, 'init'
309
310    body = '0123456789' * 500
311    (resp, sock) = client.post(
312        headers={
313            'Host': 'localhost',
314            'Connection': 'keep-alive',
315        },
316        start=True,
317        body=body,
318        read_timeout=1,
319    )
320
321    assert resp['body'] == body, 'keep-alive 1'
322
323    body = '0123456789'
324    resp = client.post(sock=sock, body=body)
325
326    assert resp['body'] == body, 'keep-alive 2'
327
328
329def test_php_application_conditional():
330    client.load('conditional')
331
332    assert re.search(r'True', client.get()['body']), 'conditional true'
333    assert re.search(r'False', client.post()['body']), 'conditional false'
334
335
336def test_php_application_get_variables():
337    client.load('get_variables')
338
339    resp = client.get(url='/?var1=val1&var2=&var3')
340    assert resp['headers']['X-Var-1'] == 'val1', 'GET variables'
341    assert resp['headers']['X-Var-2'] == '', 'GET variables 2'
342    assert resp['headers']['X-Var-3'] == '', 'GET variables 3'
343    assert resp['headers']['X-Var-4'] == 'not set', 'GET variables 4'
344
345
346def test_php_application_post_variables():
347    client.load('post_variables')
348
349    resp = client.post(
350        headers={
351            'Content-Type': 'application/x-www-form-urlencoded',
352            'Host': 'localhost',
353            'Connection': 'close',
354        },
355        body='var1=val1&var2=',
356    )
357    assert resp['headers']['X-Var-1'] == 'val1', 'POST variables'
358    assert resp['headers']['X-Var-2'] == '', 'POST variables 2'
359    assert resp['headers']['X-Var-3'] == 'not set', 'POST variables 3'
360
361
362def test_php_application_cookies():
363    client.load('cookies')
364
365    resp = client.get(
366        headers={
367            'Cookie': 'var=val; var2=val2',
368            'Host': 'localhost',
369            'Connection': 'close',
370        }
371    )
372
373    assert resp['headers']['X-Cookie-1'] == 'val', 'cookie'
374    assert resp['headers']['X-Cookie-2'] == 'val2', 'cookie'
375
376
377def test_php_application_ini_precision():
378    client.load('ini_precision')
379
380    assert client.get()['headers']['X-Precision'] != '4', 'ini value default'
381
382    assert 'success' in client.conf(
383        {"file": "ini/php.ini"}, 'applications/ini_precision/options'
384    )
385
386    assert (
387        client.get()['headers']['X-File']
388        == f'{option.test_dir}/php/ini_precision/ini/php.ini'
389    ), 'ini file'
390    assert client.get()['headers']['X-Precision'] == '4', 'ini value'
391
392
393@pytest.mark.skip('not yet')
394def test_php_application_ini_admin_user():
395    client.load('ini_precision')
396
397    assert 'error' in client.conf(
398        {"user": {"precision": "4"}, "admin": {"precision": "5"}},
399        'applications/ini_precision/options',
400    ), 'ini admin user'
401
402
403def test_php_application_ini_admin():
404    client.load('ini_precision')
405
406    assert 'success' in client.conf(
407        {"file": "ini/php.ini", "admin": {"precision": "5"}},
408        'applications/ini_precision/options',
409    )
410
411    assert (
412        client.get()['headers']['X-File']
413        == f'{option.test_dir}/php/ini_precision/ini/php.ini'
414    ), 'ini file'
415    assert client.get()['headers']['X-Precision'] == '5', 'ini value admin'
416
417
418def test_php_application_ini_user():
419    client.load('ini_precision')
420
421    assert 'success' in client.conf(
422        {"file": "ini/php.ini", "user": {"precision": "5"}},
423        'applications/ini_precision/options',
424    )
425
426    assert (
427        client.get()['headers']['X-File']
428        == f'{option.test_dir}/php/ini_precision/ini/php.ini'
429    ), 'ini file'
430    assert client.get()['headers']['X-Precision'] == '5', 'ini value user'
431
432
433def test_php_application_ini_user_2():
434    client.load('ini_precision')
435
436    assert 'success' in client.conf(
437        {"file": "ini/php.ini"}, 'applications/ini_precision/options'
438    )
439
440    assert client.get()['headers']['X-Precision'] == '4', 'ini user file'
441
442    assert 'success' in client.conf(
443        {"precision": "5"}, 'applications/ini_precision/options/user'
444    )
445
446    assert client.get()['headers']['X-Precision'] == '5', 'ini value user'
447
448
449def test_php_application_ini_set_admin():
450    client.load('ini_precision')
451
452    assert 'success' in client.conf(
453        {"admin": {"precision": "5"}}, 'applications/ini_precision/options'
454    )
455
456    assert (
457        client.get(url='/?precision=6')['headers']['X-Precision'] == '5'
458    ), 'ini set admin'
459
460
461def test_php_application_ini_set_user():
462    client.load('ini_precision')
463
464    assert 'success' in client.conf(
465        {"user": {"precision": "5"}}, 'applications/ini_precision/options'
466    )
467
468    assert (
469        client.get(url='/?precision=6')['headers']['X-Precision'] == '6'
470    ), 'ini set user'
471
472
473def test_php_application_ini_repeat():
474    client.load('ini_precision')
475
476    assert 'success' in client.conf(
477        {"user": {"precision": "5"}}, 'applications/ini_precision/options'
478    )
479
480    assert client.get()['headers']['X-Precision'] == '5', 'ini value'
481
482    assert client.get()['headers']['X-Precision'] == '5', 'ini value repeat'
483
484
485def test_php_application_disable_functions_exec():
486    client.load('time_exec')
487
488    before_disable_functions()
489
490    assert 'success' in client.conf(
491        {"admin": {"disable_functions": "exec"}},
492        'applications/time_exec/options',
493    )
494
495    body = client.get()['body']
496
497    assert re.search(r'time: \d+', body), 'disable_functions time'
498    assert not re.search(r'exec: \/\w+', body), 'disable_functions exec'
499
500
501def test_php_application_disable_functions_comma():
502    client.load('time_exec')
503
504    before_disable_functions()
505
506    assert 'success' in client.conf(
507        {"admin": {"disable_functions": "exec,time"}},
508        'applications/time_exec/options',
509    )
510
511    body = client.get()['body']
512
513    assert not re.search(r'time: \d+', body), 'disable_functions comma time'
514    assert not re.search(r'exec: \/\w+', body), 'disable_functions comma exec'
515
516
517def test_php_application_auth():
518    client.load('auth')
519
520    resp = client.get()
521    assert resp['status'] == 200, 'status'
522    assert resp['headers']['X-Digest'] == 'not set', 'digest'
523    assert resp['headers']['X-User'] == 'not set', 'user'
524    assert resp['headers']['X-Password'] == 'not set', 'password'
525
526    resp = client.get(
527        headers={
528            'Host': 'localhost',
529            'Authorization': 'Basic dXNlcjpwYXNzd29yZA==',
530            'Connection': 'close',
531        }
532    )
533    assert resp['status'] == 200, 'basic status'
534    assert resp['headers']['X-Digest'] == 'not set', 'basic digest'
535    assert resp['headers']['X-User'] == 'user', 'basic user'
536    assert resp['headers']['X-Password'] == 'password', 'basic password'
537
538    resp = client.get(
539        headers={
540            'Host': 'localhost',
541            'Authorization': 'Digest username="blah", realm="", uri="/"',
542            'Connection': 'close',
543        }
544    )
545    assert resp['status'] == 200, 'digest status'
546    assert (
547        resp['headers']['X-Digest'] == 'username="blah", realm="", uri="/"'
548    ), 'digest digest'
549    assert resp['headers']['X-User'] == 'not set', 'digest user'
550    assert resp['headers']['X-Password'] == 'not set', 'digest password'
551
552
553def test_php_application_auth_invalid():
554    client.load('auth')
555
556    def check_auth(auth):
557        resp = client.get(
558            headers={
559                'Host': 'localhost',
560                'Authorization': auth,
561                'Connection': 'close',
562            }
563        )
564
565        assert resp['status'] == 200, 'status'
566        assert resp['headers']['X-Digest'] == 'not set', 'Digest'
567        assert resp['headers']['X-User'] == 'not set', 'User'
568        assert resp['headers']['X-Password'] == 'not set', 'Password'
569
570    check_auth('Basic dXN%cjpwYXNzd29yZA==')
571    check_auth('Basic XNlcjpwYXNzd29yZA==')
572    check_auth('Basic DdXNlcjpwYXNzd29yZA==')
573    check_auth('Basic blah')
574    check_auth('Basic')
575    check_auth('Digest')
576    check_auth('blah')
577
578
579def test_php_application_disable_functions_space():
580    client.load('time_exec')
581
582    before_disable_functions()
583
584    assert 'success' in client.conf(
585        {"admin": {"disable_functions": "exec time"}},
586        'applications/time_exec/options',
587    )
588
589    body = client.get()['body']
590
591    assert not re.search(r'time: \d+', body), 'disable_functions space time'
592    assert not re.search(r'exec: \/\w+', body), 'disable_functions space exec'
593
594
595def test_php_application_disable_functions_user():
596    client.load('time_exec')
597
598    before_disable_functions()
599
600    assert 'success' in client.conf(
601        {"user": {"disable_functions": "exec"}},
602        'applications/time_exec/options',
603    )
604
605    body = client.get()['body']
606
607    assert re.search(r'time: \d+', body), 'disable_functions user time'
608    assert not re.search(r'exec: \/\w+', body), 'disable_functions user exec'
609
610
611def test_php_application_disable_functions_nonexistent():
612    client.load('time_exec')
613
614    before_disable_functions()
615
616    assert 'success' in client.conf(
617        {"admin": {"disable_functions": "blah"}},
618        'applications/time_exec/options',
619    )
620
621    body = client.get()['body']
622
623    assert re.search(r'time: \d+', body), 'disable_functions nonexistent time'
624    assert re.search(r'exec: \/\w+', body), 'disable_functions nonexistent exec'
625
626
627def test_php_application_disable_classes():
628    client.load('date_time')
629
630    assert re.search(r'012345', client.get()['body']), 'disable_classes before'
631
632    assert 'success' in client.conf(
633        {"admin": {"disable_classes": "DateTime"}},
634        'applications/date_time/options',
635    )
636
637    assert not re.search(
638        r'012345', client.get()['body']
639    ), 'disable_classes before'
640
641
642def test_php_application_disable_classes_user():
643    client.load('date_time')
644
645    assert re.search(r'012345', client.get()['body']), 'disable_classes before'
646
647    assert 'success' in client.conf(
648        {"user": {"disable_classes": "DateTime"}},
649        'applications/date_time/options',
650    )
651
652    assert not re.search(
653        r'012345', client.get()['body']
654    ), 'disable_classes before'
655
656
657def test_php_application_error_log(findall, wait_for_record):
658    client.load('error_log')
659
660    assert client.get()['status'] == 200, 'status'
661
662    time.sleep(1)
663
664    assert client.get()['status'] == 200, 'status 2'
665
666    pattern = r'\d{4}\/\d\d\/\d\d\s\d\d:.+\[notice\].+Error in application'
667
668    assert wait_for_record(pattern) is not None, 'errors print'
669
670    errs = findall(pattern)
671
672    assert len(errs) == 2, 'error_log count'
673
674    date = errs[0].split('[')[0]
675    date2 = errs[1].split('[')[0]
676    assert date != date2, 'date diff'
677
678
679def test_php_application_script():
680    assert 'success' in client.conf(
681        {
682            "listeners": {"*:8080": {"pass": "applications/script"}},
683            "applications": {
684                "script": {
685                    "type": client.get_application_type(),
686                    "processes": {"spare": 0},
687                    "root": f"{option.test_dir}/php/script",
688                    "script": "phpinfo.php",
689                }
690            },
691        }
692    ), 'configure script'
693
694    resp = client.get()
695
696    assert resp['status'] == 200, 'status'
697    assert resp['body'] != '', 'body not empty'
698
699
700def test_php_application_index_default():
701    assert 'success' in client.conf(
702        {
703            "listeners": {"*:8080": {"pass": "applications/phpinfo"}},
704            "applications": {
705                "phpinfo": {
706                    "type": client.get_application_type(),
707                    "processes": {"spare": 0},
708                    "root": f"{option.test_dir}/php/phpinfo",
709                }
710            },
711        }
712    ), 'configure index default'
713
714    resp = client.get()
715
716    assert resp['status'] == 200, 'status'
717    assert resp['body'] != '', 'body not empty'
718
719
720def test_php_application_trailing_slash(temp_dir):
721    new_root = f'{temp_dir}/php-root'
722
723    Path(f'{new_root}/path').mkdir(parents=True)
724    Path(f'{new_root}/path/index.php').write_text(
725        '<?php echo "OK\n"; ?>', encoding='utf-8'
726    )
727
728    addr = f'{temp_dir}/sock'
729
730    assert 'success' in client.conf(
731        {
732            "listeners": {
733                "*:8080": {"pass": "applications/php-path"},
734                f'unix:{addr}': {"pass": "applications/php-path"},
735            },
736            "applications": {
737                "php-path": {
738                    "type": client.get_application_type(),
739                    "processes": {"spare": 0},
740                    "root": new_root,
741                }
742            },
743        }
744    ), 'configure trailing slash'
745
746    assert client.get(url='/path/')['status'] == 200, 'uri with trailing /'
747
748    resp = client.get(url='/path?q=a')
749    assert resp['status'] == 301, 'uri without trailing /'
750    assert (
751        resp['headers']['Location'] == 'http://localhost:8080/path/?q=a'
752    ), 'Location with query string'
753
754    resp = client.get(
755        sock_type='unix',
756        addr=addr,
757        url='/path',
758        headers={'Host': 'foo', 'Connection': 'close'},
759    )
760    assert resp['status'] == 301, 'uri without trailing /'
761    assert (
762        resp['headers']['Location'] == 'http://foo/path/'
763    ), 'Location with custom Host over UDS'
764
765
766def test_php_application_forbidden(temp_dir):
767    Path(f'{temp_dir}/php-root/path').mkdir(mode=0o000, parents=True)
768
769    assert 'success' in client.conf(
770        {
771            "listeners": {"*:8080": {"pass": "applications/php-path"}},
772            "applications": {
773                "php-path": {
774                    "type": client.get_application_type(),
775                    "processes": {"spare": 0},
776                    "root": f'{temp_dir}/php-root',
777                }
778            },
779        }
780    ), 'forbidden directory'
781
782    assert client.get(url='/path/')['status'] == 403, 'access forbidden'
783
784
785def test_php_application_extension_check(temp_dir):
786    client.load('phpinfo')
787
788    assert client.get(url='/index.wrong')['status'] != 200, 'status'
789
790    new_root = f'{temp_dir}/php'
791    Path(new_root).mkdir(parents=True)
792    shutil.copy(f'{option.test_dir}/php/phpinfo/index.wrong', new_root)
793
794    assert 'success' in client.conf(
795        {
796            "listeners": {"*:8080": {"pass": "applications/phpinfo"}},
797            "applications": {
798                "phpinfo": {
799                    "type": client.get_application_type(),
800                    "processes": {"spare": 0},
801                    "root": new_root,
802                    "working_directory": new_root,
803                }
804            },
805        }
806    ), 'configure new root'
807
808    resp = client.get()
809    assert f'{resp["status"]}{resp["body"]}' != '200', 'status new root'
810
811
812def test_php_application_cwd_root():
813    client.load('cwd')
814    run_php_application_cwd_root_tests()
815
816
817def test_php_application_cwd_opcache_disabled():
818    client.load('cwd')
819    set_opcache('cwd', '0')
820    run_php_application_cwd_root_tests()
821
822
823def test_php_application_cwd_opcache_enabled():
824    client.load('cwd')
825    set_opcache('cwd', '1')
826    run_php_application_cwd_root_tests()
827
828
829def test_php_application_cwd_script():
830    client.load('cwd')
831    run_php_application_cwd_script_tests()
832
833
834def test_php_application_cwd_script_opcache_disabled():
835    client.load('cwd')
836    set_opcache('cwd', '0')
837    run_php_application_cwd_script_tests()
838
839
840def test_php_application_cwd_script_opcache_enabled():
841    client.load('cwd')
842    set_opcache('cwd', '1')
843    run_php_application_cwd_script_tests()
844
845
846def test_php_application_path_relative():
847    client.load('open')
848
849    assert client.get()['body'] == 'test', 'relative path'
850
851    assert (
852        client.get(url='/?chdir=/')['body'] != 'test'
853    ), 'relative path w/ chdir'
854
855    assert client.get()['body'] == 'test', 'relative path 2'
856
857
858def test_php_application_shared_opcache():
859    client.load('opcache', limits={'requests': 1})
860
861    r = check_opcache()
862    pid = r['headers']['X-Pid']
863    assert r['headers']['X-Cached'] == '0', 'not cached'
864
865    r = client.get()
866
867    assert r['headers']['X-Pid'] != pid, 'new instance'
868    assert r['headers']['X-Cached'] == '1', 'cached'
869
870
871def test_php_application_opcache_preload_chdir():
872    client.load('opcache')
873
874    check_opcache()
875
876    set_preload('chdir.php')
877
878    assert client.get()['headers']['X-Cached'] == '0', 'not cached'
879    assert client.get()['headers']['X-Cached'] == '1', 'cached'
880
881
882def test_php_application_opcache_preload_ffr():
883    client.load('opcache')
884
885    check_opcache()
886
887    set_preload('fastcgi_finish_request.php')
888
889    assert client.get()['headers']['X-Cached'] == '0', 'not cached'
890    assert client.get()['headers']['X-Cached'] == '1', 'cached'
891