diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 02ef0d9f..982f89e1 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -10,6 +10,7 @@ jobs: package: [ allure-python-commons-test, allure-python-commons, + allure-nose2, allure-behave, allure-pytest, allure-pytest-bdd, diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e4916a0a..725525dd 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -40,6 +40,11 @@ jobs: twine upload dist/* popd + pushd allure-nose2 + python setup.py sdist bdist_wheel + twine upload dist/* + popd + pushd allure-pytest python setup.py sdist bdist_wheel twine upload dist/* diff --git a/allure-nose2/README.rst b/allure-nose2/README.rst new file mode 100644 index 00000000..e69de29b diff --git a/allure-nose2/setup.py b/allure-nose2/setup.py new file mode 100644 index 00000000..da1533df --- /dev/null +++ b/allure-nose2/setup.py @@ -0,0 +1,58 @@ +import os +from setuptools import setup + +PACKAGE = "allure-nose2" + +classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Topic :: Software Development :: Quality Assurance', + 'Topic :: Software Development :: Testing', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', +] + +setup_requires = [ + "setuptools_scm" +] + +install_requires = [ + "nose2" +] + + +def prepare_version(): + from setuptools_scm import get_version + configuration = {"root": "..", "relative_to": __file__} + version = get_version(**configuration) + install_requires.append("allure-python-commons=={version}".format(version=version)) + return configuration + + +def get_readme(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() + + +def main(): + setup( + name=PACKAGE, + use_scm_version=prepare_version, + description="Allure nose2 integration", + url="https://github.com/allure-framework/allure-python", + author="QAMetaSoftware, Stanislav Seliverstov", + author_email="sseliverstov@qameta.io", + license="Apache-2.0", + classifiers=classifiers, + keywords="allure reporting nose2", + long_description=get_readme('README.rst'), + packages=["allure_nose2"], + package_dir={"allure_nose2": "src"}, + setup_requires=setup_requires, + install_requires=install_requires + ) + +if __name__ == '__main__': + main() + diff --git a/allure-nose2/src/__init__.py b/allure-nose2/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/allure-nose2/src/listener.py b/allure-nose2/src/listener.py new file mode 100644 index 00000000..600605d2 --- /dev/null +++ b/allure-nose2/src/listener.py @@ -0,0 +1,5 @@ + +# ToDo attaches +class AllureListener(object): + def __init__(self, lifecycle): + self.lifecycle = lifecycle diff --git a/allure-nose2/src/plugin.py b/allure-nose2/src/plugin.py new file mode 100644 index 00000000..235d0471 --- /dev/null +++ b/allure-nose2/src/plugin.py @@ -0,0 +1,129 @@ +from nose2.events import Plugin +from allure_commons import plugin_manager +from allure_commons.logger import AllureFileLogger +from allure_nose2.listener import AllureListener +from allure_commons.lifecycle import AllureLifecycle +from nose2 import result +from allure_commons.model2 import Status +from allure_commons.model2 import StatusDetails +from allure_commons.model2 import Label +from allure_commons.types import LabelType +from allure_commons.utils import host_tag, thread_tag + +from allure_commons.utils import platform_label, md5 + + +from .utils import timestamp_millis, status_details, update_attrs, labels, name, fullname, params +import allure_commons + + +class DecoratorsHelper(object): + @classmethod + @allure_commons.hookimpl + def decorate_as_label(cls, label_type, labels): + # ToDo functools.update_wrapper + def wrapper(test): + update_attrs(test, label_type, labels) + return test + + return wrapper + + @classmethod + def register(cls): + if cls not in plugin_manager.get_plugins(): + plugin_manager.register(cls) + + @classmethod + def unregister(cls): + if cls in plugin_manager.get_plugins(): + plugin_manager.unregister(plugin=cls) + + +DecoratorsHelper.register() + + +class Allure(Plugin): + configSection = 'allure' + commandLineSwitch = (None, "allure", "Generate an Allure report") + + def __init__(self, *args, **kwargs): + super(Allure, self).__init__(*args, **kwargs) + self._host = host_tag() + self._thread = thread_tag() + self.lifecycle = AllureLifecycle() + self.logger = AllureFileLogger("allure-result") + self.listener = AllureListener(self.lifecycle) + + def registerInSubprocess(self, event): + self.unregister_allure_plugins() + event.pluginClasses.append(self.__class__) + + def startSubprocess(self, event): + self.register_allure_plugins() + + def stopSubprocess(self, event): + self.unregister_allure_plugins() + + def register_allure_plugins(self): + plugin_manager.register(self.listener) + plugin_manager.register(self.logger) + + def unregister_allure_plugins(self): + plugin_manager.unregister(plugin=self.listener) + plugin_manager.unregister(plugin=self.logger) + + def is_registered(self): + return all([plugin_manager.is_registered(self.listener), + plugin_manager.is_registered(self.logger)]) + + def startTestRun(self, event): + self.register_allure_plugins() + + def afterTestRun(self, event): + self.unregister_allure_plugins() + + def startTest(self, event): + if self.is_registered(): + with self.lifecycle.schedule_test_case() as test_result: + test_result.name = name(event) + test_result.start = timestamp_millis(event.startTime) + test_result.fullName = fullname(event) + test_result.testCaseId = md5(test_result.fullName) + test_result.historyId = md5(event.test.id()) + test_result.labels.extend(labels(event.test)) + test_result.labels.append(Label(name=LabelType.HOST, value=self._host)) + test_result.labels.append(Label(name=LabelType.THREAD, value=self._thread)) + test_result.labels.append(Label(name=LabelType.FRAMEWORK, value='nose2')) + test_result.labels.append(Label(name=LabelType.LANGUAGE, value=platform_label())) + test_result.parameters = params(event) + + def stopTest(self, event): + if self.is_registered(): + with self.lifecycle.update_test_case() as test_result: + test_result.stop = timestamp_millis(event.stopTime) + self.lifecycle.write_test_case() + + def testOutcome(self, event): + if self.is_registered(): + with self.lifecycle.update_test_case() as test_result: + if event.outcome == result.PASS and event.expected: + test_result.status = Status.PASSED + elif event.outcome == result.PASS and not event.expected: + test_result.status = Status.PASSED + test_result.statusDetails = StatusDetails(message="test passes unexpectedly") + elif event.outcome == result.FAIL and not event.expected: + test_result.status = Status.FAILED + test_result.statusDetails = status_details(event) + elif event.outcome == result.ERROR: + test_result.status = Status.BROKEN + test_result.statusDetails = status_details(event) + elif event.outcome == result.SKIP: + test_result.status = Status.SKIPPED + test_result.statusDetails = status_details(event) + # Todo default status and other cases + # elif event.outcome == result.FAIL and event.expected: + # pass + # self.skipped += 1 + # skipped = ET.SubElement(testcase, 'skipped') + # skipped.set('message', 'expected test failure') + # skipped.text = msg diff --git a/allure-nose2/src/utils.py b/allure-nose2/src/utils.py new file mode 100644 index 00000000..5d2192a7 --- /dev/null +++ b/allure-nose2/src/utils.py @@ -0,0 +1,98 @@ +from traceback import format_exception_only +from allure_commons.model2 import StatusDetails, Label +from allure_commons.model2 import Parameter +from allure_commons.utils import represent +from nose2 import util +import inspect + +# ToDo move to commons +ALLURE_LABELS = [ + 'epic', + 'feature', + 'story', +] + + +def timestamp_millis(timestamp): + return int(timestamp * 1000) + + +def status_details(event): + message, trace = None, None + if event.exc_info: + exc_type, value, _ = event.exc_info + message = '\n'.join(format_exception_only(exc_type, value)) if exc_type or value else None + trace = ''.join(util.exc_info_to_string(event.exc_info, event.test)) + elif event.reason: + message = event.reason + + if message or trace: + return StatusDetails(message=message, trace=trace) + + +def update_attrs(test, name, values): + if type(values) in (list, tuple, str) and name.isidentifier(): + attrib = getattr(test, name, values) + if attrib and attrib != values: + attrib = sum( + [tuple(i) if type(i) in (tuple, list) else (i,) for i in (attrib, values)], + () + ) + setattr(test, name, attrib) + + +def labels(test): + + def _get_attrs(obj, keys): + pairs = set() + for key in keys: + values = getattr(obj, key, ()) + for value in (values,) if type(values) == str else values: + pairs.add((key, value)) + return pairs + + keys = ALLURE_LABELS + pairs = _get_attrs(test, keys) + + if hasattr(test, "_testFunc"): + pairs.update(_get_attrs(test._testFunc, keys)) + elif hasattr(test, "_testMethodName"): + test_method = getattr(test, test._testMethodName) + pairs.update(_get_attrs(test_method, keys)) + return [Label(name=name, value=value) for name, value in pairs] + + +def name(event): + full_name = fullname(event) + return full_name.split(".")[-1] + + +def fullname(event): + if hasattr(event.test, "_testFunc"): + test_module = event.test._testFunc.__module__ + test_name = event.test._testFunc.__name__ + return "{module}.{name}".format(module=test_module, name=test_name) + test_id = event.test.id() + return test_id.split(":")[0] + + +def params(event): + def _params(names, values): + return [Parameter(name=name, value=represent(value)) for name, value in zip(names, values)] + + test_id = event.test.id() + + if len(test_id.split("\n")) > 1: + if hasattr(event.test, "_testFunc"): + wrapper_arg_spec = inspect.getfullargspec(event.test._testFunc) + arg_set, obj = wrapper_arg_spec.defaults + test_arg_spec = inspect.getfullargspec(obj) + args = test_arg_spec.args + return _params(args, arg_set) + elif hasattr(event.test, "_testMethodName"): + method = getattr(event.test, event.test._testMethodName) + wrapper_arg_spec = inspect.getfullargspec(method) + obj, arg_set = wrapper_arg_spec.defaults + test_arg_spec = inspect.getfullargspec(obj) + args = test_arg_spec.args + return _params(args[1:], arg_set) diff --git a/allure-nose2/test/__init__.py b/allure-nose2/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/allure-nose2/test/example_loader.py b/allure-nose2/test/example_loader.py new file mode 100644 index 00000000..e4e3f167 --- /dev/null +++ b/allure-nose2/test/example_loader.py @@ -0,0 +1,29 @@ +import sys +import types +from importlib import util +from doctest import script_from_examples +from nose2 import events + + +class CurrentExample(events.Plugin): + commandLineSwitch = (None, "current-example", "Method docstring to module") + + def __init__(self, *args, **kwargs): + super(CurrentExample, self).__init__(*args, **kwargs) + self._current_docstring = "" + + def startTest(self, event): + if hasattr(event.test, "_testFunc"): + self._current_docstring = event.test._testFunc.__doc__ + else: + self._current_docstring = event.test._testMethodDoc + + def get_example_module(self): + module = types.ModuleType("stub") + if self._current_docstring: + code = script_from_examples(self._current_docstring) + spec = util.spec_from_loader("example_module", origin="example_module", loader=None) + module = util.module_from_spec(spec) + exec(code, module.__dict__) + sys.modules['example_module'] = module + return module diff --git a/allure-nose2/test/example_runner.py b/allure-nose2/test/example_runner.py new file mode 100644 index 00000000..3b666aae --- /dev/null +++ b/allure-nose2/test/example_runner.py @@ -0,0 +1,63 @@ +import io +import sys +from contextlib import ContextDecorator, redirect_stdout, redirect_stderr +from nose2 import main +from allure_commons import plugin_manager +from allure_commons.logger import AllureMemoryLogger +from allure_commons.logger import AllureFileLogger +from .example_loader import CurrentExample + +_Allure = sys.modules['allure_nose2.plugin'].Allure + + +class TestAllure(_Allure): + commandLineSwitch = (None, "test-allure", "Generate an Allure report") + + def register_allure_plugins(self): + self.fileLoger = AllureFileLogger("examples") + self.logger = AllureMemoryLogger() + plugin_manager.register(self.fileLoger) + plugin_manager.register(self.listener) + plugin_manager.register(self.logger) + + def unregister_allure_plugins(self): + plugin_manager.unregister(plugin=self.fileLoger) + plugin_manager.unregister(plugin=self.listener) + plugin_manager.unregister(plugin=self.logger) + + +def get_plugin(instance, plugin): + return next(iter([p for p in instance.getCurrentSession().plugins if isinstance(p, plugin)])) + + +class test_context(ContextDecorator): + def __enter__(self): + get_plugin(main, _Allure).unregister_allure_plugins() + + def __exit__(self, exc_type, exc_val, exc_tb): + get_plugin(main, _Allure).register_allure_plugins() + + +@test_context() +def run_docstring_example(**kwargs): + kwargs['exit'] = False + # kwargs['plugins'] = ["test.common", "nose2.plugins.mp"] + # kwargs['argv'] = ('nose2', '--test-allure', '--processes=2') + + kwargs['plugins'] = ["test.example_runner"] + kwargs['argv'] = ('nose2', '--test-allure') + + kwargs['module'] = get_plugin(main, CurrentExample).get_example_module() + + test_nose2 = type("TestNose2", (main,), {}) + stdout = io.StringIO() + stderr = io.StringIO() + + with redirect_stderr(stderr): + with redirect_stdout(stdout): + test_nose2_instance = test_nose2(**kwargs) + + #test_nose2_instance = test_nose2(**kwargs) + + return get_plugin(test_nose2_instance, _Allure).logger + diff --git a/allure-nose2/test/labels/__init__.py b/allure-nose2/test/labels/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/allure-nose2/test/labels/test_bdd_labels.py b/allure-nose2/test/labels/test_bdd_labels.py new file mode 100644 index 00000000..6277a893 --- /dev/null +++ b/allure-nose2/test/labels/test_bdd_labels.py @@ -0,0 +1,85 @@ +import unittest +from hamcrest import assert_that +from allure_commons_test.report import has_test_case +from allure_commons_test.label import has_epic +from allure_commons_test.label import has_feature +from test.example_runner import run_docstring_example + + +class TestBDDLabel(unittest.TestCase): + def test_method_label(self): + """ + >>> import unittest + >>> import allure + + >>> class TestBDDLabelExample(unittest.TestCase): + ... @allure.epic("Label", "Bdd") + ... @allure.feature("Method label") + ... def test_method_label_example(self): + ... pass + """ + allure_report = run_docstring_example() + assert_that(allure_report, + has_test_case("test_method_label_example", + has_epic("Label"), + has_epic("Bdd"), + has_feature("Method label") + ) + ) + + def test_class_label(self): + """ + >>> import unittest + >>> import allure + + >>> @allure.epic("Label", "Bdd") + ... class TestBDDLabelExample(unittest.TestCase): + ... def test_class_label_example(self): + ... pass + """ + allure_report = run_docstring_example() + assert_that(allure_report, + has_test_case("test_class_label_example", + has_epic("Label"), + has_epic("Bdd"), + ) + ) + + def test_class_method_label(self): + """ + >>> import unittest + >>> import allure + + >>> @allure.epic("Label", "Bdd") + ... class TestBDDLabelExample(unittest.TestCase): + ... @allure.feature("Method label") + ... def test_class_and_method_label_example(self): + ... pass + """ + allure_report = run_docstring_example() + assert_that(allure_report, + has_test_case("test_class_and_method_label_example", + has_epic("Label"), + has_epic("Bdd"), + has_feature("Method label") + ) + ) + + +def test_func_label(): + """ + >>> import allure + + >>> @allure.epic("Label", "Bdd") + ... @allure.feature("Function label") + ... def test_func_label_example(): + ... pass + """ + allure_report = run_docstring_example() + assert_that(allure_report, + has_test_case("test_func_label_example", + has_epic("Label"), + has_epic("Bdd"), + has_feature("Function label") + ) + ) diff --git a/allure-nose2/test/parametrized/__init__.py b/allure-nose2/test/parametrized/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/allure-nose2/test/parametrized/test_parametrized.py b/allure-nose2/test/parametrized/test_parametrized.py new file mode 100644 index 00000000..6def5d7d --- /dev/null +++ b/allure-nose2/test/parametrized/test_parametrized.py @@ -0,0 +1,59 @@ +from nose2.tools import params +import unittest +from test.example_runner import run_docstring_example +from hamcrest import assert_that +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_parameter +from allure_commons.utils import represent + + +@params( + (("alpha", "hello"), ("betta", 42)), + (("alpha", "world"), ("betta", 777)) +) +def test_parametrized_func(first, second): + """ + >>> from nose2.tools import params + + >>> @params(("hello", 42), ("world", 777)) + ... def test_parametrized_func_example(alpha, betta): + ... pass + """ + first_param_name, first_param_value = first + second_param_name, second_param_value = second + + allure_report = run_docstring_example() + assert_that(allure_report, + has_test_case("test_parametrized_func_example", + has_parameter(first_param_name, represent(first_param_value)), + has_parameter(second_param_name, represent(second_param_value)) + ) + ) + + +class TestParametrized(unittest.TestCase): + + @params( + (("bravo", {"hello": 4}), ("charlie", [4, 2])), + (("bravo", {"wold": 2}), ("charlie", [7, 7, 7])) + ) + def test_parametrized_method(self, first, second): + """ + >>> import unittest + >>> from nose2.tools import params + + >>> class TestParametrizedExample(unittest.TestCase): + ... @params(({"hello": 4}, [4, 2]), ({"wold": 2}, [7, 7, 7])) + ... def test_parametrized_method_example(self, bravo, charlie): + ... pass + """ + first_param_name, first_param_value = first + second_param_name, second_param_value = second + + allure_report = run_docstring_example() + assert_that(allure_report, + has_test_case("test_parametrized_method_example", + has_parameter(first_param_name, represent(first_param_value)), + has_parameter(second_param_name, represent(second_param_value)) + ) + ) \ No newline at end of file diff --git a/allure-nose2/test/result/__init__.py b/allure-nose2/test/result/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/allure-nose2/test/result/test_fullname.py b/allure-nose2/test/result/test_fullname.py new file mode 100644 index 00000000..6bd5e38d --- /dev/null +++ b/allure-nose2/test/result/test_fullname.py @@ -0,0 +1,39 @@ +import unittest +from hamcrest import assert_that +from test.example_runner import run_docstring_example +from hamcrest import has_entry, has_item, has_property + + +def test_func_fullname(): + """ + >>> def test_func_fullname_example(): + ... pass + """ + allure_report = run_docstring_example() + assert_that(allure_report, + has_property("test_cases", + has_item( + has_entry("fullName", "example_module.test_func_fullname_example") + ) + ) + ) + + +class TestFullname(unittest.TestCase): + def test_method_fullname(self): + """ + >>> import unittest + + >>> class TestFullnameExample(unittest.TestCase): + ... def test_method_fullname_example(self): + ... pass + """ + allure_report = run_docstring_example() + assert_that(allure_report, + has_property("test_cases", + has_item( + has_entry("fullName", + "example_module.TestFullnameExample.test_method_fullname_example") + ) + ) + ) \ No newline at end of file diff --git a/allure-nose2/test/result/test_status.py b/allure-nose2/test/result/test_status.py new file mode 100644 index 00000000..4e65d2b0 --- /dev/null +++ b/allure-nose2/test/result/test_status.py @@ -0,0 +1,71 @@ +import unittest +from hamcrest import assert_that +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.result import has_status_details +from allure_commons_test.result import with_message_contains +from test.example_runner import run_docstring_example + + +class TestStatus(unittest.TestCase): + def test_passed_status(self): + """ + >>> import unittest + + >>> class TestStatusExample(unittest.TestCase): + ... def test_passed_example(self): + ... assert True + """ + allure_report = run_docstring_example() + assert_that(allure_report, + has_test_case("test_passed_example", + with_status("passed")) + ) + + def test_failed_status(self): + """ + >>> import unittest + + >>> class TestStatusExample(unittest.TestCase): + ... def test_failed_example(self): + ... assert False, "my message" + """ + allure_report = run_docstring_example() + assert_that(allure_report, + has_test_case("test_failed_example", + with_status("failed"), + has_status_details(with_message_contains("my message")) + ) + ) + + def test_broken_status(self): + """ + >>> import unittest + + >>> class TestStatusExample(unittest.TestCase): + ... def test_broken_example(self): + ... raise Exception("my error") + """ + allure_report = run_docstring_example() + assert_that(allure_report, + has_test_case("test_broken_example", + with_status("broken"), + has_status_details(with_message_contains("my error")) + ) + ) + + def test_skipped_status(self): + """ + >>> import unittest + + >>> class TestStatusExample(unittest.TestCase): + ... def test_skipped_example(self): + ... self.skipTest('my skip reason') + """ + allure_report = run_docstring_example() + assert_that(allure_report, + has_test_case("test_skipped_example", + with_status("skipped"), + has_status_details(with_message_contains("my skip reason")) + ) + ) \ No newline at end of file diff --git a/allure-nose2/test/with_mp/__init__.py b/allure-nose2/test/with_mp/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/allure-nose2/test/with_mp/test_pm.py b/allure-nose2/test/with_mp/test_pm.py new file mode 100644 index 00000000..6d665d9a --- /dev/null +++ b/allure-nose2/test/with_mp/test_pm.py @@ -0,0 +1,13 @@ +# Todo test mp +from test.example_runner import run_docstring_example + +def test_func_fullname(): + """ + >>> def test_func_fullname_example1(): + ... pass + >>> def test_func_fullname_example2(): + ... pass + >>> def test_func_fullname_example3(): + ... pass + """ + allure_report = run_docstring_example() \ No newline at end of file diff --git a/allure-nose2/tox.ini b/allure-nose2/tox.ini new file mode 100644 index 00000000..0585078f --- /dev/null +++ b/allure-nose2/tox.ini @@ -0,0 +1,37 @@ +[tox] +envlist = + py{36,37} + static-check + +[testenv] +passenv = HOME + +setenv = + TEST_TMP={envtmpdir} + ALLURE_INDENT_OUTPUT=yep + +deps= + nose2 + {distshare}/allure-python-commons-test-?.*.zip + {distshare}/allure-python-commons-?.*.zip + + +whitelist_externals = + rm + +commands = + rm -rf {envtmpdir}/* + nose2 --plugin=test.example_loader --plugin=allure_nose2.plugin --allure --current-example -t . -s {posargs: ./test} +; nose2 --plugin=allure_nose2.plugin --plugin=nose2.plugins.mp --allure --processes=2 -t . -s {posargs: ./test} + +[testenv:static-check] +basepython = python +skip_install = True + +deps = flake8 + +commands = flake8 src/ + + +[flake8] +max-line-length = 120 diff --git a/allure-pytest/test/conftest.py b/allure-pytest/test/conftest.py index 37077521..ac2daaa5 100644 --- a/allure-pytest/test/conftest.py +++ b/allure-pytest/test/conftest.py @@ -1,37 +1,11 @@ import pytest import six -from attr import asdict -from allure_commons import hookimpl from allure_commons_test.report import AllureReport from doctest import script_from_examples import mock import allure_commons from contextlib import contextmanager - - -class AllureMemoryLogger(object): - def __init__(self): - self.test_cases = [] - self.test_containers = [] - self.attachments = {} - - @hookimpl - def report_result(self, result): - data = asdict(result, filter=lambda attr, value: not (type(value) != bool and not bool(value))) - self.test_cases.append(data) - - @hookimpl - def report_container(self, container): - data = asdict(container, filter=lambda attr, value: not (type(value) != bool and not bool(value))) - self.test_containers.append(data) - - @hookimpl - def report_attached_file(self, source, file_name): - pass - - @hookimpl - def report_attached_data(self, body, file_name): - self.attachments[file_name] = body +from allure_commons.logger import AllureMemoryLogger @contextmanager diff --git a/allure-python-commons/src/logger.py b/allure-python-commons/src/logger.py index 407406c5..ab326b06 100644 --- a/allure-python-commons/src/logger.py +++ b/allure-python-commons/src/logger.py @@ -60,3 +60,29 @@ def report_attached_data(self, body, file_name): attached_file.write(body.encode('utf-8')) else: attached_file.write(body) + + +class AllureMemoryLogger(object): + + def __init__(self): + self.test_cases = [] + self.test_containers = [] + self.attachments = {} + + @hookimpl + def report_result(self, result): + data = asdict(result, filter=lambda attr, value: not (type(value) != bool and not bool(value))) + self.test_cases.append(data) + + @hookimpl + def report_container(self, container): + data = asdict(container, filter=lambda attr, value: not (type(value) != bool and not bool(value))) + self.test_containers.append(data) + + @hookimpl + def report_attached_file(self, source, file_name): + pass + + @hookimpl + def report_attached_data(self, body, file_name): + self.attachments[file_name] = body