From cc0092b9d898fdd903f56b9b937139fbf766d1c1 Mon Sep 17 00:00:00 2001 From: holesch <8659229+holesch@users.noreply.github.com> Date: Fri, 12 Aug 2022 11:11:03 +0200 Subject: [PATCH] JUnit XML: Escape error messages in setup/teardown (#10190) Co-authored-by: Holesch, Simon (BSH) --- AUTHORS | 1 + changelog/10190.bugfix.rst | 1 + src/_pytest/junitxml.py | 2 +- testing/test_junitxml.py | 22 ++++++++++++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 changelog/10190.bugfix.rst diff --git a/AUTHORS b/AUTHORS index b8e6377fb..e797f2146 100644 --- a/AUTHORS +++ b/AUTHORS @@ -313,6 +313,7 @@ Seth Junot Shantanu Jain Shubham Adep Simon Gomizelj +Simon Holesch Simon Kerr Skylar Downes Srinivas Reddy Thatiparthy diff --git a/changelog/10190.bugfix.rst b/changelog/10190.bugfix.rst new file mode 100644 index 000000000..731d0efad --- /dev/null +++ b/changelog/10190.bugfix.rst @@ -0,0 +1 @@ +Invalid XML characters in setup or teardown error messages are now properly escaped for JUnit XML reports. diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index c92a227bf..66057ef6f 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -231,7 +231,7 @@ class _NodeReporter: msg = f'failed on teardown with "{reason}"' else: msg = f'failed on setup with "{reason}"' - self._add_simple("error", msg, str(report.longrepr)) + self._add_simple("error", bin_xml_escape(msg), str(report.longrepr)) def append_skipped(self, report: TestReport) -> None: if hasattr(report, "wasxfail"): diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 02531e814..b266c76d9 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1625,6 +1625,28 @@ def test_escaped_skipreason_issue3533( snode.assert_attr(message="1 <> 2") +def test_escaped_setup_teardown_error( + pytester: Pytester, run_and_parse: RunAndParse +) -> None: + pytester.makepyfile( + """ + import pytest + + @pytest.fixture() + def my_setup(): + raise Exception("error: \033[31mred\033[m") + + def test_esc(my_setup): + pass + """ + ) + _, dom = run_and_parse() + node = dom.find_first_by_tag("testcase") + snode = node.find_first_by_tag("error") + assert "#x1B[31mred#x1B[m" in snode["message"] + assert "#x1B[31mred#x1B[m" in snode.text + + @parametrize_families def test_logging_passing_tests_disabled_does_not_log_test_output( pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str