aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/allure-pytest/allure_pytest/utils.py
diff options
context:
space:
mode:
authoriddqd <iddqd@yandex-team.com>2024-05-13 17:19:30 +0300
committeriddqd <iddqd@yandex-team.com>2024-05-13 17:28:44 +0300
commit84d127b9b7e96ba4352e3f5ddc9222aee9a66053 (patch)
tree2ebb2689abf65e68dfe92a3ca9b161b4b6ae183f /contrib/python/allure-pytest/allure_pytest/utils.py
parentb7deb7f0b71db7419781d1b0357dfa443ccc3ff1 (diff)
downloadydb-84d127b9b7e96ba4352e3f5ddc9222aee9a66053.tar.gz
Add allure support to ydb github export
d6cba27d09fb5e50a99c36070a6a3545c8393ea1
Diffstat (limited to 'contrib/python/allure-pytest/allure_pytest/utils.py')
-rw-r--r--contrib/python/allure-pytest/allure_pytest/utils.py197
1 files changed, 197 insertions, 0 deletions
diff --git a/contrib/python/allure-pytest/allure_pytest/utils.py b/contrib/python/allure-pytest/allure_pytest/utils.py
new file mode 100644
index 0000000000..1e07cb492c
--- /dev/null
+++ b/contrib/python/allure-pytest/allure_pytest/utils.py
@@ -0,0 +1,197 @@
+import pytest
+from itertools import chain, islice
+from allure_commons.utils import represent, SafeFormatter, md5
+from allure_commons.utils import format_exception, format_traceback
+from allure_commons.model2 import Status
+from allure_commons.model2 import StatusDetails
+from allure_commons.types import LabelType
+
+
+ALLURE_DESCRIPTION_MARK = 'allure_description'
+ALLURE_DESCRIPTION_HTML_MARK = 'allure_description_html'
+ALLURE_LABEL_MARK = 'allure_label'
+ALLURE_LINK_MARK = 'allure_link'
+ALLURE_UNIQUE_LABELS = [
+ LabelType.SEVERITY,
+ LabelType.FRAMEWORK,
+ LabelType.HOST,
+ LabelType.SUITE,
+ LabelType.PARENT_SUITE,
+ LabelType.SUB_SUITE
+]
+
+
+def get_marker_value(item, keyword):
+ marker = item.get_closest_marker(keyword)
+ return marker.args[0] if marker and marker.args else None
+
+
+def allure_title(item):
+ return getattr(
+ getattr(item, "obj", None),
+ "__allure_display_name__",
+ None
+ )
+
+
+def allure_description(item):
+ description = get_marker_value(item, ALLURE_DESCRIPTION_MARK)
+ if description:
+ return description
+ elif hasattr(item, 'function'):
+ return item.function.__doc__
+
+
+def allure_description_html(item):
+ return get_marker_value(item, ALLURE_DESCRIPTION_HTML_MARK)
+
+
+def allure_label(item, label):
+ labels = []
+ for mark in item.iter_markers(name=ALLURE_LABEL_MARK):
+ if mark.kwargs.get("label_type") == label:
+ labels.extend(mark.args)
+ return labels
+
+
+def allure_labels(item):
+ unique_labels = dict()
+ labels = set()
+ for mark in item.iter_markers(name=ALLURE_LABEL_MARK):
+ label_type = mark.kwargs["label_type"]
+ if label_type in ALLURE_UNIQUE_LABELS:
+ if label_type not in unique_labels.keys():
+ unique_labels[label_type] = mark.args[0]
+ else:
+ for arg in mark.args:
+ labels.add((label_type, arg))
+ for k, v in unique_labels.items():
+ labels.add((k, v))
+ return labels
+
+
+def allure_links(item):
+ for mark in item.iter_markers(name=ALLURE_LINK_MARK):
+ yield (mark.kwargs["link_type"], mark.args[0], mark.kwargs["name"])
+
+
+def format_allure_link(config, url, link_type):
+ pattern = dict(config.option.allure_link_pattern).get(link_type, '{}')
+ return pattern.format(url)
+
+
+def pytest_markers(item):
+ for keyword in item.keywords.keys():
+ if any([keyword.startswith('allure_'), keyword == 'parametrize']):
+ continue
+ marker = item.get_closest_marker(keyword)
+ if marker is None:
+ continue
+
+ yield mark_to_str(marker)
+
+
+def mark_to_str(marker):
+ args = [represent(arg) for arg in marker.args]
+ kwargs = [f'{key}={represent(value)}' for key, value in marker.kwargs.items()]
+ if marker.name in ('filterwarnings', 'skip', 'skipif', 'xfail', 'usefixtures', 'tryfirst', 'trylast'):
+ markstr = f'@pytest.mark.{marker.name}'
+ else:
+ markstr = str(marker.name)
+ if args or kwargs:
+ parameters = ', '.join(args + kwargs)
+ markstr = f'{markstr}({parameters})'
+ return markstr
+
+
+def allure_package(item):
+ parts = item.nodeid.split('::')
+ path = parts[0].rsplit('.', 1)[0]
+ return path.replace('/', '.')
+
+
+def allure_name(item, parameters, param_id=None):
+ name = item.name
+ title = allure_title(item)
+ param_id_kwargs = {}
+ if param_id:
+ # if param_id is an ASCII string, it could have been encoded by pytest (_pytest.compat.ascii_escaped)
+ if param_id.isascii():
+ param_id = param_id.encode().decode("unicode-escape")
+ param_id_kwargs["param_id"] = param_id
+ return SafeFormatter().format(
+ title,
+ **{**param_id_kwargs, **parameters, **item.funcargs}
+ ) if title else name
+
+
+def allure_full_name(item: pytest.Item):
+ package = allure_package(item)
+ class_name = f".{item.parent.name}" if isinstance(item.parent, pytest.Class) else ''
+ test = item.originalname if isinstance(item, pytest.Function) else item.name.split("[")[0]
+ full_name = f'{package}{class_name}#{test}'
+ return full_name
+
+
+def allure_suite_labels(item):
+ head, possibly_clazz, tail = islice(chain(item.nodeid.split('::'), [None], [None]), 3)
+ clazz = possibly_clazz if tail else None
+ file_name, path = islice(chain(reversed(head.rsplit('/', 1)), [None]), 2)
+ module = file_name.split('.')[0]
+ package = path.replace('/', '.') if path else None
+ pairs = dict(zip([LabelType.PARENT_SUITE, LabelType.SUITE, LabelType.SUB_SUITE], [package, module, clazz]))
+ labels = dict(allure_labels(item))
+ default_suite_labels = []
+ for label, value in pairs.items():
+ if label not in labels.keys() and value:
+ default_suite_labels.append((label, value))
+
+ return default_suite_labels
+
+
+def get_outcome_status(outcome):
+ _, exception, _ = outcome.excinfo or (None, None, None)
+ return get_status(exception)
+
+
+def get_outcome_status_details(outcome):
+ exception_type, exception, exception_traceback = outcome.excinfo or (None, None, None)
+ return get_status_details(exception_type, exception, exception_traceback)
+
+
+def get_status(exception):
+ if exception:
+ if isinstance(exception, AssertionError) or isinstance(exception, pytest.fail.Exception):
+ return Status.FAILED
+ elif isinstance(exception, pytest.skip.Exception):
+ return Status.SKIPPED
+ return Status.BROKEN
+ else:
+ return Status.PASSED
+
+
+def get_status_details(exception_type, exception, exception_traceback):
+ message = format_exception(exception_type, exception)
+ trace = format_traceback(exception_traceback)
+ return StatusDetails(message=message, trace=trace) if message or trace else None
+
+
+def get_pytest_report_status(pytest_report):
+ pytest_statuses = ('failed', 'passed', 'skipped')
+ statuses = (Status.FAILED, Status.PASSED, Status.SKIPPED)
+ for pytest_status, status in zip(pytest_statuses, statuses):
+ if getattr(pytest_report, pytest_status):
+ return status
+
+
+def get_history_id(full_name, parameters, original_values):
+ return md5(
+ full_name,
+ *(original_values.get(p.name, p.value) for p in sorted(
+ filter(
+ lambda p: not p.excluded,
+ parameters
+ ),
+ key=lambda p: p.name
+ ))
+ )