aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/allure-pytest/allure_pytest/plugin.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/plugin.py
parentb7deb7f0b71db7419781d1b0357dfa443ccc3ff1 (diff)
downloadydb-84d127b9b7e96ba4352e3f5ddc9222aee9a66053.tar.gz
Add allure support to ydb github export
d6cba27d09fb5e50a99c36070a6a3545c8393ea1
Diffstat (limited to 'contrib/python/allure-pytest/allure_pytest/plugin.py')
-rw-r--r--contrib/python/allure-pytest/allure_pytest/plugin.py236
1 files changed, 236 insertions, 0 deletions
diff --git a/contrib/python/allure-pytest/allure_pytest/plugin.py b/contrib/python/allure-pytest/allure_pytest/plugin.py
new file mode 100644
index 0000000000..2771722ffc
--- /dev/null
+++ b/contrib/python/allure-pytest/allure_pytest/plugin.py
@@ -0,0 +1,236 @@
+import argparse
+
+import allure
+import allure_commons
+import os
+
+from allure_commons.types import LabelType, Severity
+from allure_commons.logger import AllureFileLogger
+from allure_commons.utils import get_testplan
+
+from allure_pytest.utils import allure_label, allure_labels, allure_full_name
+from allure_pytest.helper import AllureTestHelper, AllureTitleHelper
+from allure_pytest.listener import AllureListener
+
+from allure_pytest.utils import ALLURE_DESCRIPTION_MARK, ALLURE_DESCRIPTION_HTML_MARK
+from allure_pytest.utils import ALLURE_LABEL_MARK, ALLURE_LINK_MARK
+
+
+def pytest_addoption(parser):
+ parser.getgroup("reporting").addoption('--alluredir',
+ action="store",
+ dest="allure_report_dir",
+ metavar="DIR",
+ default=None,
+ help="Generate Allure report in the specified directory (may not exist)")
+
+ parser.getgroup("reporting").addoption('--clean-alluredir',
+ action="store_true",
+ dest="clean_alluredir",
+ help="Clean alluredir folder if it exists")
+
+ parser.getgroup("reporting").addoption('--allure-no-capture',
+ action="store_false",
+ dest="attach_capture",
+ help="Do not attach pytest captured logging/stdout/stderr to report")
+
+ parser.getgroup("reporting").addoption('--inversion',
+ action="store",
+ dest="inversion",
+ default=False,
+ help="Run tests not in testplan")
+
+ def label_type(type_name, legal_values=set()):
+ def a_label_type(string):
+ atoms = set(string.split(','))
+ if type_name is LabelType.SEVERITY:
+ if not atoms <= legal_values:
+ raise argparse.ArgumentTypeError('Illegal {} values: {}, only [{}] are allowed'.format(
+ type_name,
+ ', '.join(atoms - legal_values),
+ ', '.join(legal_values)
+ ))
+ return set((type_name, allure.severity_level(atom)) for atom in atoms)
+ return set((type_name, atom) for atom in atoms)
+ return a_label_type
+
+ severities = [x.value for x in list(allure.severity_level)]
+ formatted_severities = ', '.join(severities)
+ parser.getgroup("general").addoption('--allure-severities',
+ action="store",
+ dest="allure_severities",
+ metavar="SEVERITIES_SET",
+ default={},
+ type=label_type(LabelType.SEVERITY, legal_values=set(severities)),
+ help=f"""Comma-separated list of severity names.
+ Tests only with these severities will be run.
+ Possible values are: {formatted_severities}.""")
+
+ parser.getgroup("general").addoption('--allure-epics',
+ action="store",
+ dest="allure_epics",
+ metavar="EPICS_SET",
+ default={},
+ type=label_type(LabelType.EPIC),
+ help="""Comma-separated list of epic names.
+ Run tests that have at least one of the specified feature labels.""")
+
+ parser.getgroup("general").addoption('--allure-features',
+ action="store",
+ dest="allure_features",
+ metavar="FEATURES_SET",
+ default={},
+ type=label_type(LabelType.FEATURE),
+ help="""Comma-separated list of feature names.
+ Run tests that have at least one of the specified feature labels.""")
+
+ parser.getgroup("general").addoption('--allure-stories',
+ action="store",
+ dest="allure_stories",
+ metavar="STORIES_SET",
+ default={},
+ type=label_type(LabelType.STORY),
+ help="""Comma-separated list of story names.
+ Run tests that have at least one of the specified story labels.""")
+
+ parser.getgroup("general").addoption('--allure-ids',
+ action="store",
+ dest="allure_ids",
+ metavar="IDS_SET",
+ default={},
+ type=label_type(LabelType.ID),
+ help="""Comma-separated list of IDs.
+ Run tests that have at least one of the specified id labels.""")
+
+ def cf_type(string):
+ type_name, values = string.split("=", 1)
+ atoms = set(values.split(","))
+ return [(type_name, atom) for atom in atoms]
+
+ parser.getgroup("general").addoption('--allure-label',
+ action="append",
+ dest="allure_labels",
+ metavar="LABELS_SET",
+ default=[],
+ type=cf_type,
+ help="""List of labels to run in format label_name=value1,value2.
+ "Run tests that have at least one of the specified labels.""")
+
+ def link_pattern(string):
+ pattern = string.split(':', 1)
+ if not pattern[0]:
+ raise argparse.ArgumentTypeError('Link type is mandatory.')
+
+ if len(pattern) != 2:
+ raise argparse.ArgumentTypeError('Link pattern is mandatory')
+ return pattern
+
+ parser.getgroup("general").addoption('--allure-link-pattern',
+ action="append",
+ dest="allure_link_pattern",
+ metavar="LINK_TYPE:LINK_PATTERN",
+ default=[],
+ type=link_pattern,
+ help="""Url pattern for link type. Allows short links in test,
+ like 'issue-1'. Text will be formatted to full url with python
+ str.format().""")
+
+
+def cleanup_factory(plugin):
+ def clean_up():
+ name = allure_commons.plugin_manager.get_name(plugin)
+ allure_commons.plugin_manager.unregister(name=name)
+ return clean_up
+
+
+def pytest_addhooks(pluginmanager):
+ # Need register title hooks before conftest init
+ title_helper = AllureTitleHelper()
+ allure_commons.plugin_manager.register(title_helper)
+
+
+def pytest_configure(config):
+ report_dir = config.option.allure_report_dir
+ clean = False if config.option.collectonly else config.option.clean_alluredir
+
+ test_helper = AllureTestHelper(config)
+ allure_commons.plugin_manager.register(test_helper)
+ config.add_cleanup(cleanup_factory(test_helper))
+
+ if report_dir:
+ report_dir = os.path.abspath(report_dir)
+ test_listener = AllureListener(config)
+ config.pluginmanager.register(test_listener, 'allure_listener')
+ allure_commons.plugin_manager.register(test_listener)
+ config.add_cleanup(cleanup_factory(test_listener))
+
+ file_logger = AllureFileLogger(report_dir, clean)
+ allure_commons.plugin_manager.register(file_logger)
+ config.add_cleanup(cleanup_factory(file_logger))
+
+ config.addinivalue_line("markers", f"{ALLURE_LABEL_MARK}: allure label marker")
+ config.addinivalue_line("markers", f"{ALLURE_LINK_MARK}: allure link marker")
+ config.addinivalue_line("markers", f"{ALLURE_DESCRIPTION_MARK}: allure description")
+ config.addinivalue_line("markers", f"{ALLURE_DESCRIPTION_HTML_MARK}: allure description html")
+
+
+def select_by_labels(items, config):
+ arg_labels = set().union(
+ config.option.allure_epics,
+ config.option.allure_features,
+ config.option.allure_stories,
+ config.option.allure_ids,
+ config.option.allure_severities,
+ *config.option.allure_labels
+ )
+ if arg_labels:
+ selected, deselected = [], []
+ for item in items:
+ test_labels = set(allure_labels(item))
+ test_severity = allure_label(item, LabelType.SEVERITY)
+ if not test_severity:
+ test_labels.add((LabelType.SEVERITY, Severity.NORMAL))
+ if arg_labels & test_labels:
+ selected.append(item)
+ else:
+ deselected.append(item)
+ return selected, deselected
+ else:
+ return items, []
+
+
+def select_by_testcase(items, config):
+ planned_tests = get_testplan()
+ is_inversion = config.option.inversion
+
+ if planned_tests:
+
+ def is_planed(item):
+ allure_ids = allure_label(item, LabelType.ID)
+ allure_string_ids = list(map(str, allure_ids))
+ for planed_item in planned_tests:
+ planed_item_string_id = str(planed_item.get("id"))
+ planed_item_selector = planed_item.get("selector")
+ if (
+ planed_item_string_id in allure_string_ids
+ or planed_item_selector == allure_full_name(item)
+ ):
+ return True if not is_inversion else False
+ return False if not is_inversion else True
+
+ selected, deselected = [], []
+ for item in items:
+ selected.append(item) if is_planed(item) else deselected.append(item)
+ return selected, deselected
+ else:
+ return items, []
+
+
+def pytest_collection_modifyitems(items, config):
+ selected, deselected_by_testcase = select_by_testcase(items, config)
+ selected, deselected_by_labels = select_by_labels(selected, config)
+
+ items[:] = selected
+
+ if deselected_by_testcase or deselected_by_labels:
+ config.hook.pytest_deselected(items=[*deselected_by_testcase, *deselected_by_labels])