#!/usr/bin/python3
# SPDX-FileCopyrightText: 2021-2025 Univention GmbH
# SPDX-License-Identifier: AGPL-3.0-only
"""conftest plugin for pytest runner in ucs-test"""
from collections.abc import Callable
from typing import TypeVar
import pytest
from _pytest.config import Config
from _pytest.config.argparsing import Parser
_RT = TypeVar("_RT")
_F = Callable[..., _RT] # Py3.10+: Callable[ParamSpec, _RT]
[docs]
def pytest_addoption(parser: Parser) -> None:
parser.addoption(
"--ucs-test-tags-prohibited",
action="append",
metavar="TAG",
default=[],
help="Skip tests with this tag",
)
parser.addoption(
"--ucs-test-tags-required",
action="append",
metavar="TAG",
default=[],
help="Only run tests with this tag",
)
parser.addoption(
"--ucs-test-tags-ignore",
action="append",
metavar="TAG",
default=[],
help="Neither require nor prohibit this tag",
)
parser.addoption(
"--ucs-test-default-tags",
action="append",
metavar="TAG",
default=[],
help="The tags for the entire test case, if the test function does not specify any",
)
parser.addoption(
"--ucs-test-exposure",
choices=('safe', 'careful', 'dangerous'),
help="Run more dangerous tests",
)
parser.addoption(
"--ucs-test-default-exposure",
choices=('safe', 'careful', 'dangerous'),
help="The exposure of the test",
)
[docs]
def pytest_runtest_setup(item: pytest.Item) -> None:
check_tags(item)
check_roles(item)
check_exposure(item)
[docs]
def check_roles(item: pytest.Item) -> None:
from univention.config_registry import ucr
from univention.testing.data import CheckRoles
roles_required = {
role
for mark in item.iter_markers(name="roles")
for role in mark.args
}
roles_prohibited = {
role
for mark in item.iter_markers(name="roles_not")
for role in mark.args
}
overlap = roles_required & roles_prohibited
if overlap:
roles = roles_required - roles_prohibited
elif roles_required:
roles = roles_required
else:
roles = set(CheckRoles.ROLES) - roles_prohibited
if ucr['server/role'] not in roles:
pytest.skip('Wrong role: %s not in (%s)' % (ucr['server/role'], ','.join(roles)))
[docs]
def check_exposure(item: pytest.Item) -> None:
from univention.testing.data import CheckExposure
required_exposure = item.config.getoption("--ucs-test-exposure")
if not required_exposure:
return
try:
exposure = next(mark.args[0] for mark in item.iter_markers(name="exposure"))
except StopIteration:
exposure = item.config.getoption("--ucs-test-default-exposure", "safe")
if CheckExposure.STATES.index(exposure) > CheckExposure.STATES.index(required_exposure):
pytest.skip(f'Too dangerous: {exposure} > {required_exposure}')
[docs]
def locale_available(*locales: str) -> Callable[[_F], _F]: # Py3.10+: ParamSpec
from univention.config_registry import ucr
available = {locale.split(".")[0] for locale in ucr.get("locale", "").split()}
required = set(locales) or {"de_DE", "en_US"}
return pytest.mark.skipif(
available < required,
reason="Required locales are not available",
)