diff --git a/pytest_nunit/attrs2xml.py b/pytest_nunit/attrs2xml.py index 9271e81..45cca85 100644 --- a/pytest_nunit/attrs2xml.py +++ b/pytest_nunit/attrs2xml.py @@ -6,6 +6,8 @@ class CdataComment(ET.Element): def __init__(self, text): super(CdataComment, self).__init__("CDATA!") + if type(text) is not str: + text = str(text) self.text = escape(text, {'\x1b': ""}) diff --git a/pytest_nunit/nunit.py b/pytest_nunit/nunit.py index 1aea7ef..77623a3 100644 --- a/pytest_nunit/nunit.py +++ b/pytest_nunit/nunit.py @@ -22,6 +22,7 @@ ValueMatchFilterType, ) from .attrs2xml import AttrsXmlRenderer, CdataComment +import string FRAMEWORK_VERSION = "3.6.2" # Nunit version this was based on CLR_VERSION = sys.version @@ -34,6 +35,18 @@ } +def filter_ctrl(str): + if str: + str = "".join(filter(lambda x: x in string.printable, str)) + cwd = os.path.curdir + str = str.replace(os.path.join(cwd, "tests"), "tests") + return str + +class CDataComment_filtered(CdataComment): + def __init__(self, text): + text = filter_ctrl(text) + super().__init__(text) + def _get_user_id(): try: username = getpass.getuser() @@ -62,7 +75,7 @@ def _format_attachments(case, attach_on): :param case: The test case :type case: :class:`pytest.TestCase` - + :param attach_on: Attach-on criteria, one of any|pass|fail :type attach_on: ``str`` @@ -156,6 +169,10 @@ def environment(self): ) def test_cases(self, module): + for nodeid, case in self.nunitxml.modules[module].cases.items(): + if "reason" not in case: + case['reason'] = 'Not set' + return [ TestCaseElementType( id_=str(case["idref"]), @@ -171,11 +188,11 @@ def test_cases(self, module): environment=self.environment, settings=None, # TODO : Add settings as optional fixture failure=FailureType( - message=CdataComment(text=case["error"]), - stack_trace=CdataComment(text=case["stack-trace"]), + message=CDataComment_filtered(text=case["error"]), + stack_trace=CDataComment_filtered(text=case["stack-trace"]), ), - reason=ReasonType(message=CdataComment(text=case["reason"])), - output=CdataComment(text=case["reason"]), + reason=ReasonType(message=CDataComment_filtered(text=case["reason"])), + output=CDataComment_filtered(text=filter_ctrl(case["reason"])), assertions=_format_assertions(case), attachments=_format_attachments(case, self.nunitxml.attach_on), classname=get_node_names(nodeid)[0], diff --git a/pytest_nunit/plugin.py b/pytest_nunit/plugin.py index fd9e796..1f3753c 100644 --- a/pytest_nunit/plugin.py +++ b/pytest_nunit/plugin.py @@ -14,10 +14,11 @@ import functools from collections import namedtuple, defaultdict, Counter -from .nunit import NunitTestRun +from .nunit import NunitTestRun, filter_ctrl import logging import pytest +import string log = logging.getLogger(__name__) @@ -120,7 +121,6 @@ def pytest_unconfigure(config): del config._nunitxml config.pluginmanager.unregister(nunitxml) - class _NunitNodeReporter: def __init__(self, nodeid, nunit_xml): self.id = nodeid @@ -163,8 +163,9 @@ def record_testreport(self, testreport): elif testreport.when == "call": r = self.nunit_xml.cases[testreport.nodeid] r["call-report"] = testreport - r["error"] = testreport.longreprtext + r["error"] = filter_ctrl(testreport.longreprtext) r["stack-trace"] = self.nunit_xml._getcrashline(testreport) + r["properties"].update(testreport.user_properties) elif testreport.when == "teardown": r = self.nunit_xml.cases[testreport.nodeid] r["stop"] = datetime.utcnow() @@ -268,7 +269,7 @@ def __init__( self.show_username = show_username self.show_user_domain = show_user_domain self.attach_on = attach_on - logging.debug("Attach on criteria : {0}".format(attach_on)) + #logging.debug("Attach on criteria : {0}".format(attach_on)) self.idrefindex = 100 # Create a unique ID counter self.filters = filters @@ -387,30 +388,35 @@ def pytest_sessionfinish(self, session, *args): self.suite_stop_time - self.suite_start_time ).total_seconds() - full_report = self._create_module_report(self.cases) - self.stats.update(full_report.stats) - - # pytest-xdist collection is done on workers, - # so node_to_module_map is empty - if not self.node_to_module_map and self.cases: - for case_name, case in self.cases.items(): - if 'path' in case: - self.node_to_module_map[case_name] = case['path'] - else: - self.node_to_module_map[case_name] = ParentlessNode - - # Sort nodes into modules - for module_id in set(self.node_to_module_map.values()): - cases = { - nodeid: self.cases[nodeid] - for nodeid, m_id in self.node_to_module_map.items() - if module_id == m_id and nodeid in self.cases - } - self.modules[module_id] = self._create_module_report(cases) - - with open(self.logfile, "w", encoding="utf-8") as logfile: - result = NunitTestRun(self).generate_xml() - logfile.write(result.decode(encoding="utf-8")) + try: + full_report = self._create_module_report(self.cases) + self.stats.update(full_report.stats) + + # pytest-xdist collection is done on workers, + # so node_to_module_map is empty + if not self.node_to_module_map and self.cases: + for case_name, case in self.cases.items(): + if 'path' in case: + self.node_to_module_map[case_name] = case['path'] + else: + self.node_to_module_map[case_name] = ParentlessNode + + # Sort nodes into modules + for module_id in set(self.node_to_module_map.values()): + cases = { + nodeid: self.cases[nodeid] + for nodeid, m_id in self.node_to_module_map.items() + if module_id == m_id and nodeid in self.cases + } + self.modules[module_id] = self._create_module_report(cases) + + with open(self.logfile, "w", encoding="utf-8") as logfile: + result = NunitTestRun(self).generate_xml() + logfile.write(result.decode(encoding="utf-8")) + + except BaseException: + # ignore errors from the report generation as it is buggy + pass def pytest_terminal_summary(self, terminalreporter): """Notify XML report path."""