Tests: prerequisites checking reworked.

Prerequisites check moved to the module level to simplify class structure.
Discovery and prerequisites checks functions moved to the separate files.
Introduced "require" fixture to provide per-test requirements check.
This commit is contained in:
Andrei Zeliankou
2023-06-12 14:16:59 +01:00
parent a3b9b49cfb
commit ce2405ec3d
79 changed files with 524 additions and 548 deletions

View File

@@ -13,14 +13,8 @@ import time
from multiprocessing import Process
import pytest
from unit.check.chroot import check_chroot
from unit.check.go import check_go
from unit.check.isolation import check_isolation
from unit.check.njs import check_njs
from unit.check.node import check_node
from unit.check.regex import check_regex
from unit.check.tls import check_openssl
from unit.check.unix_abstract import check_unix_abstract
from unit.check.discover_available import discover_available
from unit.check.check_prerequisites import check_prerequisites
from unit.http import TestHTTP
from unit.log import Log
from unit.log import print_log_on_assert
@@ -130,6 +124,9 @@ def pytest_generate_tests(metafunc):
type = cls.application_type
def generate_tests(versions):
if not versions:
pytest.skip('no available module versions')
metafunc.fixturenames.append('tmp_ct')
metafunc.parametrize('tmp_ct', versions)
@@ -140,75 +137,43 @@ def pytest_generate_tests(metafunc):
# take available module from option and generate tests for each version
for module, prereq_version in cls.prerequisites['modules'].items():
if module in option.available['modules']:
available_versions = option.available['modules'][module]
available_modules = option.available['modules']
if prereq_version == 'all':
for module, version in metafunc.module.prerequisites['modules'].items():
if module in available_modules and available_modules[module]:
available_versions = available_modules[module]
if version == 'all':
generate_tests(available_versions)
elif prereq_version == 'any':
elif version == 'any':
option.generated_tests[
metafunc.function.__name__
] = f'{type} {available_versions[0]}'
elif callable(prereq_version):
generate_tests(list(filter(prereq_version, available_versions)))
elif callable(version):
generate_tests(list(filter(version, available_versions)))
else:
raise ValueError(
f'''
Unexpected prerequisite version "{prereq_version}" for module "{module}" in
{cls}. 'all', 'any' or callable expected.'''
Unexpected prerequisite version "{version}" for module "{module}".
'all', 'any' or callable expected.'''
)
def pytest_sessionstart():
option.available = {'modules': {}, 'features': {}}
unit = unit_run()
output_version = subprocess.check_output(
[unit['unitd'], '--version'], stderr=subprocess.STDOUT
).decode()
if not _wait_for_record(r'controller started'):
Log.print_log()
exit("Unit is writing log too long")
discover_available(unit)
# discover available modules from unit.log
for module in re.findall(
r'module: ([a-zA-Z]+) (.*) ".*"$', Log.read(), re.M
):
versions = option.available['modules'].setdefault(module[0], [])
if module[1] not in versions:
versions.append(module[1])
# discover modules from check
option.available['modules']['go'] = check_go()
option.available['modules']['njs'] = check_njs(output_version)
option.available['modules']['node'] = check_node(option.current_dir)
option.available['modules']['openssl'] = check_openssl(output_version)
option.available['modules']['regex'] = check_regex(output_version)
# remove None values
option.available['modules'] = {
k: v for k, v in option.available['modules'].items() if v is not None
}
check_chroot()
check_isolation()
check_unix_abstract()
_clear_conf(f'{unit["temp_dir"]}/control.unit.sock')
_clear_conf()
unit_stop()
Log.check_alerts()
if option.restart:
shutil.rmtree(unit_instance['temp_dir'])
shutil.rmtree(unit['temp_dir'])
else:
_clear_temp_dir()
@@ -225,38 +190,10 @@ def pytest_runtest_makereport(item):
setattr(item, f'rep_{rep.when}', rep)
@pytest.fixture(scope='class', autouse=True)
def check_prerequisites(request):
cls = request.cls
missed = []
# check modules
if 'modules' in cls.prerequisites:
available_modules = list(option.available['modules'].keys())
for module in cls.prerequisites['modules']:
if module in available_modules:
continue
missed.append(module)
if missed:
pytest.skip(f'Unit has no {", ".join(missed)} module(s)')
# check features
if 'features' in cls.prerequisites:
available_features = list(option.available['features'].keys())
for feature in cls.prerequisites['features']:
if feature in available_features:
continue
missed.append(feature)
if missed:
pytest.skip(f'{", ".join(missed)} feature(s) not supported')
@pytest.fixture(scope='module', autouse=True)
def check_prerequisites_module(request):
if hasattr(request.module, 'prerequisites'):
check_prerequisites(request.module.prerequisites)
@pytest.fixture(autouse=True)
@@ -283,7 +220,7 @@ def run(request):
# prepare log
with Log.open(encoding='utf-8') as f:
with Log.open() as f:
log = f.read()
Log.set_pos(f.tell())
@@ -294,7 +231,7 @@ def run(request):
# clean temp_dir before the next test
if not option.restart:
_clear_conf(f'{unit["temp_dir"]}/control.unit.sock', log=log)
_clear_conf(log=log)
_clear_temp_dir()
# check descriptors
@@ -384,7 +321,7 @@ def unit_run(state_dir=None):
unit_instance['pid'] = f.read().rstrip()
if state_dir is None:
_clear_conf(control_sock)
_clear_conf()
_fds_info['main']['fds'] = _count_fds(unit_instance['pid'])
@@ -440,7 +377,9 @@ def unit_stop():
@print_log_on_assert
def _clear_conf(sock, *, log=None):
def _clear_conf(*, log=None):
sock = unit_instance['control_sock']
resp = http.put(
url='/config',
sock_type='unix',
@@ -456,7 +395,10 @@ def _clear_conf(sock, *, log=None):
def delete(url):
return http.delete(url=url, sock_type='unix', addr=sock)['body']
if 'openssl' in option.available['modules']:
if (
'openssl' in option.available['modules']
and option.available['modules']['openssl']
):
try:
certs = json.loads(get('/certificates')).keys()
@@ -466,7 +408,10 @@ def _clear_conf(sock, *, log=None):
for cert in certs:
assert 'success' in delete(f'/certificates/{cert}'), 'delete cert'
if 'njs' in option.available['modules']:
if (
'njs' in option.available['modules']
and option.available['modules']['njs']
):
try:
scripts = json.loads(get('/js_modules')).keys()
@@ -621,19 +566,6 @@ def _count_fds(pid):
return 0
def _wait_for_record(pattern, name='unit.log', wait=150, flags=re.M):
with Log.open(name) as file:
for _ in range(wait):
found = re.search(pattern, file.read(), flags)
if found is not None:
break
time.sleep(0.1)
return found
def run_process(target, *args):
global _processes
@@ -696,8 +628,8 @@ def date_to_sec_epoch():
@pytest.fixture
def findall():
def _findall(pattern, name='unit.log', flags=re.M):
return re.findall(pattern, Log.read(name), flags)
def _findall(*args, **kwargs):
return Log.findall(*args, **kwargs)
return _findall
@@ -712,6 +644,11 @@ def is_unsafe(request):
return request.config.getoption("--unsafe")
@pytest.fixture
def require():
return check_prerequisites
@pytest.fixture
def search_in_file():
def _search_in_file(pattern, name='unit.log', flags=re.M):
@@ -760,4 +697,7 @@ def unit_pid():
@pytest.fixture
def wait_for_record():
def _wait_for_record(*args, **kwargs):
return Log.wait_for_record(*args, **kwargs)
return _wait_for_record

View File

@@ -4,10 +4,10 @@ import pytest
from unit.applications.lang.python import TestApplicationPython
from unit.option import option
prerequisites = {'modules': {'python': 'any'}}
class TestAccessLog(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
def load(self, script):
super().load(script)

View File

@@ -5,13 +5,12 @@ import pytest
from packaging import version
from unit.applications.lang.python import TestApplicationPython
prerequisites = {
'modules': {'python': lambda v: version.parse(v) >= version.parse('3.5')}
}
class TestASGIApplication(TestApplicationPython):
prerequisites = {
'modules': {
'python': lambda v: version.parse(v) >= version.parse('3.5')
}
}
load_module = 'asgi'
def test_asgi_application_variables(self, date_to_sec_epoch, sec_epoch):

View File

@@ -1,14 +1,13 @@
from packaging import version
from unit.applications.lang.python import TestApplicationPython
prerequisites = {
'modules': {'python': lambda v: version.parse(v) >= version.parse('3.5')},
'features': {'unix_abstract': True},
}
class TestASGIApplicationUnixAbstract(TestApplicationPython):
prerequisites = {
'modules': {
'python': lambda v: version.parse(v) >= version.parse('3.5')
},
'features': ['unix_abstract'],
}
load_module = 'asgi'
def test_asgi_application_unix_abstract(self):

View File

@@ -5,13 +5,12 @@ from packaging import version
from unit.applications.lang.python import TestApplicationPython
from unit.option import option
prerequisites = {
'modules': {'python': lambda v: version.parse(v) >= version.parse('3.5')}
}
class TestASGILifespan(TestApplicationPython):
prerequisites = {
'modules': {
'python': lambda v: version.parse(v) >= version.parse('3.5')
}
}
load_module = 'asgi'
def setup_cookies(self, prefix):

View File

@@ -3,13 +3,12 @@ from packaging import version
from unit.applications.lang.python import TestApplicationPython
from unit.option import option
prerequisites = {
'modules': {'python': lambda v: version.parse(v) >= version.parse('3.5')}
}
class TestASGITargets(TestApplicationPython):
prerequisites = {
'modules': {
'python': lambda v: version.parse(v) >= version.parse('3.5')
}
}
load_module = 'asgi'
@pytest.fixture(autouse=True)

View File

@@ -6,13 +6,12 @@ from packaging import version
from unit.applications.lang.python import TestApplicationPython
from unit.applications.websockets import TestApplicationWebsocket
prerequisites = {
'modules': {'python': lambda v: version.parse(v) >= version.parse('3.5')}
}
class TestASGIWebsockets(TestApplicationPython):
prerequisites = {
'modules': {
'python': lambda v: version.parse(v) >= version.parse('3.5')
}
}
load_module = 'asgi'
ws = TestApplicationWebsocket()

View File

@@ -2,10 +2,10 @@ import pytest
from unit.applications.lang.python import TestApplicationPython
from unit.option import option
prerequisites = {'modules': {'python': 'any'}}
class TestClientIP(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
self.load('client_ip')

View File

@@ -3,10 +3,10 @@ import socket
import pytest
from unit.control import TestControl
prerequisites = {'modules': {'python': 'any'}}
class TestConfiguration(TestControl):
prerequisites = {'modules': {'python': 'any'}}
def try_addr(self, addr):
return self.conf(
{
@@ -420,10 +420,10 @@ class TestConfiguration(TestControl):
assert 'success' in self.conf(conf)
def test_unprivileged_user_error(self, is_su, skip_alert):
def test_unprivileged_user_error(self, require, skip_alert):
require({'privileged_user': False})
skip_alert(r'cannot set user "root"', r'failed to apply new conf')
if is_su:
pytest.skip('unprivileged tests')
assert 'error' in self.conf(
{

View File

@@ -1,10 +1,10 @@
import pytest
from unit.applications.lang.python import TestApplicationPython
prerequisites = {'modules': {'python': 'any'}}
class TestForwardedHeader(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
self.load('forwarded_header')
@@ -190,9 +190,7 @@ class TestForwardedHeader(TestApplicationPython):
== '1.1.1.1'
), 'xff replace multi 3'
assert (
self.get_addr(
xff='8.8.8.8, 2001:db8:3c4d:15::1a2f:1a2b, 127.0.0.1'
)
self.get_addr(xff='8.8.8.8, 2001:db8:3c4d:15::1a2f:1a2b, 127.0.0.1')
== '2001:db8:3c4d:15::1a2f:1a2b'
), 'xff chain ipv6'

View File

@@ -3,10 +3,10 @@ import re
import pytest
from unit.applications.lang.go import TestApplicationGo
prerequisites = {'modules': {'go': 'all'}}
class TestGoApplication(TestApplicationGo):
prerequisites = {'modules': {'go': 'all'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, skip_alert):
skip_alert(r'\[unit\] close\(\d+\) failed: Bad file descriptor')

View File

@@ -7,10 +7,10 @@ from unit.applications.lang.go import TestApplicationGo
from unit.option import option
from unit.utils import getns
prerequisites = {'modules': {'go': 'any'}, 'features': {'isolation': True}}
class TestGoIsolation(TestApplicationGo):
prerequisites = {'modules': {'go': 'any'}, 'features': ['isolation']}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, skip_alert):
skip_alert(r'\[unit\] close\(\d+\) failed: Bad file descriptor')
@@ -27,9 +27,6 @@ class TestGoIsolation(TestApplicationGo):
return (nobody_uid, nogroup_gid, nogroup)
def isolation_key(self, key):
return key in option.available['features']['isolation'].keys()
def test_isolation_values(self):
self.load('ns_inspect')
@@ -39,12 +36,13 @@ class TestGoIsolation(TestApplicationGo):
if ns.upper() in obj['NS']:
assert obj['NS'][ns.upper()] == ns_value, f'{ns} match'
def test_isolation_unpriv_user(self, is_su):
if not self.isolation_key('unprivileged_userns_clone'):
pytest.skip('unprivileged clone is not available')
if is_su:
pytest.skip('privileged tests, skip this')
def test_isolation_unpriv_user(self, require):
require(
{
'privileged_user': False,
'features': {'isolation': ['unprivileged_userns_clone']},
}
)
self.load('ns_inspect')
obj = self.getjson()['body']
@@ -101,9 +99,8 @@ class TestGoIsolation(TestApplicationGo):
assert obj['UID'] == 0, 'uid match uidmap'
assert obj['GID'] == 0, 'gid match gidmap'
def test_isolation_priv_user(self, is_su):
if not is_su:
pytest.skip('unprivileged tests, skip this')
def test_isolation_priv_user(self, require):
require({'privileged_user': True})
self.load('ns_inspect')
@@ -176,12 +173,12 @@ class TestGoIsolation(TestApplicationGo):
assert obj['UID'] == nobody_uid, 'uid match uidmap user=nobody'
assert obj['GID'] == nogroup_gid, 'gid match uidmap user=nobody'
def test_isolation_mnt(self):
if not self.isolation_key('mnt'):
pytest.skip('mnt namespace is not supported')
if not self.isolation_key('unprivileged_userns_clone'):
pytest.skip('unprivileged clone is not available')
def test_isolation_mnt(self, require):
require(
{
'features': {'isolation': ['unprivileged_userns_clone', 'mnt']},
}
)
self.load(
'ns_inspect',
@@ -205,19 +202,21 @@ class TestGoIsolation(TestApplicationGo):
assert obj['NS']['MNT'] != getns('mnt'), 'mnt set'
assert obj['NS']['USER'] != getns('user'), 'user set'
def test_isolation_pid(self, is_su):
if not self.isolation_key('pid'):
pytest.skip('pid namespace is not supported')
def test_isolation_pid(self, is_su, require):
require({'features': {'isolation': ['pid']}})
if not is_su:
if not self.isolation_key('unprivileged_userns_clone'):
pytest.skip('unprivileged clone is not available')
if not self.isolation_key('user'):
pytest.skip('user namespace is not supported')
if not self.isolation_key('mnt'):
pytest.skip('mnt namespace is not supported')
require(
{
'features': {
'isolation': [
'unprivileged_userns_clone',
'user',
'mnt',
]
}
}
)
isolation = {'namespaces': {'pid': True}}
@@ -262,19 +261,20 @@ class TestGoIsolation(TestApplicationGo):
== option.available['features']['isolation'][ns]
), f'{ns} match'
def test_go_isolation_rootfs_container(self, is_su, temp_dir):
def test_go_isolation_rootfs_container(self, is_su, require, temp_dir):
if not is_su:
if not self.isolation_key('unprivileged_userns_clone'):
pytest.skip('unprivileged clone is not available')
if not self.isolation_key('user'):
pytest.skip('user namespace is not supported')
if not self.isolation_key('mnt'):
pytest.skip('mnt namespace is not supported')
if not self.isolation_key('pid'):
pytest.skip('pid namespace is not supported')
require(
{
'features': {
'isolation': [
'unprivileged_userns_clone',
'user',
'mnt',
'pid',
]
}
}
)
isolation = {'rootfs': temp_dir}
@@ -294,12 +294,8 @@ class TestGoIsolation(TestApplicationGo):
obj = self.getjson(url='/?file=/bin/sh')['body']
assert not obj['FileExists'], 'file should not exists'
def test_go_isolation_rootfs_container_priv(self, is_su, temp_dir):
if not is_su:
pytest.skip('requires root')
if not self.isolation_key('mnt'):
pytest.skip('mnt namespace is not supported')
def test_go_isolation_rootfs_container_priv(self, require, temp_dir):
require({'privileged_user': True, 'features': {'isolation': ['mnt']}})
isolation = {
'namespaces': {'mount': True},
@@ -315,24 +311,27 @@ class TestGoIsolation(TestApplicationGo):
obj = self.getjson(url='/?file=/bin/sh')['body']
assert not obj['FileExists'], 'file should not exists'
def test_go_isolation_rootfs_automount_tmpfs(self, is_su, temp_dir):
def test_go_isolation_rootfs_automount_tmpfs(
self, is_su, require, temp_dir
):
try:
open("/proc/self/mountinfo")
except:
pytest.skip('The system lacks /proc/self/mountinfo file')
if not is_su:
if not self.isolation_key('unprivileged_userns_clone'):
pytest.skip('unprivileged clone is not available')
if not self.isolation_key('user'):
pytest.skip('user namespace is not supported')
if not self.isolation_key('mnt'):
pytest.skip('mnt namespace is not supported')
if not self.isolation_key('pid'):
pytest.skip('pid namespace is not supported')
require(
{
'features': {
'isolation': [
'unprivileged_userns_clone',
'user',
'mnt',
'pid',
]
}
}
)
isolation = {'rootfs': temp_dir}

View File

@@ -1,26 +1,20 @@
import os
import pytest
from unit.applications.lang.go import TestApplicationGo
prerequisites = {
'modules': {'go': 'all'},
'features': {'isolation': True},
'privileged_user': True,
}
class TestGoIsolationRootfs(TestApplicationGo):
prerequisites = {'modules': {'go': 'all'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, skip_alert):
skip_alert(r'\[unit\] close\(\d+\) failed: Bad file descriptor')
def test_go_isolation_rootfs_chroot(self, is_su, temp_dir):
if not is_su:
pytest.skip('requires root')
if os.uname().sysname == 'Darwin':
pytest.skip('chroot tests not supported on OSX')
isolation = {
'rootfs': temp_dir,
}
def test_go_isolation_rootfs_chroot(self, temp_dir):
isolation = {'rootfs': temp_dir}
self.load('ns_inspect', isolation=isolation)

View File

@@ -1,10 +1,10 @@
import pytest
from unit.applications.lang.python import TestApplicationPython
prerequisites = {'modules': {'python': 'any'}}
class TestHTTPHeader(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
def test_http_header_value_leading_sp(self):
self.load('custom_header')

View File

@@ -7,10 +7,10 @@ from unit.applications.lang.java import TestApplicationJava
from unit.option import option
from unit.utils import public_dir
prerequisites = {'modules': {'java': 'all'}}
class TestJavaApplication(TestApplicationJava):
prerequisites = {'modules': {'java': 'all'}}
def test_java_conf_error(self, temp_dir, skip_alert):
skip_alert(
r'realpath.*failed',

View File

@@ -5,15 +5,12 @@ import pytest
from unit.applications.lang.java import TestApplicationJava
from unit.option import option
prerequisites = {'modules': {'java': 'all'}, 'privileged_user': True}
class TestJavaIsolationRootfs(TestApplicationJava):
prerequisites = {'modules': {'java': 'all'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, is_su, temp_dir):
if not is_su:
pytest.skip('require root')
def setup_method_fixture(self, temp_dir):
os.makedirs(f'{temp_dir}/jars')
os.makedirs(f'{temp_dir}/tmp')
os.chmod(f'{temp_dir}/tmp', 0o777)
@@ -35,10 +32,7 @@ class TestJavaIsolationRootfs(TestApplicationJava):
except subprocess.CalledProcessError:
pytest.fail("Can't run mount process.")
def teardown_method(self, is_su):
if not is_su:
return
def teardown_method(self):
try:
subprocess.run(
["umount", "--lazy", f"{option.temp_dir}/jars"],
@@ -51,13 +45,8 @@ class TestJavaIsolationRootfs(TestApplicationJava):
except subprocess.CalledProcessError:
pytest.fail("Can't run umount process.")
def test_java_isolation_rootfs_chroot_war(self, is_su, temp_dir):
if not is_su:
pytest.skip('require root')
isolation = {
'rootfs': temp_dir,
}
def test_java_isolation_rootfs_chroot_war(self, temp_dir):
isolation = {'rootfs': temp_dir}
self.load('empty_war', isolation=isolation)

View File

@@ -5,10 +5,10 @@ import pytest
from unit.applications.lang.java import TestApplicationJava
from unit.applications.websockets import TestApplicationWebsocket
prerequisites = {'modules': {'java': 'any'}}
class TestJavaWebsockets(TestApplicationJava):
prerequisites = {'modules': {'java': 'any'}}
ws = TestApplicationWebsocket()
@pytest.fixture(autouse=True)

View File

@@ -5,18 +5,16 @@ from unit.applications.proto import TestApplicationProto
from unit.option import option
from unit.utils import waitforfiles
prerequisites = {'modules': {'njs': 'any'}}
class TestNJS(TestApplicationProto):
prerequisites = {'modules': {'njs': 'any'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
assert 'success' in self.conf(
{
"listeners": {"*:7080": {"pass": "routes"}},
"routes": [
{"action": {"share": f"{temp_dir}/assets$uri"}}
],
"routes": [{"action": {"share": f"{temp_dir}/assets$uri"}}],
}
)

View File

@@ -1,10 +1,10 @@
from unit.applications.proto import TestApplicationProto
from unit.option import option
prerequisites = {'modules': {'njs': 'any'}}
class TestNJSModules(TestApplicationProto):
prerequisites = {'modules': {'njs': 'any'}}
def njs_script_load(self, module, name=None, expect='success'):
if name is None:
name = module

View File

@@ -4,10 +4,10 @@ import pytest
from unit.applications.lang.node import TestApplicationNode
from unit.utils import waitforfiles
prerequisites = {'modules': {'node': 'all'}}
class TestNodeApplication(TestApplicationNode):
prerequisites = {'modules': {'node': 'all'}}
def assert_basic_application(self):
resp = self.get()
assert resp['headers']['Content-Type'] == 'text/plain', 'basic header'

View File

@@ -2,14 +2,12 @@ from packaging import version
from unit.applications.lang.node import TestApplicationNode
from unit.applications.websockets import TestApplicationWebsocket
prerequisites = {
'modules': {'node': lambda v: version.parse(v) >= version.parse('14.16.0')}
}
class TestNodeESModules(TestApplicationNode):
prerequisites = {
'modules': {
'node': lambda v: version.parse(v) >= version.parse('14.16.0')
}
}
es_modules = True
ws = TestApplicationWebsocket()

View File

@@ -5,10 +5,10 @@ import pytest
from unit.applications.lang.node import TestApplicationNode
from unit.applications.websockets import TestApplicationWebsocket
prerequisites = {'modules': {'node': 'any'}}
class TestNodeWebsockets(TestApplicationNode):
prerequisites = {'modules': {'node': 'any'}}
ws = TestApplicationWebsocket()
@pytest.fixture(autouse=True)

View File

@@ -3,10 +3,10 @@ import re
import pytest
from unit.applications.lang.perl import TestApplicationPerl
prerequisites = {'modules': {'perl': 'all'}}
class TestPerlApplication(TestApplicationPerl):
prerequisites = {'modules': {'perl': 'all'}}
def test_perl_application(self, date_to_sec_epoch, sec_epoch):
self.load('variables')

View File

@@ -10,10 +10,10 @@ import pytest
from unit.applications.lang.php import TestApplicationPHP
from unit.option import option
prerequisites = {'modules': {'php': 'all'}}
class TestPHPApplication(TestApplicationPHP):
prerequisites = {'modules': {'php': 'all'}}
def before_disable_functions(self):
body = self.get()['body']

View File

@@ -1,9 +1,9 @@
from unit.control import TestControl
prerequisites = {'modules': {'php': 'any'}}
class TestPHPBasic(TestControl):
prerequisites = {'modules': {'php': 'any'}}
conf_app = {
"app": {
"type": "php",

View File

@@ -1,30 +1,26 @@
import pytest
from unit.applications.lang.php import TestApplicationPHP
from unit.option import option
prerequisites = {'modules': {'php': 'any'}, 'features': {'isolation': True}}
class TestPHPIsolation(TestApplicationPHP):
prerequisites = {'modules': {'php': 'any'}, 'features': ['isolation']}
def test_php_isolation_rootfs(self, is_su, temp_dir):
isolation_features = option.available['features']['isolation'].keys()
if not is_su:
if not 'unprivileged_userns_clone' in isolation_features:
pytest.skip('requires unprivileged userns or root')
if 'user' not in isolation_features:
pytest.skip('user namespace is not supported')
if 'mnt' not in isolation_features:
pytest.skip('mnt namespace is not supported')
if 'pid' not in isolation_features:
pytest.skip('pid namespace is not supported')
def test_php_isolation_rootfs(self, is_su, require, temp_dir):
isolation = {'rootfs': temp_dir}
if not is_su:
require(
{
'features': {
'isolation': [
'unprivileged_userns_clone',
'user',
'mnt',
'pid',
]
}
}
)
isolation['namespaces'] = {
'mount': True,
'credential': True,
@@ -42,25 +38,23 @@ class TestPHPIsolation(TestApplicationPHP):
assert self.get()['status'] == 200, 'empty rootfs'
def test_php_isolation_rootfs_extensions(self, is_su, temp_dir):
isolation_features = option.available['features']['isolation'].keys()
if not is_su:
if not 'unprivileged_userns_clone' in isolation_features:
pytest.skip('requires unprivileged userns or root')
if 'user' not in isolation_features:
pytest.skip('user namespace is not supported')
if 'mnt' not in isolation_features:
pytest.skip('mnt namespace is not supported')
if 'pid' not in isolation_features:
pytest.skip('pid namespace is not supported')
def test_php_isolation_rootfs_extensions(self, is_su, require, temp_dir):
isolation = {'rootfs': temp_dir}
if not is_su:
require(
{
'features': {
'isolation': [
'unprivileged_userns_clone',
'user',
'mnt',
'pid',
]
}
}
)
isolation['namespaces'] = {
'mount': True,
'credential': True,

View File

@@ -1,10 +1,10 @@
from unit.applications.lang.php import TestApplicationPHP
from unit.option import option
prerequisites = {'modules': {'php': 'any'}}
class TestPHPTargets(TestApplicationPHP):
prerequisites = {'modules': {'php': 'any'}}
def test_php_application_targets(self):
targets_dir = f"{option.test_dir}/php/targets"
assert 'success' in self.conf(

View File

@@ -8,10 +8,10 @@ from unit.applications.lang.python import TestApplicationPython
from unit.option import option
from unit.utils import waitforsocket
prerequisites = {'modules': {'python': 'any'}}
class TestProxy(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
SERVER_PORT = 7999
@pytest.fixture(autouse=True)

View File

@@ -8,10 +8,10 @@ from conftest import run_process
from unit.applications.lang.python import TestApplicationPython
from unit.utils import waitforsocket
prerequisites = {'modules': {'python': 'any'}}
class TestProxyChunked(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
SERVER_PORT = 7999
@pytest.fixture(autouse=True)

View File

@@ -10,10 +10,10 @@ import pytest
from packaging import version
from unit.applications.lang.python import TestApplicationPython
prerequisites = {'modules': {'python': 'all'}}
class TestPythonApplication(TestApplicationPython):
prerequisites = {'modules': {'python': 'all'}}
def test_python_application_variables(self, date_to_sec_epoch, sec_epoch):
self.load('variables')
@@ -740,9 +740,8 @@ last line: 987654321
), 'exception raise close'
assert len(findall(r'Traceback')) == 8, 'traceback count 8'
def test_python_user_group(self, is_su):
if not is_su:
pytest.skip('requires root')
def test_python_user_group(self, require):
require({'privileged_user': True})
nobody_uid = pwd.getpwnam('nobody').pw_uid

View File

@@ -1,8 +1,9 @@
from unit.control import TestControl
prerequisites = {'modules': {'python': 'any'}}
class TestPythonBasic(TestControl):
prerequisites = {'modules': {'python': 'any'}}
conf_app = {
"app": {

View File

@@ -1,9 +1,9 @@
from unit.applications.lang.python import TestApplicationPython
prerequisites = {'modules': {'python': 'any'}}
class TestPythonEnvironment(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
def test_python_environment_name_null(self):
self.load('environment')

View File

@@ -10,10 +10,10 @@ from unit.utils import findmnt
from unit.utils import waitformount
from unit.utils import waitforunmount
prerequisites = {'modules': {'python': 'any'}, 'features': {'isolation': True}}
class TestPythonIsolation(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}, 'features': ['isolation']}
def get_cgroup(self, app_name):
output = subprocess.check_output(
['ps', 'ax', '-o', 'pid', '-o', 'cmd']
@@ -31,25 +31,23 @@ class TestPythonIsolation(TestApplicationPython):
with open(cgroup, 'r') as f:
return f.read().rstrip()
def test_python_isolation_rootfs(self, is_su, temp_dir):
isolation_features = option.available['features']['isolation'].keys()
if not is_su:
if not 'unprivileged_userns_clone' in isolation_features:
pytest.skip('requires unprivileged userns or root')
if 'user' not in isolation_features:
pytest.skip('user namespace is not supported')
if 'mnt' not in isolation_features:
pytest.skip('mnt namespace is not supported')
if 'pid' not in isolation_features:
pytest.skip('pid namespace is not supported')
def test_python_isolation_rootfs(self, is_su, require, temp_dir):
isolation = {'rootfs': temp_dir}
if not is_su:
require(
{
'features': {
'isolation': [
'unprivileged_userns_clone',
'user',
'mnt',
'pid',
]
}
}
)
isolation['namespaces'] = {
'mount': True,
'credential': True,
@@ -78,9 +76,8 @@ class TestPythonIsolation(TestApplicationPython):
assert ret['body']['FileExists'], 'application exists in rootfs'
def test_python_isolation_rootfs_no_language_deps(self, is_su, temp_dir):
if not is_su:
pytest.skip('requires root')
def test_python_isolation_rootfs_no_language_deps(self, require, temp_dir):
require({'privileged_user': True})
isolation = {'rootfs': temp_dir, 'automount': {'language_deps': False}}
self.load('empty', isolation=isolation)
@@ -103,9 +100,8 @@ class TestPythonIsolation(TestApplicationPython):
assert waitforunmount(python_path), 'language_deps unmount'
def test_python_isolation_procfs(self, is_su, temp_dir):
if not is_su:
pytest.skip('requires root')
def test_python_isolation_procfs(self, require, temp_dir):
require({'privileged_user': True})
isolation = {'rootfs': temp_dir, 'automount': {'procfs': False}}
@@ -123,12 +119,10 @@ class TestPythonIsolation(TestApplicationPython):
'FileExists'
], '/proc/self'
def test_python_isolation_cgroup(self, is_su):
if not is_su:
pytest.skip('requires root')
if not 'cgroup' in option.available['features']['isolation']:
pytest.skip('cgroup is not supported')
def test_python_isolation_cgroup(self, require):
require(
{'privileged_user': True, 'features': {'isolation': ['cgroup']}}
)
def set_cgroup_path(path):
isolation = {'cgroup': {'path': path}}
@@ -146,12 +140,10 @@ class TestPythonIsolation(TestApplicationPython):
assert len(cgroup_rel.parts) >= len(cgroup_abs.parts)
def test_python_isolation_cgroup_two(self, is_su):
if not is_su:
pytest.skip('requires root')
if not 'cgroup' in option.available['features']['isolation']:
pytest.skip('cgroup is not supported')
def test_python_isolation_cgroup_two(self, require):
require(
{'privileged_user': True, 'features': {'isolation': ['cgroup']}}
)
def set_two_cgroup_path(path, path2):
script_path = f'{option.test_dir}/python/empty'
@@ -193,12 +185,10 @@ class TestPythonIsolation(TestApplicationPython):
set_two_cgroup_path('/scope/python', '/scope2/python')
assert self.get_cgroup('one') != self.get_cgroup('two')
def test_python_isolation_cgroup_invalid(self, is_su):
if not is_su:
pytest.skip('requires root')
if not 'cgroup' in option.available['features']['isolation']:
pytest.skip('cgroup is not supported')
def test_python_isolation_cgroup_invalid(self, require):
require(
{'privileged_user': True, 'features': {'isolation': ['cgroup']}}
)
def check_invalid(path):
script_path = f'{option.test_dir}/python/empty'

View File

@@ -1,17 +1,11 @@
import pytest
from unit.applications.lang.python import TestApplicationPython
prerequisites = {'modules': {'python': 'any'}, 'privileged_user': True}
class TestPythonIsolation(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
def test_python_isolation_chroot(self, is_su, temp_dir):
if not is_su:
pytest.skip('requires root')
isolation = {
'rootfs': temp_dir,
}
def test_python_isolation_chroot(self, temp_dir):
isolation = {'rootfs': temp_dir}
self.load('ns_inspect', isolation=isolation)

View File

@@ -7,10 +7,10 @@ import pytest
from unit.applications.lang.python import TestApplicationPython
from unit.option import option
prerequisites = {'modules': {'python': 'any'}}
class TestPythonProcman(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
self.app_name = f'app-{temp_dir.split("/")[-1]}'

View File

@@ -1,10 +1,10 @@
from unit.applications.lang.python import TestApplicationPython
from unit.option import option
prerequisites = {'modules': {'python': 'all'}}
class TestPythonTargets(TestApplicationPython):
prerequisites = {'modules': {'python': 'all'}}
def test_python_targets(self):
python_dir = f'{option.test_dir}/python'

View File

@@ -5,8 +5,6 @@ from unit.applications.proto import TestApplicationProto
class TestReconfigure(TestApplicationProto):
prerequisites = {}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
assert 'success' in self.conf(

View File

@@ -5,10 +5,10 @@ import time
import pytest
from unit.applications.tls import TestApplicationTLS
prerequisites = {'modules': {'openssl': 'any'}}
class TestReconfigureTLS(TestApplicationTLS):
prerequisites = {'modules': {'openssl': 'any'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
if 'HAS_TLSv1_2' not in dir(ssl) or not ssl.HAS_TLSv1_2:

View File

@@ -5,10 +5,10 @@ import time
import pytest
from unit.applications.lang.python import TestApplicationPython
prerequisites = {'modules': {'python': 'any'}}
class TestRespawn(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
PATTERN_ROUTER = 'unit: router'
PATTERN_CONTROLLER = 'unit: controller'

View File

@@ -5,8 +5,6 @@ from unit.applications.proto import TestApplicationProto
class TestReturn(TestApplicationProto):
prerequisites = {}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
self._load_conf(

View File

@@ -2,12 +2,9 @@ import os
import pytest
from unit.applications.proto import TestApplicationProto
from unit.option import option
class TestRewrite(TestApplicationProto):
prerequisites = {}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
assert 'success' in self.conf(
@@ -97,9 +94,8 @@ class TestRewrite(TestApplicationProto):
)
assert self.get(url='/foo?arg=val')['status'] == 200
def test_rewrite_njs(self):
if 'njs' not in option.available['modules'].keys():
pytest.skip('NJS is not available')
def test_rewrite_njs(self, require):
require({'modules': {'njs': 'any'}})
self.set_rewrite("`/${host}`", "/localhost")
assert self.get()['status'] == 200

View File

@@ -3,10 +3,10 @@ import pytest
from unit.applications.lang.python import TestApplicationPython
from unit.option import option
prerequisites = {'modules': {'python': 'any'}}
class TestRouting(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
assert 'success' in self.conf(
@@ -232,9 +232,8 @@ class TestRouting(TestApplicationPython):
assert self.get(url='/aBCaBbc')['status'] == 200
assert self.get(url='/ABc')['status'] == 404
def test_routes_empty_regex(self):
if not option.available['modules']['regex']:
pytest.skip('requires regex')
def test_routes_empty_regex(self, require):
require({'modules': {'regex': True}})
self.route_match({"uri": "~"})
assert self.get(url='/')['status'] == 200, 'empty regexp'
@@ -244,9 +243,8 @@ class TestRouting(TestApplicationPython):
assert self.get(url='/')['status'] == 404, 'empty regexp 2'
assert self.get(url='/nothing')['status'] == 404, '/nothing'
def test_routes_bad_regex(self):
if not option.available['modules']['regex']:
pytest.skip('requires regex')
def test_routes_bad_regex(self, require):
require({'modules': {'regex': True}})
assert 'error' in self.route(
{"match": {"uri": "~/bl[ah"}, "action": {"return": 200}}
@@ -264,9 +262,8 @@ class TestRouting(TestApplicationPython):
if 'error' not in status:
assert self.get(url='/nothing_z')['status'] == 500, '/nothing_z'
def test_routes_match_regex_case_sensitive(self):
if not option.available['modules']['regex']:
pytest.skip('requires regex')
def test_routes_match_regex_case_sensitive(self, require):
require({'modules': {'regex': True}})
self.route_match({"uri": "~/bl[ah]"})
@@ -275,9 +272,8 @@ class TestRouting(TestApplicationPython):
assert self.get(url='/blh')['status'] == 200, '/blh'
assert self.get(url='/BLAH')['status'] == 404, '/BLAH'
def test_routes_match_regex_negative_case_sensitive(self):
if not option.available['modules']['regex']:
pytest.skip('requires regex')
def test_routes_match_regex_negative_case_sensitive(self, require):
require({'modules': {'regex': True}})
self.route_match({"uri": "!~/bl[ah]"})

View File

@@ -1,9 +1,9 @@
from unit.applications.tls import TestApplicationTLS
prerequisites = {'modules': {'openssl': 'any'}}
class TestRoutingTLS(TestApplicationTLS):
prerequisites = {'modules': {'openssl': 'any'}}
def test_routes_match_scheme_tls(self):
self.certificate()

View File

@@ -4,10 +4,10 @@ import subprocess
import pytest
from unit.applications.lang.ruby import TestApplicationRuby
prerequisites = {'modules': {'ruby': 'all'}}
class TestRubyApplication(TestApplicationRuby):
prerequisites = {'modules': {'ruby': 'all'}}
def test_ruby_application(self, date_to_sec_epoch, sec_epoch):
self.load('variables')

View File

@@ -2,10 +2,10 @@ from unit.applications.lang.ruby import TestApplicationRuby
from unit.option import option
from unit.utils import waitforglob
prerequisites = {'modules': {'ruby': 'all'}}
class TestRubyHooks(TestApplicationRuby):
prerequisites = {'modules': {'ruby': 'all'}}
def _wait_cookie(self, pattern, count):
return waitforglob(
f'{option.temp_dir}/ruby/hooks/cookie_{pattern}', count

View File

@@ -1,30 +1,26 @@
import pytest
from unit.applications.lang.ruby import TestApplicationRuby
from unit.option import option
prerequisites = {'modules': {'ruby': 'any'}, 'features': {'isolation': True}}
class TestRubyIsolation(TestApplicationRuby):
prerequisites = {'modules': {'ruby': 'any'}, 'features': ['isolation']}
def test_ruby_isolation_rootfs(self, is_su, temp_dir):
isolation_features = option.available['features']['isolation'].keys()
if not is_su:
if not 'unprivileged_userns_clone' in isolation_features:
pytest.skip('requires unprivileged userns or root')
if 'user' not in isolation_features:
pytest.skip('user namespace is not supported')
if 'mnt' not in isolation_features:
pytest.skip('mnt namespace is not supported')
if 'pid' not in isolation_features:
pytest.skip('pid namespace is not supported')
def test_ruby_isolation_rootfs(self, is_su, require, temp_dir):
isolation = {'rootfs': temp_dir}
if not is_su:
require(
{
'features': {
'isolation': [
'unprivileged_userns_clone',
'user',
'mnt',
'pid',
]
}
}
)
isolation['namespaces'] = {
'mount': True,
'credential': True,

View File

@@ -6,10 +6,10 @@ import time
import pytest
from unit.applications.lang.python import TestApplicationPython
prerequisites = {'modules': {'python': 'any'}}
class TestSettings(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
def sysctl(self):
try:
out = subprocess.check_output(

View File

@@ -7,8 +7,6 @@ from unit.utils import waitforfiles
class TestStatic(TestApplicationProto):
prerequisites = {}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
os.makedirs(f'{temp_dir}/assets/dir')

View File

@@ -5,10 +5,10 @@ import pytest
from unit.applications.proto import TestApplicationProto
from unit.option import option
prerequisites = {'features': {'chroot': True}}
class TestStaticChroot(TestApplicationProto):
prerequisites = {'features': ['chroot']}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
os.makedirs(f'{temp_dir}/assets/dir')
@@ -62,9 +62,8 @@ class TestStaticChroot(TestApplicationProto):
)
assert self.get()['status'] != 200, 'share array bad'
def test_static_chroot_permission(self, is_su, temp_dir):
if is_su:
pytest.skip("does't work under root")
def test_static_chroot_permission(self, require, temp_dir):
require({'privileged_user': False})
os.chmod(f'{temp_dir}/assets/dir', 0o100)
@@ -81,9 +80,8 @@ class TestStaticChroot(TestApplicationProto):
assert 'success' in self.update_action("", ".$uri")
assert self.get(url=self.test_path)['status'] == 200, 'empty relative'
def test_static_chroot_relative(self, is_su):
if is_su:
pytest.skip("Does't work under root.")
def test_static_chroot_relative(self, require):
require({'privileged_user': False})
assert 'success' in self.update_action('.')
assert self.get(url='/dir/file')['status'] == 403, 'relative chroot'

View File

@@ -6,8 +6,6 @@ from unit.applications.proto import TestApplicationProto
class TestStaticFallback(TestApplicationProto):
prerequisites = {}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
assets_dir = f'{temp_dir}/assets'

View File

@@ -5,15 +5,12 @@ from pathlib import Path
import pytest
from unit.applications.proto import TestApplicationProto
prerequisites = {'features': {'chroot': True}, 'privileged_user': True}
class TestStaticMount(TestApplicationProto):
prerequisites = {'features': ['chroot']}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, is_su, temp_dir):
if not is_su:
pytest.skip('requires root')
def setup_method_fixture(self, temp_dir):
os.makedirs(f'{temp_dir}/assets/dir/mount')
os.makedirs(f'{temp_dir}/assets/dir/dir')
os.makedirs(f'{temp_dir}/assets/mount')

View File

@@ -6,8 +6,6 @@ from unit.applications.proto import TestApplicationProto
class TestStaticShare(TestApplicationProto):
prerequisites = {}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
os.makedirs(f'{temp_dir}/assets/dir')

View File

@@ -4,10 +4,10 @@ from pathlib import Path
import pytest
from unit.applications.proto import TestApplicationProto
prerequisites = {'features': {'chroot': True}}
class TestStaticSymlink(TestApplicationProto):
prerequisites = {'features': ['chroot']}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
os.makedirs(f'{temp_dir}/assets/dir/dir')

View File

@@ -5,8 +5,6 @@ from unit.applications.proto import TestApplicationProto
class TestStaticTypes(TestApplicationProto):
prerequisites = {}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
Path(f'{temp_dir}/assets').mkdir()

View File

@@ -6,8 +6,6 @@ from unit.applications.proto import TestApplicationProto
class TestStaticVariables(TestApplicationProto):
prerequisites = {}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
os.makedirs(f'{temp_dir}/assets/dir')

View File

@@ -4,10 +4,10 @@ from unit.applications.lang.python import TestApplicationPython
from unit.option import option
from unit.status import Status
prerequisites = {'modules': {'python': 'any'}}
class TestStatus(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
def check_connections(self, accepted, active, idle, closed):
assert Status.get('/connections') == {
'accepted': accepted,

View File

@@ -1,10 +1,10 @@
from unit.applications.tls import TestApplicationTLS
from unit.status import Status
prerequisites = {'modules': {'openssl': 'any'}}
class TestStatusTLS(TestApplicationTLS):
prerequisites = {'modules': {'openssl': 'any'}}
def test_status_tls_requests(self):
self.certificate()

View File

@@ -7,10 +7,10 @@ import pytest
from unit.applications.tls import TestApplicationTLS
from unit.option import option
prerequisites = {'modules': {'python': 'any', 'openssl': 'any'}}
class TestTLS(TestApplicationTLS):
prerequisites = {'modules': {'python': 'any', 'openssl': 'any'}}
def add_tls(self, application='empty', cert='default', port=7080):
assert 'success' in self.conf(
{

View File

@@ -3,10 +3,10 @@ import ssl
import pytest
from unit.applications.tls import TestApplicationTLS
prerequisites = {'modules': {'openssl': 'any'}}
class TestTLSConfCommand(TestApplicationTLS):
prerequisites = {'modules': {'openssl': 'any'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
self.certificate()

View File

@@ -14,10 +14,10 @@ from OpenSSL.SSL import (
)
from unit.applications.tls import TestApplicationTLS
prerequisites = {'modules': {'openssl': 'any'}}
class TestTLSSession(TestApplicationTLS):
prerequisites = {'modules': {'openssl': 'any'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
self.certificate()

View File

@@ -5,10 +5,10 @@ import pytest
from unit.applications.tls import TestApplicationTLS
from unit.option import option
prerequisites = {'modules': {'openssl': 'any'}}
class TestTLSSNI(TestApplicationTLS):
prerequisites = {'modules': {'openssl': 'any'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
self._load_conf(

View File

@@ -11,10 +11,10 @@ from OpenSSL.SSL import (
)
from unit.applications.tls import TestApplicationTLS
prerequisites = {'modules': {'openssl': 'any'}}
class TestTLSTicket(TestApplicationTLS):
prerequisites = {'modules': {'openssl': 'any'}}
ticket = 'U1oDTh11mMxODuw12gS0EXX1E/PkZG13cJNQ6m5+6BGlfPTjNlIEw7PSVU3X1gTE'
ticket2 = '5AV0DSYIYbZWZQB7fCnTHZmMxtotb/aXjam+n2XS79lTvX3Tq9xGqpC8XKNEF2lt'
ticket80 = '6Pfil8lv/k8zf8MndPpfXaO5EAV6dhME6zs6CfUyq2yziynQwSywtKQMqHGnJ2HR\

View File

@@ -1,13 +1,13 @@
from unit.applications.lang.python import TestApplicationPython
from unit.option import option
prerequisites = {
'modules': {'python': 'any'},
'features': {'unix_abstract': True},
}
class TestUnixAbstract(TestApplicationPython):
prerequisites = {
'modules': {'python': 'any'},
'features': ['unix_abstract'],
}
def test_unix_abstract_source(self):
addr = '\0sock'

View File

@@ -5,10 +5,10 @@ import pytest
from unit.applications.lang.python import TestApplicationPython
from unit.option import option
prerequisites = {'modules': {'python': 'any'}}
class TestUpstreamsRR(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
assert 'success' in self.conf(

View File

@@ -5,10 +5,10 @@ from unit.applications.lang.python import TestApplicationPython
from unit.log import Log
from unit.utils import waitforfiles
prerequisites = {'modules': {'python': 'any'}}
class TestUSR1(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}}
def test_usr1_access_log(
self, search_in_file, temp_dir, unit_pid, wait_for_record
):

View File

@@ -7,8 +7,6 @@ from unit.option import option
class TestVariables(TestApplicationProto):
prerequisites = {}
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
assert 'success' in self.conf(

View File

@@ -1,9 +1,6 @@
import os
import re
import time
from unit.control import TestControl
from unit.log import Log
from unit.option import option

View File

@@ -0,0 +1,63 @@
import pytest
from unit.option import option
def check_prerequisites(prerequisites):
if 'privileged_user' in prerequisites:
if prerequisites['privileged_user'] and not option.is_privileged:
pytest.skip(
'privileged user required',
allow_module_level=True,
)
elif not prerequisites['privileged_user'] and option.is_privileged:
pytest.skip(
'unprivileged user required',
allow_module_level=True,
)
missed = []
# check modules
if 'modules' in prerequisites:
available = option.available['modules']
for module in prerequisites['modules']:
if module in available and available[module]:
continue
missed.append(module)
if missed:
pytest.skip(
f'Unit has no {", ".join(missed)} module(s)',
allow_module_level=True,
)
# check features
if 'features' in prerequisites:
available = option.available['features']
require = prerequisites['features']
for feature in require:
avail_feature = available[feature]
if feature in available and avail_feature:
if isinstance(require[feature], list) and isinstance(
avail_feature, dict
):
avail_keys = avail_feature.keys()
for key in require[feature]:
if key not in avail_keys:
missed.append(f'{feature}/{key}')
continue
missed.append(feature)
if missed:
pytest.skip(
f'{", ".join(missed)} feature(s) not supported',
allow_module_level=True,
)

View File

@@ -7,26 +7,24 @@ http = TestHTTP()
def check_chroot():
available = option.available
resp = http.put(
url='/config',
sock_type='unix',
addr=f'{option.temp_dir}/control.unit.sock',
body=json.dumps(
{
"listeners": {"*:7080": {"pass": "routes"}},
"routes": [
{
"action": {
"share": option.temp_dir,
"chroot": option.temp_dir,
return (
'success'
in http.put(
url='/config',
sock_type='unix',
addr=f'{option.temp_dir}/control.unit.sock',
body=json.dumps(
{
"listeners": {"*:7080": {"pass": "routes"}},
"routes": [
{
"action": {
"share": option.temp_dir,
"chroot": option.temp_dir,
}
}
}
],
}
),
],
}
),
)['body']
)
if 'success' in resp['body']:
available['features']['chroot'] = True

View File

@@ -0,0 +1,47 @@
import subprocess
import sys
from unit.check.chroot import check_chroot
from unit.check.go import check_go
from unit.check.isolation import check_isolation
from unit.check.njs import check_njs
from unit.check.node import check_node
from unit.check.regex import check_regex
from unit.check.tls import check_openssl
from unit.check.unix_abstract import check_unix_abstract
from unit.log import Log
from unit.option import option
def discover_available(unit):
output_version = subprocess.check_output(
[unit['unitd'], '--version'], stderr=subprocess.STDOUT
).decode()
# wait for controller start
if Log.wait_for_record(r'controller started') is None:
Log.print_log()
sys.exit("controller didn't start")
# discover modules from log file
for module in Log.findall(r'module: ([a-zA-Z]+) (.*) ".*"$'):
versions = option.available['modules'].setdefault(module[0], [])
if module[1] not in versions:
versions.append(module[1])
# discover modules using check
option.available['modules']['go'] = check_go()
option.available['modules']['njs'] = check_njs(output_version)
option.available['modules']['node'] = check_node()
option.available['modules']['openssl'] = check_openssl(output_version)
option.available['modules']['regex'] = check_regex(output_version)
# Discover features using check. Features should be discovered after
# modules since some features can require modules.
option.available['features']['chroot'] = check_chroot()
option.available['features']['isolation'] = check_isolation()
option.available['features']['unix_abstract'] = check_unix_abstract()

View File

@@ -2,5 +2,4 @@ from unit.applications.lang.go import TestApplicationGo
def check_go():
if TestApplicationGo.prepare_env('empty') is not None:
return True
return TestApplicationGo.prepare_env('empty') is not None

View File

@@ -127,7 +127,7 @@ def check_isolation():
}
else:
return
return False
resp = http.put(
url='/config',
@@ -137,23 +137,23 @@ def check_isolation():
)
if 'success' not in resp['body']:
return
return False
userns = getns('user')
if not userns:
return
return False
available['features']['isolation'] = {'user': userns}
isolation = {'user': userns}
unp_clone_path = '/proc/sys/kernel/unprivileged_userns_clone'
if os.path.exists(unp_clone_path):
with open(unp_clone_path, 'r') as f:
if str(f.read()).rstrip() == '1':
available['features']['isolation'][
'unprivileged_userns_clone'
] = True
isolation['unprivileged_userns_clone'] = True
for ns in allns:
ns_value = getns(ns)
if ns_value:
available['features']['isolation'][ns] = ns_value
isolation[ns] = ns_value
return isolation

View File

@@ -2,5 +2,4 @@ import re
def check_njs(output_version):
if re.search('--njs', output_version):
return True
return re.search('--njs', output_version)

View File

@@ -1,10 +1,12 @@
import os
import subprocess
from unit.option import option
def check_node(current_dir):
if not os.path.exists(f'{current_dir}/node/node_modules'):
return None
def check_node():
if not os.path.exists(f'{option.current_dir}/node/node_modules'):
return False
try:
v_bytes = subprocess.check_output(['/usr/bin/env', 'node', '-v'])
@@ -12,4 +14,4 @@ def check_node(current_dir):
return [str(v_bytes, 'utf-8').lstrip('v').rstrip()]
except subprocess.CalledProcessError:
return None
return False

View File

@@ -2,7 +2,4 @@ import re
def check_regex(output_version):
if re.search('--no-regex', output_version):
return False
return True
return not re.search('--no-regex', output_version)

View File

@@ -6,7 +6,6 @@ def check_openssl(output_version):
try:
subprocess.check_output(['which', 'openssl'])
except subprocess.CalledProcessError:
return None
return False
if re.search('--openssl', output_version):
return True
return re.search('--openssl', output_version)

View File

@@ -7,19 +7,19 @@ http = TestHTTP()
def check_unix_abstract():
available = option.available
resp = http.put(
url='/config',
sock_type='unix',
addr=f'{option.temp_dir}/control.unit.sock',
body=json.dumps(
{
"listeners": {"unix:@sock": {"pass": "routes"}},
"routes": [],
}
),
return (
'success'
in http.put(
url='/config',
sock_type='unix',
addr=f'{option.temp_dir}/control.unit.sock',
body=json.dumps(
{
"listeners": {
f'unix:@{option.temp_dir}/sock': {"pass": "routes"}
},
"routes": [],
}
),
)['body']
)
if 'success' in resp['body']:
available['features']['unix_abstract'] = True

View File

@@ -1,6 +1,7 @@
import os
import re
import sys
import time
from unit.option import option
@@ -25,7 +26,7 @@ class Log:
@print_log_on_assert
def check_alerts(log=None):
if log is None:
log = Log.read(encoding='utf-8')
log = Log.read()
found = False
alerts = re.findall(r'.+\[alert\].+', log)
@@ -51,12 +52,16 @@ class Log:
if found and option.detailed:
print('skipped.')
@staticmethod
def findall(pattern, name=UNIT_LOG, flags=re.M):
return re.findall(pattern, Log.read(name), flags)
@staticmethod
def get_path(name=UNIT_LOG):
return f'{option.temp_dir}/{name}'
@staticmethod
def open(name=UNIT_LOG, encoding=None):
def open(name=UNIT_LOG, encoding='utf-8'):
file = open(Log.get_path(name), 'r', encoding=encoding, errors='ignore')
file.seek(Log.pos.get(name, 0))
@@ -71,7 +76,7 @@ class Log:
sys.stdout.flush()
if log is None:
log = Log.read(encoding='utf-8')
log = Log.read()
sys.stdout.write(log)
@@ -93,3 +98,16 @@ class Log:
pos = Log.pos.get(UNIT_LOG, 0)
Log.pos[UNIT_LOG] = Log.pos.get(name, 0)
Log.pos[name] = pos
@staticmethod
def wait_for_record(pattern, name=UNIT_LOG, wait=150, flags=re.M):
with Log.open(name) as file:
for _ in range(wait):
found = re.search(pattern, file.read(), flags)
if found is not None:
break
time.sleep(0.1)
return found

View File

@@ -4,6 +4,7 @@ import platform
class Options:
_options = {
'architecture': platform.architecture()[0],
'available': {'modules': {}, 'features': {}},
'is_privileged': os.geteuid() == 0,
'skip_alerts': [],
'skip_sanitizer': False,