summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrei Zeliankou <zelenkov@nginx.com>2023-06-12 14:16:59 +0100
committerAndrei Zeliankou <zelenkov@nginx.com>2023-06-12 14:16:59 +0100
commitce2405ec3dd97e8bdf8f63312e3c6ce59ef562d4 (patch)
tree818e60eb10d7f2be90f25003b3a2b347314e966f
parenta3b9b49cfb091410ca8f3c8d9df24d1fe184f8e0 (diff)
downloadunit-ce2405ec3dd97e8bdf8f63312e3c6ce59ef562d4.tar.gz
unit-ce2405ec3dd97e8bdf8f63312e3c6ce59ef562d4.tar.bz2
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.
-rw-r--r--test/conftest.py154
-rw-r--r--test/test_access_log.py4
-rw-r--r--test/test_asgi_application.py9
-rw-r--r--test/test_asgi_application_unix_abstract.py11
-rw-r--r--test/test_asgi_lifespan.py9
-rw-r--r--test/test_asgi_targets.py9
-rw-r--r--test/test_asgi_websockets.py9
-rw-r--r--test/test_client_ip.py4
-rw-r--r--test/test_configuration.py10
-rw-r--r--test/test_forwarded_header.py8
-rw-r--r--test/test_go_application.py4
-rw-r--r--test/test_go_isolation.py121
-rw-r--r--test/test_go_isolation_rootfs.py22
-rw-r--r--test/test_http_header.py4
-rw-r--r--test/test_java_application.py4
-rw-r--r--test/test_java_isolation_rootfs.py23
-rw-r--r--test/test_java_websockets.py4
-rw-r--r--test/test_njs.py8
-rw-r--r--test/test_njs_modules.py4
-rw-r--r--test/test_node_application.py4
-rw-r--r--test/test_node_es_modules.py10
-rw-r--r--test/test_node_websockets.py4
-rw-r--r--test/test_perl_application.py4
-rw-r--r--test/test_php_application.py4
-rw-r--r--test/test_php_basic.py4
-rw-r--r--test/test_php_isolation.py66
-rw-r--r--test/test_php_targets.py4
-rw-r--r--test/test_proxy.py4
-rw-r--r--test/test_proxy_chunked.py4
-rw-r--r--test/test_python_application.py9
-rw-r--r--test/test_python_basic.py3
-rw-r--r--test/test_python_environment.py4
-rw-r--r--test/test_python_isolation.py74
-rw-r--r--test/test_python_isolation_chroot.py14
-rw-r--r--test/test_python_procman.py4
-rw-r--r--test/test_python_targets.py4
-rw-r--r--test/test_reconfigure.py2
-rw-r--r--test/test_reconfigure_tls.py4
-rw-r--r--test/test_respawn.py4
-rw-r--r--test/test_return.py2
-rw-r--r--test/test_rewrite.py8
-rw-r--r--test/test_routing.py24
-rw-r--r--test/test_routing_tls.py4
-rw-r--r--test/test_ruby_application.py4
-rw-r--r--test/test_ruby_hooks.py4
-rw-r--r--test/test_ruby_isolation.py36
-rw-r--r--test/test_settings.py4
-rw-r--r--test/test_static.py2
-rw-r--r--test/test_static_chroot.py14
-rw-r--r--test/test_static_fallback.py2
-rw-r--r--test/test_static_mount.py9
-rw-r--r--test/test_static_share.py2
-rw-r--r--test/test_static_symlink.py4
-rw-r--r--test/test_static_types.py2
-rw-r--r--test/test_static_variables.py2
-rw-r--r--test/test_status.py4
-rw-r--r--test/test_status_tls.py4
-rw-r--r--test/test_tls.py4
-rw-r--r--test/test_tls_conf_command.py4
-rw-r--r--test/test_tls_session.py4
-rw-r--r--test/test_tls_sni.py4
-rw-r--r--test/test_tls_tickets.py4
-rw-r--r--test/test_unix_abstract.py10
-rw-r--r--test/test_upstreams_rr.py4
-rw-r--r--test/test_usr1.py4
-rw-r--r--test/test_variables.py2
-rw-r--r--test/unit/applications/proto.py3
-rw-r--r--test/unit/check/check_prerequisites.py63
-rw-r--r--test/unit/check/chroot.py40
-rw-r--r--test/unit/check/discover_available.py47
-rw-r--r--test/unit/check/go.py3
-rw-r--r--test/unit/check/isolation.py16
-rw-r--r--test/unit/check/njs.py3
-rw-r--r--test/unit/check/node.py10
-rw-r--r--test/unit/check/regex.py5
-rw-r--r--test/unit/check/tls.py5
-rw-r--r--test/unit/check/unix_abstract.py30
-rw-r--r--test/unit/log.py24
-rw-r--r--test/unit/option.py1
79 files changed, 524 insertions, 548 deletions
diff --git a/test/conftest.py b/test/conftest.py
index 84902dc2..4487f059 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -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']
+
+ for module, version in metafunc.module.prerequisites['modules'].items():
+ if module in available_modules and available_modules[module]:
+ available_versions = available_modules[module]
- if prereq_version == 'all':
+ 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 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)
+ discover_available(unit)
- # 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
@@ -713,6 +645,11 @@ def is_unsafe(request):
@pytest.fixture
+def require():
+ return check_prerequisites
+
+
+@pytest.fixture
def search_in_file():
def _search_in_file(pattern, name='unit.log', flags=re.M):
return re.search(pattern, Log.read(name), flags)
@@ -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
diff --git a/test/test_access_log.py b/test/test_access_log.py
index dd20d828..184a5fdf 100644
--- a/test/test_access_log.py
+++ b/test/test_access_log.py
@@ -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'}}
+class TestAccessLog(TestApplicationPython):
def load(self, script):
super().load(script)
diff --git a/test/test_asgi_application.py b/test/test_asgi_application.py
index e2f74dd9..1f98b170 100644
--- a/test/test_asgi_application.py
+++ b/test/test_asgi_application.py
@@ -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):
diff --git a/test/test_asgi_application_unix_abstract.py b/test/test_asgi_application_unix_abstract.py
index 2ca7839f..e473154a 100644
--- a/test/test_asgi_application_unix_abstract.py
+++ b/test/test_asgi_application_unix_abstract.py
@@ -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):
diff --git a/test/test_asgi_lifespan.py b/test/test_asgi_lifespan.py
index d3ac1721..0d0cb162 100644
--- a/test/test_asgi_lifespan.py
+++ b/test/test_asgi_lifespan.py
@@ -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):
diff --git a/test/test_asgi_targets.py b/test/test_asgi_targets.py
index 5afc7079..0c9d9ad0 100644
--- a/test/test_asgi_targets.py
+++ b/test/test_asgi_targets.py
@@ -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)
diff --git a/test/test_asgi_websockets.py b/test/test_asgi_websockets.py
index 3fab318c..fa71274b 100644
--- a/test/test_asgi_websockets.py
+++ b/test/test_asgi_websockets.py
@@ -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()
diff --git a/test/test_client_ip.py b/test/test_client_ip.py
index 59e170f1..347083bc 100644
--- a/test/test_client_ip.py
+++ b/test/test_client_ip.py
@@ -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'}}
+class TestClientIP(TestApplicationPython):
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
self.load('client_ip')
diff --git a/test/test_configuration.py b/test/test_configuration.py
index e78a2957..0c48ad57 100644
--- a/test/test_configuration.py
+++ b/test/test_configuration.py
@@ -3,10 +3,10 @@ import socket
import pytest
from unit.control import TestControl
+prerequisites = {'modules': {'python': 'any'}}
-class TestConfiguration(TestControl):
- prerequisites = {'modules': {'python': 'any'}}
+class TestConfiguration(TestControl):
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(
{
diff --git a/test/test_forwarded_header.py b/test/test_forwarded_header.py
index 9c6e0395..6fe61d23 100644
--- a/test/test_forwarded_header.py
+++ b/test/test_forwarded_header.py
@@ -1,10 +1,10 @@
import pytest
from unit.applications.lang.python import TestApplicationPython
+prerequisites = {'modules': {'python': 'any'}}
-class TestForwardedHeader(TestApplicationPython):
- prerequisites = {'modules': {'python': 'any'}}
+class TestForwardedHeader(TestApplicationPython):
@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'
diff --git a/test/test_go_application.py b/test/test_go_application.py
index 062d0cb5..36ddbc3a 100644
--- a/test/test_go_application.py
+++ b/test/test_go_application.py
@@ -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'}}
+class TestGoApplication(TestApplicationGo):
@pytest.fixture(autouse=True)
def setup_method_fixture(self, skip_alert):
skip_alert(r'\[unit\] close\(\d+\) failed: Bad file descriptor')
diff --git a/test/test_go_isolation.py b/test/test_go_isolation.py
index 8216a6fe..ba997fd3 100644
--- a/test/test_go_isolation.py
+++ b/test/test_go_isolation.py
@@ -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']}
+class TestGoIsolation(TestApplicationGo):
@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}
diff --git a/test/test_go_isolation_rootfs.py b/test/test_go_isolation_rootfs.py
index b4d73ec8..aa07b80d 100644
--- a/test/test_go_isolation_rootfs.py
+++ b/test/test_go_isolation_rootfs.py
@@ -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'}}
+class TestGoIsolationRootfs(TestApplicationGo):
@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)
diff --git a/test/test_http_header.py b/test/test_http_header.py
index cae5e9b8..3202ae9c 100644
--- a/test/test_http_header.py
+++ b/test/test_http_header.py
@@ -1,10 +1,10 @@
import pytest
from unit.applications.lang.python import TestApplicationPython
+prerequisites = {'modules': {'python': 'any'}}
-class TestHTTPHeader(TestApplicationPython):
- prerequisites = {'modules': {'python': 'any'}}
+class TestHTTPHeader(TestApplicationPython):
def test_http_header_value_leading_sp(self):
self.load('custom_header')
diff --git a/test/test_java_application.py b/test/test_java_application.py
index 85cf71a1..4296b1e5 100644
--- a/test/test_java_application.py
+++ b/test/test_java_application.py
@@ -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'}}
+class TestJavaApplication(TestApplicationJava):
def test_java_conf_error(self, temp_dir, skip_alert):
skip_alert(
r'realpath.*failed',
diff --git a/test/test_java_isolation_rootfs.py b/test/test_java_isolation_rootfs.py
index 28bc4a0d..bbd2c915 100644
--- a/test/test_java_isolation_rootfs.py
+++ b/test/test_java_isolation_rootfs.py
@@ -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'}}
+class TestJavaIsolationRootfs(TestApplicationJava):
@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)
diff --git a/test/test_java_websockets.py b/test/test_java_websockets.py
index ce8f6e83..573e3525 100644
--- a/test/test_java_websockets.py
+++ b/test/test_java_websockets.py
@@ -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'}}
+class TestJavaWebsockets(TestApplicationJava):
ws = TestApplicationWebsocket()
@pytest.fixture(autouse=True)
diff --git a/test/test_njs.py b/test/test_njs.py
index 86a8a4ba..dc0ff921 100644
--- a/test/test_njs.py
+++ b/test/test_njs.py
@@ -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'}}
+class TestNJS(TestApplicationProto):
@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"}}],
}
)
diff --git a/test/test_njs_modules.py b/test/test_njs_modules.py
index ce592fe4..10ea03a7 100644
--- a/test/test_njs_modules.py
+++ b/test/test_njs_modules.py
@@ -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'}}
+class TestNJSModules(TestApplicationProto):
def njs_script_load(self, module, name=None, expect='success'):
if name is None:
name = module
diff --git a/test/test_node_application.py b/test/test_node_application.py
index 321e7aa9..8b0b90ea 100644
--- a/test/test_node_application.py
+++ b/test/test_node_application.py
@@ -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'}}
+class TestNodeApplication(TestApplicationNode):
def assert_basic_application(self):
resp = self.get()
assert resp['headers']['Content-Type'] == 'text/plain', 'basic header'
diff --git a/test/test_node_es_modules.py b/test/test_node_es_modules.py
index 8a9cb181..10f9eab0 100644
--- a/test/test_node_es_modules.py
+++ b/test/test_node_es_modules.py
@@ -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')
- }
- }
+class TestNodeESModules(TestApplicationNode):
es_modules = True
ws = TestApplicationWebsocket()
diff --git a/test/test_node_websockets.py b/test/test_node_websockets.py
index fc263d63..d59bc2ef 100644
--- a/test/test_node_websockets.py
+++ b/test/test_node_websockets.py
@@ -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'}}
+class TestNodeWebsockets(TestApplicationNode):
ws = TestApplicationWebsocket()
@pytest.fixture(autouse=True)
diff --git a/test/test_perl_application.py b/test/test_perl_application.py
index 17bd0fea..91737843 100644
--- a/test/test_perl_application.py
+++ b/test/test_perl_application.py
@@ -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'}}
+class TestPerlApplication(TestApplicationPerl):
def test_perl_application(self, date_to_sec_epoch, sec_epoch):
self.load('variables')
diff --git a/test/test_php_application.py b/test/test_php_application.py
index faf1d18b..548c0c9d 100644
--- a/test/test_php_application.py
+++ b/test/test_php_application.py
@@ -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'}}
+class TestPHPApplication(TestApplicationPHP):
def before_disable_functions(self):
body = self.get()['body']
diff --git a/test/test_php_basic.py b/test/test_php_basic.py
index bcd66173..1a2a00e6 100644
--- a/test/test_php_basic.py
+++ b/test/test_php_basic.py
@@ -1,9 +1,9 @@
from unit.control import TestControl
+prerequisites = {'modules': {'php': 'any'}}
-class TestPHPBasic(TestControl):
- prerequisites = {'modules': {'php': 'any'}}
+class TestPHPBasic(TestControl):
conf_app = {
"app": {
"type": "php",
diff --git a/test/test_php_isolation.py b/test/test_php_isolation.py
index aebeefa6..e8471015 100644
--- a/test/test_php_isolation.py
+++ b/test/test_php_isolation.py
@@ -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')
+class TestPHPIsolation(TestApplicationPHP):
+ 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,
diff --git a/test/test_php_targets.py b/test/test_php_targets.py
index e74f2ec6..db9b56ce 100644
--- a/test/test_php_targets.py
+++ b/test/test_php_targets.py
@@ -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'}}
+class TestPHPTargets(TestApplicationPHP):
def test_php_application_targets(self):
targets_dir = f"{option.test_dir}/php/targets"
assert 'success' in self.conf(
diff --git a/test/test_proxy.py b/test/test_proxy.py
index 23b4a6a4..ad14c23d 100644
--- a/test/test_proxy.py
+++ b/test/test_proxy.py
@@ -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'}}
+class TestProxy(TestApplicationPython):
SERVER_PORT = 7999
@pytest.fixture(autouse=True)
diff --git a/test/test_proxy_chunked.py b/test/test_proxy_chunked.py
index b5198e9c..75203073 100644
--- a/test/test_proxy_chunked.py
+++ b/test/test_proxy_chunked.py
@@ -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'}}
+class TestProxyChunked(TestApplicationPython):
SERVER_PORT = 7999
@pytest.fixture(autouse=True)
diff --git a/test/test_python_application.py b/test/test_python_application.py
index f67dc24f..d846c1e3 100644
--- a/test/test_python_application.py
+++ b/test/test_python_application.py
@@ -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'}}
+class TestPythonApplication(TestApplicationPython):
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
diff --git a/test/test_python_basic.py b/test/test_python_basic.py
index e661a89c..5783e78d 100644
--- a/test/test_python_basic.py
+++ b/test/test_python_basic.py
@@ -1,8 +1,9 @@
from unit.control import TestControl
+prerequisites = {'modules': {'python': 'any'}}
+
class TestPythonBasic(TestControl):
- prerequisites = {'modules': {'python': 'any'}}
conf_app = {
"app": {
diff --git a/test/test_python_environment.py b/test/test_python_environment.py
index bce72c4d..a57e3760 100644
--- a/test/test_python_environment.py
+++ b/test/test_python_environment.py
@@ -1,9 +1,9 @@
from unit.applications.lang.python import TestApplicationPython
+prerequisites = {'modules': {'python': 'any'}}
-class TestPythonEnvironment(TestApplicationPython):
- prerequisites = {'modules': {'python': 'any'}}
+class TestPythonEnvironment(TestApplicationPython):
def test_python_environment_name_null(self):
self.load('environment')
diff --git a/test/test_python_isolation.py b/test/test_python_isolation.py
index 506d20b7..a9ed2900 100644
--- a/test/test_python_isolation.py
+++ b/test/test_python_isolation.py
@@ -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']}
+class TestPythonIsolation(TestApplicationPython):
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'
diff --git a/test/test_python_isolation_chroot.py b/test/test_python_isolation_chroot.py
index dd0dede0..fd16c1fb 100644
--- a/test/test_python_isolation_chroot.py
+++ b/test/test_python_isolation_chroot.py
@@ -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,
- }
+class TestPythonIsolation(TestApplicationPython):
+ def test_python_isolation_chroot(self, temp_dir):
+ isolation = {'rootfs': temp_dir}
self.load('ns_inspect', isolation=isolation)
diff --git a/test/test_python_procman.py b/test/test_python_procman.py
index 82766d65..86f70ba9 100644
--- a/test/test_python_procman.py
+++ b/test/test_python_procman.py
@@ -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'}}
+class TestPythonProcman(TestApplicationPython):
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
self.app_name = f'app-{temp_dir.split("/")[-1]}'
diff --git a/test/test_python_targets.py b/test/test_python_targets.py
index f55609ba..dc0dc529 100644
--- a/test/test_python_targets.py
+++ b/test/test_python_targets.py
@@ -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'}}
+class TestPythonTargets(TestApplicationPython):
def test_python_targets(self):
python_dir = f'{option.test_dir}/python'
diff --git a/test/test_reconfigure.py b/test/test_reconfigure.py
index feb027aa..ae19db9d 100644
--- a/test/test_reconfigure.py
+++ b/test/test_reconfigure.py
@@ -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(
diff --git a/test/test_reconfigure_tls.py b/test/test_reconfigure_tls.py
index 0f92a419..310f800a 100644
--- a/test/test_reconfigure_tls.py
+++ b/test/test_reconfigure_tls.py
@@ -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'}}
+class TestReconfigureTLS(TestApplicationTLS):
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
if 'HAS_TLSv1_2' not in dir(ssl) or not ssl.HAS_TLSv1_2:
diff --git a/test/test_respawn.py b/test/test_respawn.py
index 2d01cf3b..84bddbaa 100644
--- a/test/test_respawn.py
+++ b/test/test_respawn.py
@@ -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'}}
+class TestRespawn(TestApplicationPython):
PATTERN_ROUTER = 'unit: router'
PATTERN_CONTROLLER = 'unit: controller'
diff --git a/test/test_return.py b/test/test_return.py
index 71b1242c..478f8393 100644
--- a/test/test_return.py
+++ b/test/test_return.py
@@ -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(
diff --git a/test/test_rewrite.py b/test/test_rewrite.py
index a7b8e975..c1844165 100644
--- a/test/test_rewrite.py
+++ b/test/test_rewrite.py
@@ -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
diff --git a/test/test_routing.py b/test/test_routing.py
index 41de6f51..715033dd 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -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'}}
+class TestRouting(TestApplicationPython):
@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]"})
diff --git a/test/test_routing_tls.py b/test/test_routing_tls.py
index 76cfb485..1ba79986 100644
--- a/test/test_routing_tls.py
+++ b/test/test_routing_tls.py
@@ -1,9 +1,9 @@
from unit.applications.tls import TestApplicationTLS
+prerequisites = {'modules': {'openssl': 'any'}}
-class TestRoutingTLS(TestApplicationTLS):
- prerequisites = {'modules': {'openssl': 'any'}}
+class TestRoutingTLS(TestApplicationTLS):
def test_routes_match_scheme_tls(self):
self.certificate()
diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py
index 166ca1ed..424d6764 100644
--- a/test/test_ruby_application.py
+++ b/test/test_ruby_application.py
@@ -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'}}
+class TestRubyApplication(TestApplicationRuby):
def test_ruby_application(self, date_to_sec_epoch, sec_epoch):
self.load('variables')
diff --git a/test/test_ruby_hooks.py b/test/test_ruby_hooks.py
index 078e5723..6bd4c808 100644
--- a/test/test_ruby_hooks.py
+++ b/test/test_ruby_hooks.py
@@ -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'}}
+class TestRubyHooks(TestApplicationRuby):
def _wait_cookie(self, pattern, count):
return waitforglob(
f'{option.temp_dir}/ruby/hooks/cookie_{pattern}', count
diff --git a/test/test_ruby_isolation.py b/test/test_ruby_isolation.py
index 3ec06d86..82a12dff 100644
--- a/test/test_ruby_isolation.py
+++ b/test/test_ruby_isolation.py
@@ -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')
+class TestRubyIsolation(TestApplicationRuby):
+ 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,
diff --git a/test/test_settings.py b/test/test_settings.py
index 4b139069..0a48da5e 100644
--- a/test/test_settings.py
+++ b/test/test_settings.py
@@ -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'}}
+class TestSettings(TestApplicationPython):
def sysctl(self):
try:
out = subprocess.check_output(
diff --git a/test/test_static.py b/test/test_static.py
index 48004661..60a066a0 100644
--- a/test/test_static.py
+++ b/test/test_static.py
@@ -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')
diff --git a/test/test_static_chroot.py b/test/test_static_chroot.py
index 812e14ea..21a47a78 100644
--- a/test/test_static_chroot.py
+++ b/test/test_static_chroot.py
@@ -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']}
+class TestStaticChroot(TestApplicationProto):
@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'
diff --git a/test/test_static_fallback.py b/test/test_static_fallback.py
index 75012bbb..5c3ec7d3 100644
--- a/test/test_static_fallback.py
+++ b/test/test_static_fallback.py
@@ -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'
diff --git a/test/test_static_mount.py b/test/test_static_mount.py
index 406922b1..45ae8fa7 100644
--- a/test/test_static_mount.py
+++ b/test/test_static_mount.py
@@ -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']}
+class TestStaticMount(TestApplicationProto):
@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')
diff --git a/test/test_static_share.py b/test/test_static_share.py
index 0166f1f0..2eb63659 100644
--- a/test/test_static_share.py
+++ b/test/test_static_share.py
@@ -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')
diff --git a/test/test_static_symlink.py b/test/test_static_symlink.py
index 13d67bc7..de65b85b 100644
--- a/test/test_static_symlink.py
+++ b/test/test_static_symlink.py
@@ -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']}
+class TestStaticSymlink(TestApplicationProto):
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
os.makedirs(f'{temp_dir}/assets/dir/dir')
diff --git a/test/test_static_types.py b/test/test_static_types.py
index 28ab28e6..125da181 100644
--- a/test/test_static_types.py
+++ b/test/test_static_types.py
@@ -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()
diff --git a/test/test_static_variables.py b/test/test_static_variables.py
index 370c3e6f..f69a936e 100644
--- a/test/test_static_variables.py
+++ b/test/test_static_variables.py
@@ -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')
diff --git a/test/test_status.py b/test/test_status.py
index f8885dff..2fc235d4 100644
--- a/test/test_status.py
+++ b/test/test_status.py
@@ -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'}}
+class TestStatus(TestApplicationPython):
def check_connections(self, accepted, active, idle, closed):
assert Status.get('/connections') == {
'accepted': accepted,
diff --git a/test/test_status_tls.py b/test/test_status_tls.py
index dc3d68da..e38a2f43 100644
--- a/test/test_status_tls.py
+++ b/test/test_status_tls.py
@@ -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'}}
+class TestStatusTLS(TestApplicationTLS):
def test_status_tls_requests(self):
self.certificate()
diff --git a/test/test_tls.py b/test/test_tls.py
index 5c7b31c1..ca9d5b07 100644
--- a/test/test_tls.py
+++ b/test/test_tls.py
@@ -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'}}
+class TestTLS(TestApplicationTLS):
def add_tls(self, application='empty', cert='default', port=7080):
assert 'success' in self.conf(
{
diff --git a/test/test_tls_conf_command.py b/test/test_tls_conf_command.py
index f2238574..a7500551 100644
--- a/test/test_tls_conf_command.py
+++ b/test/test_tls_conf_command.py
@@ -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'}}
+class TestTLSConfCommand(TestApplicationTLS):
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
self.certificate()
diff --git a/test/test_tls_session.py b/test/test_tls_session.py
index 84637dea..997b7148 100644
--- a/test/test_tls_session.py
+++ b/test/test_tls_session.py
@@ -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'}}
+class TestTLSSession(TestApplicationTLS):
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
self.certificate()
diff --git a/test/test_tls_sni.py b/test/test_tls_sni.py
index 09e212bd..1c3afbea 100644
--- a/test/test_tls_sni.py
+++ b/test/test_tls_sni.py
@@ -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'}}
+class TestTLSSNI(TestApplicationTLS):
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
self._load_conf(
diff --git a/test/test_tls_tickets.py b/test/test_tls_tickets.py
index 3962316e..acb03428 100644
--- a/test/test_tls_tickets.py
+++ b/test/test_tls_tickets.py
@@ -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'}}
+class TestTLSTicket(TestApplicationTLS):
ticket = 'U1oDTh11mMxODuw12gS0EXX1E/PkZG13cJNQ6m5+6BGlfPTjNlIEw7PSVU3X1gTE'
ticket2 = '5AV0DSYIYbZWZQB7fCnTHZmMxtotb/aXjam+n2XS79lTvX3Tq9xGqpC8XKNEF2lt'
ticket80 = '6Pfil8lv/k8zf8MndPpfXaO5EAV6dhME6zs6CfUyq2yziynQwSywtKQMqHGnJ2HR\
diff --git a/test/test_unix_abstract.py b/test/test_unix_abstract.py
index c562487b..a6b7cd99 100644
--- a/test/test_unix_abstract.py
+++ b/test/test_unix_abstract.py
@@ -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'],
- }
+class TestUnixAbstract(TestApplicationPython):
def test_unix_abstract_source(self):
addr = '\0sock'
diff --git a/test/test_upstreams_rr.py b/test/test_upstreams_rr.py
index 2bb23be0..cf7aa67d 100644
--- a/test/test_upstreams_rr.py
+++ b/test/test_upstreams_rr.py
@@ -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'}}
+class TestUpstreamsRR(TestApplicationPython):
@pytest.fixture(autouse=True)
def setup_method_fixture(self):
assert 'success' in self.conf(
diff --git a/test/test_usr1.py b/test/test_usr1.py
index 30160a88..160cef77 100644
--- a/test/test_usr1.py
+++ b/test/test_usr1.py
@@ -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'}}
+class TestUSR1(TestApplicationPython):
def test_usr1_access_log(
self, search_in_file, temp_dir, unit_pid, wait_for_record
):
diff --git a/test/test_variables.py b/test/test_variables.py
index 45e193cc..b93f8b36 100644
--- a/test/test_variables.py
+++ b/test/test_variables.py
@@ -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(
diff --git a/test/unit/applications/proto.py b/test/unit/applications/proto.py
index 00ea44b2..354c31af 100644
--- a/test/unit/applications/proto.py
+++ b/test/unit/applications/proto.py
@@ -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
diff --git a/test/unit/check/check_prerequisites.py b/test/unit/check/check_prerequisites.py
new file mode 100644
index 00000000..44c3f10f
--- /dev/null
+++ b/test/unit/check/check_prerequisites.py
@@ -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,
+ )
diff --git a/test/unit/check/chroot.py b/test/unit/check/chroot.py
index 1b7aae90..ac5a91d0 100644
--- a/test/unit/check/chroot.py
+++ b/test/unit/check/chroot.py
@@ -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
diff --git a/test/unit/check/discover_available.py b/test/unit/check/discover_available.py
new file mode 100644
index 00000000..0942581b
--- /dev/null
+++ b/test/unit/check/discover_available.py
@@ -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()
diff --git a/test/unit/check/go.py b/test/unit/check/go.py
index 09ae641d..469bef1d 100644
--- a/test/unit/check/go.py
+++ b/test/unit/check/go.py
@@ -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
diff --git a/test/unit/check/isolation.py b/test/unit/check/isolation.py
index 829d956b..aaa9b166 100644
--- a/test/unit/check/isolation.py
+++ b/test/unit/check/isolation.py
@@ -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
diff --git a/test/unit/check/njs.py b/test/unit/check/njs.py
index 433473a1..363a1b62 100644
--- a/test/unit/check/njs.py
+++ b/test/unit/check/njs.py
@@ -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)
diff --git a/test/unit/check/node.py b/test/unit/check/node.py
index dd59e7a4..6a3d581f 100644
--- a/test/unit/check/node.py
+++ b/test/unit/check/node.py
@@ -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
diff --git a/test/unit/check/regex.py b/test/unit/check/regex.py
index 51cf966b..83e93f2d 100644
--- a/test/unit/check/regex.py
+++ b/test/unit/check/regex.py
@@ -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)
diff --git a/test/unit/check/tls.py b/test/unit/check/tls.py
index 53ce5ffc..9cc2a5f9 100644
--- a/test/unit/check/tls.py
+++ b/test/unit/check/tls.py
@@ -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)
diff --git a/test/unit/check/unix_abstract.py b/test/unit/check/unix_abstract.py
index aadde43a..780e0212 100644
--- a/test/unit/check/unix_abstract.py
+++ b/test/unit/check/unix_abstract.py
@@ -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
diff --git a/test/unit/log.py b/test/unit/log.py
index c98054d5..7d7e355a 100644
--- a/test/unit/log.py
+++ b/test/unit/log.py
@@ -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)
@@ -52,11 +53,15 @@ class Log:
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
diff --git a/test/unit/option.py b/test/unit/option.py
index e00a043a..6bd0c836 100644
--- a/test/unit/option.py
+++ b/test/unit/option.py
@@ -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,