Repository: dask/distributed

Issue:
parse_stdout.py produces malformed Junit reports
parse_stdout.py has recently stopped working.
This causes Windows timeouts to be reported as a white box instead of a red box in https://dask.org/distributed/test_report.html.

https://github.com/dask/distributed/actions/runs/2293197167
> Publish test results: artifacts/windows-latest-3.10-notci1/pytest.xml#L976Error processing result file: not well-formed (invalid token): line 976, column 93



Discussion:

Resolving patch:
diff --git a/continuous_integration/scripts/parse_stdout.py b/continuous_integration/scripts/parse_stdout.py
--- a/continuous_integration/scripts/parse_stdout.py
+++ b/continuous_integration/scripts/parse_stdout.py
@@ -3,6 +3,7 @@
 """
 from __future__ import annotations
 
+import html
 import re
 import sys
 from collections import Counter, defaultdict
@@ -70,6 +71,8 @@ def build_xml(rows: list[tuple[str, str, set[str | None]]]) -> None:
     )
 
     for clsname, tname, outcomes in rows:
+        clsname = html.escape(clsname)
+        tname = html.escape(tname)
         print(f'<testcase classname="{clsname}" name="{tname}" time="0.0"', end="")
         if outcomes == {"PASSED"}:
             print(" />")


Test patch:
diff --git a/distributed/tests/test_parse_stdout.py b/distributed/tests/test_parse_stdout.py
--- a/distributed/tests/test_parse_stdout.py
+++ b/distributed/tests/test_parse_stdout.py
@@ -28,8 +28,9 @@
 distributed/tests/test1.py::test_flaky [32mPASSED[0m[31m                            [ 70%][0m
 distributed/tests/test1.py::test_leaking [32mPASSED[0m[31m                          [ 72%][0m
 distributed/tests/test1.py::test_leaking [32mLEAKED[0m[31m                          [ 72%][0m
-distributed/tests/test1.py::test_pass [32mPASSED[0m[31m                             [ 80%][0m
+distributed/tests/test1.py::test_pass [32mPASSED[0m[31m                             [ 75%][0m
 distributed/tests/test1.py::test_params[a a] [32mPASSED[0m[31m                      [ 78%][0m
+distributed/tests/test1.py::test_escape_chars[<lambda>] [32mPASSED[0m[31m           [ 80%][0m
 distributed/tests/test1.py::MyTest::test_unittest [32mPASSED[0m[31m                 [ 86%][0m
 distributed/tests/test1.py::test_timeout 
 """
@@ -57,6 +58,7 @@ def test_parse_rows():
         ("distributed.tests.test1", "test_leaking", {"PASSED"}),
         ("distributed.tests.test1", "test_pass", {"PASSED"}),
         ("distributed.tests.test1", "test_params[a a]", {"PASSED"}),
+        ("distributed.tests.test1", "test_escape_chars[<lambda>]", {"PASSED"}),
         ("distributed.tests.test1.MyTest", "test_unittest", {"PASSED"}),
         ("distributed.tests.test1", "test_timeout", {None}),
     ]
@@ -74,7 +76,7 @@ def test_build_xml(capsys):
     expect = """
 <?xml version="1.0" encoding="utf-8"?>
 <testsuites>
-<testsuite name="distributed" errors="3" failures="3" skipped="2" tests="14" time="0.0" timestamp="snip" hostname="">
+<testsuite name="distributed" errors="3" failures="3" skipped="2" tests="15" time="0.0" timestamp="snip" hostname="">
 <testcase classname="distributed.tests.test1" name="test_fail" time="0.0"><failure message=""></failure></testcase>
 <testcase classname="distributed.tests.test1" name="test_error_in_setup" time="0.0"><error message="failed on setup"></error></testcase>
 <testcase classname="distributed.tests.test1" name="test_pass_and_then_error_in_teardown" time="0.0"><error message="failed on teardown"></error></testcase>
@@ -86,6 +88,7 @@ def test_build_xml(capsys):
 <testcase classname="distributed.tests.test1" name="test_leaking" time="0.0" />
 <testcase classname="distributed.tests.test1" name="test_pass" time="0.0" />
 <testcase classname="distributed.tests.test1" name="test_params[a a]" time="0.0" />
+<testcase classname="distributed.tests.test1" name="test_escape_chars[&lt;lambda&gt;]" time="0.0" />
 <testcase classname="distributed.tests.test1.MyTest" name="test_unittest" time="0.0" />
 <testcase classname="distributed.tests.test1" name="test_timeout" time="0.0"><failure message="pytest-timeout exceeded"></failure></testcase>
 </testsuite>