Skip to content

Commit 87a359d

Browse files
committed
Handle JUnit <error> result state in parse_testcase()
Previously, test cases with an <error> element were silently classified as "passed" because the if/elif/else chain only checked for <skipped> and <failure>. This is a false-positive verdict — errors indicate unexpected exceptions or infrastructure crashes, not passing tests. - Add explicit handling for the <error> element in parse_testcase() - Add CSS styling for the tr_error result type (orange) - Also fix potential None in failure text (use `or ""` guard) - Add test fixture and test case for error state parsing Closes #132
1 parent 82819bb commit 87a359d

4 files changed

Lines changed: 62 additions & 2 deletions

File tree

sphinxcontrib/test_reports/css/common.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ tr.tr_skipped{
1010
background-color: rgba(0,0,0,0.1) !important;
1111
}
1212

13+
tr.tr_error, div.tr_error {
14+
background-color: rgba(255,165,0,0.2) !important;
15+
}
16+
1317
td.status p {
1418
padding: 3px 5px;
1519
text-align: center;
@@ -30,3 +34,8 @@ td.status p.tr_skipped{
3034
border: 1px solid #555;
3135
color: #555;
3236
}
37+
38+
td.status p.tr_error{
39+
border: 1px solid #cc6600;
40+
color: #cc6600;
41+
}

sphinxcontrib/test_reports/junitparser.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,14 @@ def parse_testcase(xml_object):
7070
tc_dict["result"] = "failure"
7171
tc_dict["type"] = result.attrib.get("type", "unknown")
7272
# tc_dict["text"] = re.sub(r"[\n\t]*", "", result.text) # Removes newlines and tabs
73-
tc_dict["text"] = result.text
74-
tc_dict["message"] = ""
73+
tc_dict["text"] = result.text or ""
74+
tc_dict["message"] = result.attrib.get("message", "")
75+
elif hasattr(testcase, "error"):
76+
result = testcase.error
77+
tc_dict["result"] = "error"
78+
tc_dict["type"] = result.attrib.get("type", "unknown")
79+
tc_dict["text"] = result.text or ""
80+
tc_dict["message"] = result.attrib.get("message", "unknown")
7581
else:
7682
tc_dict["result"] = "passed"
7783
tc_dict["type"] = ""
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<testsuite name="error_suite" tests="4" errors="1" failures="1" skips="0">
2+
<testcase classname="foo1" name="ASuccessfulTest" time="0.001"/>
3+
<testcase classname="foo2" name="AFailingTest" time="0.002">
4+
<failure type="AssertionError" message="expected true">some failure details</failure>
5+
</testcase>
6+
<testcase classname="foo3" name="AnErrorTest" time="0.003">
7+
<error type="RuntimeError" message="unexpected crash">stack trace here</error>
8+
</testcase>
9+
<testcase classname="foo4" name="ASkippedTest" time="0.000">
10+
<skipped type="skip" message="not applicable"/>
11+
</testcase>
12+
</testsuite>

tests/test_junit_parser.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
)
1717

1818
xml_ctest_path = os.path.join(os.path.dirname(__file__), "doc_test/utils", "ctest.xml")
19+
xml_error_path = os.path.join(
20+
os.path.dirname(__file__), "doc_test/utils", "xml_data_error.xml"
21+
)
1922

2023

2124
def test_init_parser():
@@ -148,3 +151,33 @@ def test_parse_ctest_xml():
148151
assert test_suite["testcases"][3]["result"] == "failure"
149152
assert test_suite["testcases"][4]["name"] == "skipped_test"
150153
assert test_suite["testcases"][4]["result"] == "skipped"
154+
155+
156+
def test_parse_error_xml():
157+
from sphinxcontrib.test_reports.junitparser import JUnitParser
158+
159+
parser = JUnitParser(xml_error_path)
160+
test_suites = parser.parse()
161+
162+
assert len(test_suites) == 1
163+
test_suite = test_suites[0]
164+
165+
assert test_suite["name"] == "error_suite"
166+
assert test_suite["tests"] == 4
167+
assert test_suite["errors"] == 1
168+
assert test_suite["failures"] == 1
169+
170+
assert test_suite["testcases"][0]["name"] == "ASuccessfulTest"
171+
assert test_suite["testcases"][0]["result"] == "passed"
172+
173+
assert test_suite["testcases"][1]["name"] == "AFailingTest"
174+
assert test_suite["testcases"][1]["result"] == "failure"
175+
176+
assert test_suite["testcases"][2]["name"] == "AnErrorTest"
177+
assert test_suite["testcases"][2]["result"] == "error"
178+
assert test_suite["testcases"][2]["type"] == "RuntimeError"
179+
assert test_suite["testcases"][2]["message"] == "unexpected crash"
180+
assert test_suite["testcases"][2]["text"] == "stack trace here"
181+
182+
assert test_suite["testcases"][3]["name"] == "ASkippedTest"
183+
assert test_suite["testcases"][3]["result"] == "skipped"

0 commit comments

Comments
 (0)