Skip to content

Allure step in thread (fixes #563) #605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
*.egg-info

*/build
*/dist
*/dist
.eggs
16 changes: 8 additions & 8 deletions allure-pytest/src/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -61,15 +61,15 @@ 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

@pytest.hookimpl(hookwrapper=True)
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
Expand All @@ -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 {}
Expand Down Expand Up @@ -130,13 +130,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
Expand Down
32 changes: 32 additions & 0 deletions allure-pytest/test/acceptance/attachment/attachment_step_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")),
)
)
)
Original file line number Diff line number Diff line change
@@ -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


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")
)
)
)
4 changes: 2 additions & 2 deletions allure-python-commons/src/_core.py
Original file line number Diff line number Diff line change
@@ -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():
Expand Down
3 changes: 2 additions & 1 deletion allure-python-commons/src/lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions allure-python-commons/src/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@
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):
Expand All @@ -32,6 +45,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(
Expand Down
2 changes: 2 additions & 0 deletions allure-python-commons/src/model2.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class TestResultContainer(object):
links = attrib(default=Factory(list))
start = attrib(default=None)
stop = attrib(default=None)
thrd = attrib(default=None)


@attrs
Expand All @@ -37,6 +38,7 @@ class ExecutableItem(object):
parameters = attrib(default=Factory(list))
start = attrib(default=None)
stop = attrib(default=None)
thrd = attrib(default=None)


@attrs
Expand Down
26 changes: 20 additions & 6 deletions allure-python-commons/src/reporter.py
Original file line number Diff line number Diff line change
@@ -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))]
Expand All @@ -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

Expand Down
4 changes: 4 additions & 0 deletions allure-python-commons/src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ def thread_tag():
return '{0}-{1}'.format(os.getpid(), threading.current_thread().name)


def thread_tag_detail():
return '{0}-{1}'.format(threading.get_ident(), threading.current_thread().name)


def host_tag():
return socket.gethostname()

Expand Down