From 3b41c65c81d649d962be5ec469f44104b8d09748 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 01:26:06 +0000 Subject: [PATCH] [8.0.x] Escape skip reason in junitxml (#11845) Co-authored-by: clee2000 <44682903+clee2000@users.noreply.github.com> --- changelog/11842.bugfix.rst | 1 + src/_pytest/junitxml.py | 4 +++- testing/test_junitxml.py | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 changelog/11842.bugfix.rst diff --git a/changelog/11842.bugfix.rst b/changelog/11842.bugfix.rst new file mode 100644 index 00000000000..3a11b110f44 --- /dev/null +++ b/changelog/11842.bugfix.rst @@ -0,0 +1 @@ +Properly escape the ``reason`` of a :ref:`skip ` mark when writing JUnit XML files. diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index 9ee35b84e84..c8032e158da 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -248,7 +248,9 @@ def append_skipped(self, report: TestReport) -> None: skipreason = skipreason[9:] details = f"{filename}:{lineno}: {skipreason}" - skipped = ET.Element("skipped", type="pytest.skip", message=skipreason) + skipped = ET.Element( + "skipped", type="pytest.skip", message=bin_xml_escape(skipreason) + ) skipped.text = bin_xml_escape(details) self.append(skipped) self.write_captured_output(report) diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 3f88c21e2b7..b0c2d1c6d4e 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1653,6 +1653,23 @@ def test_skip(): snode.assert_attr(message="1 <> 2") +def test_bin_escaped_skipreason(pytester: Pytester, run_and_parse: RunAndParse) -> None: + """Escape special characters from mark.skip reason (#11842).""" + pytester.makepyfile( + """ + import pytest + @pytest.mark.skip("\33[31;1mred\33[0m") + def test_skip(): + pass + """ + ) + _, dom = run_and_parse() + node = dom.find_first_by_tag("testcase") + snode = node.find_first_by_tag("skipped") + assert "#x1B[31;1mred#x1B[0m" in snode.text + snode.assert_attr(message="#x1B[31;1mred#x1B[0m") + + def test_escaped_setup_teardown_error( pytester: Pytester, run_and_parse: RunAndParse ) -> None: