From 138ff49f73b7afba62bf65717a0310ddb1cb16b8 Mon Sep 17 00:00:00 2001 From: Nikolay Glazov Date: Thu, 18 Feb 2021 15:39:18 +0500 Subject: [PATCH 1/5] Change in core Thread to Process Now step correct showing in Thread --- .gitignore | 3 ++- allure-pytest/src/listener.py | 16 ++++++++-------- allure-python-commons/src/_allure.py | 1 + allure-python-commons/src/_core.py | 4 ++-- allure-python-commons/src/lifecycle.py | 3 ++- allure-python-commons/src/logger.py | 14 ++++++++++++++ allure-python-commons/src/model2.py | 3 +++ allure-python-commons/src/reporter.py | 26 ++++++++++++++++++++------ allure-python-commons/src/utils.py | 3 +++ 9 files changed, 55 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index af0bb13b..652e4153 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ *.egg-info */build -*/dist \ No newline at end of file +*/dist +.eggs diff --git a/allure-pytest/src/listener.py b/allure-pytest/src/listener.py index b7da35c8..014d7bef 100644 --- a/allure-pytest/src/listener.py +++ b/allure-pytest/src/listener.py @@ -5,7 +5,7 @@ from allure_commons.utils import uuid4 from allure_commons.utils import represent from allure_commons.utils import platform_label -from allure_commons.utils import host_tag, thread_tag +from allure_commons.utils import host_tag, thread_tag, thread_tag_detail from allure_commons.reporter import AllureReporter from allure_commons.model2 import TestStepResult, TestResult, TestBeforeResult, TestAfterResult from allure_commons.model2 import TestResultContainer @@ -36,7 +36,7 @@ def __init__(self, config): @allure_commons.hookimpl def start_step(self, uuid, title, params): parameters = [Parameter(name=name, value=value) for name, value in params.items()] - step = TestStepResult(name=title, start=now(), parameters=parameters) + step = TestStepResult(name=title, start=now(), parameters=parameters, thrd=thread_tag_detail()) self.allure_logger.start_step(None, uuid, step) @allure_commons.hookimpl @@ -48,7 +48,7 @@ def stop_step(self, uuid, exc_type, exc_val, exc_tb): @allure_commons.hookimpl def start_fixture(self, parent_uuid, uuid, name): - after_fixture = TestAfterResult(name=name, start=now()) + after_fixture = TestAfterResult(name=name, start=now(), thrd=thread_tag_detail()) self.allure_logger.start_after_fixture(parent_uuid, uuid, after_fixture) @allure_commons.hookimpl @@ -61,7 +61,7 @@ def stop_fixture(self, parent_uuid, uuid, name, exc_type, exc_val, exc_tb): @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_protocol(self, item, nextitem): uuid = self._cache.push(item.nodeid) - test_result = TestResult(name=item.name, uuid=uuid, start=now(), stop=now()) + test_result = TestResult(name=item.name, uuid=uuid, start=now(), stop=now(), thrd=thread_tag_detail()) self.allure_logger.schedule_test(uuid, test_result) yield @@ -69,7 +69,7 @@ def pytest_runtest_protocol(self, item, nextitem): def pytest_runtest_setup(self, item): if not self._cache.get(item.nodeid): uuid = self._cache.push(item.nodeid) - test_result = TestResult(name=item.name, uuid=uuid, start=now(), stop=now()) + test_result = TestResult(name=item.name, uuid=uuid, start=now(), stop=now(), thrd=thread_tag_detail()) self.allure_logger.schedule_test(uuid, test_result) yield @@ -80,7 +80,7 @@ def pytest_runtest_setup(self, item): group_uuid = self._cache.get(fixturedef) if not group_uuid: group_uuid = self._cache.push(fixturedef) - group = TestResultContainer(uuid=group_uuid) + group = TestResultContainer(uuid=group_uuid, thrd=thread_tag_detail()) self.allure_logger.start_group(group_uuid, group) self.allure_logger.update_group(group_uuid, children=uuid) params = item.callspec.params if hasattr(item, 'callspec') else {} @@ -128,13 +128,13 @@ def pytest_fixture_setup(self, fixturedef, request): if not container_uuid: container_uuid = self._cache.push(fixturedef) - container = TestResultContainer(uuid=container_uuid) + container = TestResultContainer(uuid=container_uuid, thrd=thread_tag_detail()) self.allure_logger.start_group(container_uuid, container) self.allure_logger.update_group(container_uuid, start=now()) before_fixture_uuid = uuid4() - before_fixture = TestBeforeResult(name=fixture_name, start=now()) + before_fixture = TestBeforeResult(name=fixture_name, start=now(), thrd=thread_tag_detail()) self.allure_logger.start_before_fixture(container_uuid, before_fixture_uuid, before_fixture) outcome = yield diff --git a/allure-python-commons/src/_allure.py b/allure-python-commons/src/_allure.py index 565ac955..2597a9e9 100644 --- a/allure-python-commons/src/_allure.py +++ b/allure-python-commons/src/_allure.py @@ -1,5 +1,6 @@ from functools import wraps from typing import Any, Callable, TypeVar +import threading from allure_commons._core import plugin_manager from allure_commons.types import LabelType, LinkType diff --git a/allure-python-commons/src/_core.py b/allure-python-commons/src/_core.py index 22219905..43f20a6b 100644 --- a/allure-python-commons/src/_core.py +++ b/allure-python-commons/src/_core.py @@ -1,11 +1,11 @@ -import threading +import multiprocessing from six import with_metaclass from pluggy import PluginManager from allure_commons import _hooks class MetaPluginManager(type): - _storage = threading.local() + _storage = multiprocessing.Process @staticmethod def get_plugin_manager(): diff --git a/allure-python-commons/src/lifecycle.py b/allure-python-commons/src/lifecycle.py index e2b7413a..9fef8ae7 100644 --- a/allure-python-commons/src/lifecycle.py +++ b/allure-python-commons/src/lifecycle.py @@ -11,6 +11,7 @@ from allure_commons.utils import uuid4 from allure_commons.utils import now from allure_commons.types import AttachmentType +import threading class AllureLifecycle(object): @@ -52,7 +53,7 @@ def write_test_case(self, uuid=None): @contextmanager def start_step(self, parent_uuid=None, uuid=None): parent = self._get_item(uuid=parent_uuid, item_type=ExecutableItem) - step = TestStepResult() + step = TestStepResult(thrd=threading.current_thread().name) step.start = now() parent.steps.append(step) self._items[uuid or uuid4()] = step diff --git a/allure-python-commons/src/logger.py b/allure-python-commons/src/logger.py index ab326b06..145e8c4a 100644 --- a/allure-python-commons/src/logger.py +++ b/allure-python-commons/src/logger.py @@ -12,6 +12,18 @@ INDENT = 4 +def delete_step_thrd(data): + if isinstance(data, list): + for field in data: + field = delete_step_thrd(field) + elif isinstance(data, dict): + for field in data.keys(): + if field == 'thrd': + data.pop('thrd') + break + field = delete_step_thrd(data[field]) + return data + class AllureFileLogger(object): def __init__(self, report_dir, clean=False): @@ -32,6 +44,8 @@ def _report_item(self, item): indent = INDENT if os.environ.get("ALLURE_INDENT_OUTPUT") else None filename = item.file_pattern.format(prefix=uuid.uuid4()) data = asdict(item, filter=lambda attr, value: not (type(value) != bool and not bool(value))) + data = delete_step_thrd(data) + with io.open(os.path.join(self._report_dir, filename), 'w', encoding='utf8') as json_file: if sys.version_info.major < 3: json_file.write( diff --git a/allure-python-commons/src/model2.py b/allure-python-commons/src/model2.py index 074bff94..5e70c9a3 100644 --- a/allure-python-commons/src/model2.py +++ b/allure-python-commons/src/model2.py @@ -1,5 +1,6 @@ from attr import attrs, attrib from attr import Factory +import threading TEST_GROUP_PATTERN = "{prefix}-container.json" @@ -22,6 +23,7 @@ class TestResultContainer(object): links = attrib(default=Factory(list)) start = attrib(default=None) stop = attrib(default=None) + thrd = attrib(default=None) @attrs @@ -37,6 +39,7 @@ class ExecutableItem(object): parameters = attrib(default=Factory(list)) start = attrib(default=None) stop = attrib(default=None) + thrd = attrib(default=None) @attrs diff --git a/allure-python-commons/src/reporter.py b/allure-python-commons/src/reporter.py index 1932f2e3..660db8a5 100644 --- a/allure-python-commons/src/reporter.py +++ b/allure-python-commons/src/reporter.py @@ -1,17 +1,17 @@ from collections import OrderedDict -from allure_commons.types import AttachmentType -from allure_commons.model2 import ExecutableItem -from allure_commons.model2 import TestResult -from allure_commons.model2 import Attachment, ATTACHMENT_PATTERN -from allure_commons.utils import now from allure_commons._core import plugin_manager +from allure_commons.model2 import (ATTACHMENT_PATTERN, Attachment, + ExecutableItem, TestResult) +from allure_commons.types import AttachmentType +from allure_commons.utils import now, thread_tag_detail class AllureReporter(object): def __init__(self): self._items = OrderedDict() self._orphan_items = [] + self._thread = thread_tag_detail() def _update_item(self, uuid, **kwargs): item = self._items[uuid] if uuid else self._items[next(reversed(self._items))] @@ -23,7 +23,21 @@ def _update_item(self, uuid, **kwargs): setattr(item, name, value) def _last_executable(self): - for _uuid in reversed(self._items): + copy_items = self._items.copy() + for _uuid in reversed(copy_items): + if ( + hasattr(self._items[_uuid], "thrd") + and self._items[_uuid].thrd != thread_tag_detail() + ): + continue + if isinstance(self._items[_uuid], ExecutableItem): + return _uuid + for _uuid in reversed(copy_items): + if hasattr(self._items[_uuid], "thrd") and self._items[_uuid].thrd != self._thread: + continue + if isinstance(self._items[_uuid], ExecutableItem): + return _uuid + for _uuid in reversed(copy_items): if isinstance(self._items[_uuid], ExecutableItem): return _uuid diff --git a/allure-python-commons/src/utils.py b/allure-python-commons/src/utils.py index 6742d265..f1005df1 100644 --- a/allure-python-commons/src/utils.py +++ b/allure-python-commons/src/utils.py @@ -96,6 +96,9 @@ def thread_tag(): return '{0}-{1}'.format(os.getpid(), threading.current_thread().name) +def thread_tag_detail(): + return '{0}-{1}'.format(threading.get_native_id(), threading.current_thread().name) + def host_tag(): return socket.gethostname() From 59b50dd71f20d11d7ba0c73c0cfc76fe713d0585 Mon Sep 17 00:00:00 2001 From: nigredon1991 Date: Sat, 3 Jul 2021 15:04:21 +0500 Subject: [PATCH 2/5] Fix tox static check --- allure-python-commons/src/_allure.py | 1 - allure-python-commons/src/logger.py | 1 + allure-python-commons/src/model2.py | 1 - allure-python-commons/src/utils.py | 1 + 4 files changed, 2 insertions(+), 2 deletions(-) diff --git a/allure-python-commons/src/_allure.py b/allure-python-commons/src/_allure.py index 2597a9e9..565ac955 100644 --- a/allure-python-commons/src/_allure.py +++ b/allure-python-commons/src/_allure.py @@ -1,6 +1,5 @@ from functools import wraps from typing import Any, Callable, TypeVar -import threading from allure_commons._core import plugin_manager from allure_commons.types import LabelType, LinkType diff --git a/allure-python-commons/src/logger.py b/allure-python-commons/src/logger.py index 145e8c4a..8a004052 100644 --- a/allure-python-commons/src/logger.py +++ b/allure-python-commons/src/logger.py @@ -24,6 +24,7 @@ def delete_step_thrd(data): field = delete_step_thrd(data[field]) return data + class AllureFileLogger(object): def __init__(self, report_dir, clean=False): diff --git a/allure-python-commons/src/model2.py b/allure-python-commons/src/model2.py index 5e70c9a3..6a17389f 100644 --- a/allure-python-commons/src/model2.py +++ b/allure-python-commons/src/model2.py @@ -1,6 +1,5 @@ from attr import attrs, attrib from attr import Factory -import threading TEST_GROUP_PATTERN = "{prefix}-container.json" diff --git a/allure-python-commons/src/utils.py b/allure-python-commons/src/utils.py index 6c78556d..70531046 100644 --- a/allure-python-commons/src/utils.py +++ b/allure-python-commons/src/utils.py @@ -99,6 +99,7 @@ def thread_tag(): def thread_tag_detail(): return '{0}-{1}'.format(threading.get_native_id(), threading.current_thread().name) + def host_tag(): return socket.gethostname() From d3313fdc8f7c6cb30c12a4ec9bb0e9e72fa93ab6 Mon Sep 17 00:00:00 2001 From: nigredon1991 Date: Sun, 4 Jul 2021 07:31:15 +0500 Subject: [PATCH 3/5] Compatibility with python3.6 threading.get_native_id() -> threading.get_ident() --- allure-python-commons/src/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/allure-python-commons/src/utils.py b/allure-python-commons/src/utils.py index 70531046..14f00e91 100644 --- a/allure-python-commons/src/utils.py +++ b/allure-python-commons/src/utils.py @@ -97,7 +97,7 @@ def thread_tag(): def thread_tag_detail(): - return '{0}-{1}'.format(threading.get_native_id(), threading.current_thread().name) + return '{0}-{1}'.format(threading.get_ident(), threading.current_thread().name) def host_tag(): From 78c8551638ccfdaf6b1ea1792ff5b94e8530b365 Mon Sep 17 00:00:00 2001 From: nigredon1991 Date: Thu, 8 Jul 2021 15:21:06 +0500 Subject: [PATCH 4/5] Add tests for thread --- .../attachment/attachment_step_test.py | 32 +++++++++++++++++ ...st_step_with_several_step_inside_thread.py | 34 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 allure-pytest/test/acceptance/step/test_step_with_several_step_inside_thread.py diff --git a/allure-pytest/test/acceptance/attachment/attachment_step_test.py b/allure-pytest/test/acceptance/attachment/attachment_step_test.py index 41a85173..bdb14d44 100644 --- a/allure-pytest/test/acceptance/attachment/attachment_step_test.py +++ b/allure-pytest/test/acceptance/attachment/attachment_step_test.py @@ -14,3 +14,35 @@ def test_step_with_attachment(executed_docstring_path): ), ) ) + + +def test_step_with_thread_and_attachment(allured_testdir): + allured_testdir.testdir.makepyfile( + """ + from concurrent.futures import ThreadPoolExecutor + + import allure + import pytest + + @allure.step("thread {x}") + def parallel_step(x=1): + allure.attach("text", str(x), allure.attachment_type.TEXT) + + + def test_thread(): + with allure.step("Start in thread"): + with ThreadPoolExecutor(max_workers=2) as executor: + f_result = executor.map(parallel_step, [1, 2]) + """ + ) + + allured_testdir.run_with_allure() + + assert_that(allured_testdir.allure_report, + has_test_case("test_thread", + has_step("Start in thread", + has_step("thread 1", has_attachment(name="1")), + has_step("thread 2", has_attachment(name="2")), + ) + ) + ) diff --git a/allure-pytest/test/acceptance/step/test_step_with_several_step_inside_thread.py b/allure-pytest/test/acceptance/step/test_step_with_several_step_inside_thread.py new file mode 100644 index 00000000..19277d94 --- /dev/null +++ b/allure-pytest/test/acceptance/step/test_step_with_several_step_inside_thread.py @@ -0,0 +1,34 @@ +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_step +from hamcrest import assert_that, not_ + + +def test_step_with_thread(allured_testdir): + allured_testdir.testdir.makepyfile( + """ + from concurrent.futures import ThreadPoolExecutor + + import allure + import pytest + + @allure.step("thread {x}") + def parallel_step(x=1): + pass + + + def test_thread(): + with allure.step("Start in thread"): + with ThreadPoolExecutor(max_workers=2) as executor: + f_result = executor.map(parallel_step, [1, 2]) + """ + ) + + allured_testdir.run_with_allure() + + assert_that(allured_testdir.allure_report, + has_test_case("test_thread", + has_step("Start in thread", + has_step("thread 1"), has_step("thread 2") + ) + ) + ) From 205d8ddf2b00c0101fb6fe307abeafd92e372839 Mon Sep 17 00:00:00 2001 From: nigredon1991 Date: Thu, 8 Jul 2021 15:24:33 +0500 Subject: [PATCH 5/5] Fix static-check --- .../step/test_step_with_several_step_inside_thread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/allure-pytest/test/acceptance/step/test_step_with_several_step_inside_thread.py b/allure-pytest/test/acceptance/step/test_step_with_several_step_inside_thread.py index 19277d94..8c6302ed 100644 --- a/allure-pytest/test/acceptance/step/test_step_with_several_step_inside_thread.py +++ b/allure-pytest/test/acceptance/step/test_step_with_several_step_inside_thread.py @@ -1,6 +1,6 @@ from allure_commons_test.report import has_test_case from allure_commons_test.result import has_step -from hamcrest import assert_that, not_ +from hamcrest import assert_that def test_step_with_thread(allured_testdir):