Skip to content

Commit 63e95fd

Browse files
martinrakovec-comapsysradium
authored andcommitted
Add support for multiple sub-elements (failure/error/skipped) in testcase
Signed-off-by: Martin Rakovec <martin.rakovec@comap-control.com>
1 parent f7d5463 commit 63e95fd

3 files changed

Lines changed: 175 additions & 56 deletions

File tree

‎junit_xml/__init__.py‎

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -198,36 +198,38 @@ def build_xml_doc(self, encoding=None):
198198

199199
# failures
200200
for failure in case.failures:
201-
attrs = {"type": "failure"}
202-
if failure['message']:
203-
attrs['message'] = decode(failure['message'], encoding)
204-
if failure['type']:
205-
attrs['type'] = decode(failure['type'], encoding)
206-
failure_element = ET.Element("failure", attrs)
207-
if failure['output']:
208-
failure_element.text = decode(failure['output'], encoding)
209-
test_case_element.append(failure_element)
201+
if failure["output"] or failure["message"]:
202+
attrs = {"type": "failure"}
203+
if failure["message"]:
204+
attrs["message"] = decode(failure["message"], encoding)
205+
if failure["type"]:
206+
attrs["type"] = decode(failure["type"], encoding)
207+
failure_element = ET.Element("failure", attrs)
208+
if failure["output"]:
209+
failure_element.text = decode(failure["output"], encoding)
210+
test_case_element.append(failure_element)
210211

211212
# errors
212213
for error in case.errors:
213-
attrs = {"type": "error"}
214-
if error['message']:
215-
attrs['message'] = decode(error['message'], encoding)
216-
if error['type']:
217-
attrs['type'] = decode(error['type'], encoding)
218-
error_element = ET.Element("error", attrs)
219-
if error['output']:
220-
error_element.text = decode(error['output'], encoding)
221-
test_case_element.append(error_element)
214+
if error["message"] or error["output"]:
215+
attrs = {"type": "error"}
216+
if error["message"]:
217+
attrs["message"] = decode(error["message"], encoding)
218+
if error["type"]:
219+
attrs["type"] = decode(error["type"], encoding)
220+
error_element = ET.Element("error", attrs)
221+
if error["output"]:
222+
error_element.text = decode(error["output"], encoding)
223+
test_case_element.append(error_element)
222224

223225
# skippeds
224226
for skipped in case.skipped:
225227
attrs = {"type": "skipped"}
226-
if skipped['message']:
227-
attrs['message'] = decode(skipped['message'], encoding)
228+
if skipped["message"]:
229+
attrs["message"] = decode(skipped["message"], encoding)
228230
skipped_element = ET.Element("skipped", attrs)
229-
if skipped['output']:
230-
skipped_element.text = decode(skipped['output'], encoding)
231+
if skipped["output"]:
232+
skipped_element.text = decode(skipped["output"], encoding)
231233
test_case_element.append(skipped_element)
232234

233235
# test stdout
@@ -377,6 +379,7 @@ def __init__(
377379
line=None,
378380
log=None,
379381
url=None,
382+
allow_multiple_subelements=False,
380383
):
381384
self.name = name
382385
self.assertions = assertions
@@ -396,41 +399,70 @@ def __init__(
396399
self.errors = []
397400
self.failures = []
398401
self.skipped = []
402+
self.allow_multiple_subalements = allow_multiple_subelements
399403

400404
def add_error_info(self, message=None, output=None, error_type=None):
401405
"""Adds an error message, output, or both to the test case"""
402-
if message or output or error_type:
403-
error = {}
404-
error['message'] = message
405-
error['output'] = output
406-
error['type'] = error_type
406+
error = {}
407+
error["message"] = message
408+
error["output"] = output
409+
error["type"] = error_type
410+
if self.allow_multiple_subalements:
411+
if message or output:
412+
self.errors.append(error)
413+
elif not len(self.errors):
407414
self.errors.append(error)
415+
else:
416+
if message:
417+
self.errors[0]["message"] = message
418+
if output:
419+
self.errors[0]["output"] = output
420+
if error_type:
421+
self.errors[0]["type"] = error_type
408422

409423
def add_failure_info(self, message=None, output=None, failure_type=None):
410424
"""Adds a failure message, output, or both to the test case"""
411-
if message or output or failure_type:
412-
failure = {}
413-
failure['message'] = message
414-
failure['output'] = output
415-
failure['type'] = failure_type
425+
failure = {}
426+
failure["message"] = message
427+
failure["output"] = output
428+
failure["type"] = failure_type
429+
if self.allow_multiple_subalements:
430+
if message or output:
431+
self.failures.append(failure)
432+
elif not len(self.failures):
416433
self.failures.append(failure)
434+
else:
435+
if message:
436+
self.failures[0]["message"] = message
437+
if output:
438+
self.failures[0]["output"] = output
439+
if failure_type:
440+
self.failures[0]["type"] = failure_type
417441

418442
def add_skipped_info(self, message=None, output=None):
419443
"""Adds a skipped message, output, or both to the test case"""
420-
if message or output:
421-
skipped = {}
422-
skipped['message'] = message
423-
skipped['output'] = output
444+
skipped = {}
445+
skipped["message"] = message
446+
skipped["output"] = output
447+
if self.allow_multiple_subalements:
448+
if message or output:
449+
self.skipped.append(skipped)
450+
elif not len(self.skipped):
424451
self.skipped.append(skipped)
452+
else:
453+
if message:
454+
self.skipped[0]["message"] = message
455+
if output:
456+
self.skipped[0]["output"] = output
425457

426458
def is_failure(self):
427459
"""returns true if this test case is a failure"""
428-
return (len(self.failures) > 0)
460+
return sum(1 for f in self.failures if f["message"] or f["output"]) > 0
429461

430462
def is_error(self):
431463
"""returns true if this test case is an error"""
432-
return (len(self.errors) > 0)
464+
return sum(1 for e in self.errors if e["message"] or e["output"]) > 0
433465

434466
def is_skipped(self):
435467
"""returns true if this test case has been skipped"""
436-
return (len(self.skipped) > 0)
468+
return len(self.skipped) > 0

‎tests/asserts.py‎

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ def verify_test_case( # noqa: E302
1111
skipped_output=None,
1212
stdout=None,
1313
stderr=None,
14+
errors=None,
15+
failures=None,
16+
skipped=None,
1417
):
1518
for k, v in expected_attributes.items():
1619
assert test_case_element.attributes[k].value == v
@@ -23,38 +26,58 @@ def verify_test_case( # noqa: E302
2326
if stdout:
2427
assert test_case_element.getElementsByTagName("system-out")[0].firstChild.nodeValue.strip() == stdout
2528

26-
errors = test_case_element.getElementsByTagName("error")
29+
_errors = test_case_element.getElementsByTagName("error")
2730
if error_message or error_output:
28-
assert len(errors) > 0
31+
assert len(_errors) > 0
32+
elif errors:
33+
assert len(errors) == len(_errors)
2934
else:
30-
assert len(errors) == 0
35+
assert len(_errors) == 0
3136

3237
if error_message:
33-
assert errors[0].attributes["message"].value == error_message
38+
assert _errors[0].attributes["message"].value == error_message
3439

35-
if error_type and errors:
36-
assert errors[0].attributes["type"].value == error_type
40+
if error_type and _errors:
41+
assert _errors[0].attributes["type"].value == error_type
3742

3843
if error_output:
39-
assert errors[0].firstChild.nodeValue.strip() == error_output
44+
assert _errors[0].firstChild.nodeValue.strip() == error_output
4045

41-
failures = test_case_element.getElementsByTagName("failure")
46+
for error_exp, error_r in zip(errors or [], _errors):
47+
assert error_r.attributes["message"].value == error_exp["message"]
48+
assert error_r.firstChild.nodeValue.strip() == error_exp["output"]
49+
assert error_r.attributes["type"].value == error_exp["type"]
50+
51+
_failures = test_case_element.getElementsByTagName("failure")
4252
if failure_message or failure_output:
43-
assert len(failures) > 0
53+
assert len(_failures) > 0
54+
elif failures:
55+
assert len(failures) == len(_failures)
4456
else:
45-
assert len(failures) == 0
57+
assert len(_failures) == 0
4658

4759
if failure_message:
48-
assert failures[0].attributes["message"].value == failure_message
60+
assert _failures[0].attributes["message"].value == failure_message
4961

50-
if failure_type and failures:
51-
assert failures[0].attributes["type"].value == failure_type
62+
if failure_type and _failures:
63+
assert _failures[0].attributes["type"].value == failure_type
5264

5365
if failure_output:
54-
assert failures[0].firstChild.nodeValue.strip() == failure_output
66+
assert _failures[0].firstChild.nodeValue.strip() == failure_output
67+
68+
for failure_exp, failure_r in zip(failures or [], _failures):
69+
assert failure_r.attributes["message"].value == failure_exp["message"]
70+
assert failure_r.firstChild.nodeValue.strip() == failure_exp["output"]
71+
assert failure_r.attributes["type"].value == failure_exp["type"]
5572

56-
skipped = test_case_element.getElementsByTagName("skipped")
73+
_skipped = test_case_element.getElementsByTagName("skipped")
5774
if skipped_message or skipped_output:
58-
assert len(skipped) > 0
75+
assert len(_skipped) > 0
76+
elif skipped:
77+
assert len(skipped) == len(_skipped)
5978
else:
60-
assert len(skipped) == 0
79+
assert len(_skipped) == 0
80+
81+
for skipped_exp, skipped_r in zip(skipped or [], _skipped):
82+
assert skipped_r.attributes["message"].value == skipped_exp["message"]
83+
assert skipped_r.firstChild.nodeValue.strip() == skipped_exp["output"]

‎tests/test_test_case.py‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,67 @@ def test_init_unicode():
258258
error_message=decode("Skipped error äöü", "utf-8"),
259259
error_output=decode("I skippäd with an error!", "utf-8"),
260260
)
261+
262+
263+
def test_multiple_errors():
264+
"""Tests multiple errors in one test case"""
265+
tc = Case("Multiple error", allow_multiple_subelements=True)
266+
tc.add_error_info("First error", "First error message")
267+
(_, tcs) = serialize_and_read(Suite("test", [tc]))[0]
268+
verify_test_case(
269+
tcs[0],
270+
{"name": "Multiple error"},
271+
errors=[{"message": "First error", "output": "First error message", "type": "error"}],
272+
)
273+
tc.add_error_info("Second error", "Second error message")
274+
(_, tcs) = serialize_and_read(Suite("test", [tc]))[0]
275+
verify_test_case(
276+
tcs[0],
277+
{"name": "Multiple error"},
278+
errors=[
279+
{"message": "First error", "output": "First error message", "type": "error"},
280+
{"message": "Second error", "output": "Second error message", "type": "error"},
281+
],
282+
)
283+
284+
285+
def test_multiple_failures():
286+
"""Tests multiple failures in one test case"""
287+
tc = Case("Multiple failures", allow_multiple_subelements=True)
288+
tc.add_failure_info("First failure", "First failure message")
289+
(_, tcs) = serialize_and_read(Suite("test", [tc]))[0]
290+
verify_test_case(
291+
tcs[0],
292+
{"name": "Multiple failures"},
293+
failures=[{"message": "First failure", "output": "First failure message", "type": "failure"}],
294+
)
295+
tc.add_failure_info("Second failure", "Second failure message")
296+
(_, tcs) = serialize_and_read(Suite("test", [tc]))[0]
297+
verify_test_case(
298+
tcs[0],
299+
{"name": "Multiple failures"},
300+
failures=[
301+
{"message": "First failure", "output": "First failure message", "type": "failure"},
302+
{"message": "Second failure", "output": "Second failure message", "type": "failure"},
303+
],
304+
)
305+
306+
307+
def test_multiple_skipped():
308+
"""Tests multiple skipped messages in one test case"""
309+
tc = Case("Multiple skipped", allow_multiple_subelements=True)
310+
tc.add_skipped_info("First skipped", "First skipped message")
311+
(_, tcs) = serialize_and_read(Suite("test", [tc]))[0]
312+
verify_test_case(
313+
tcs[0], {"name": "Multiple skipped"}, skipped=[{"message": "First skipped", "output": "First skipped message"}]
314+
)
315+
tc.add_skipped_info("Second skipped", "Second skipped message")
316+
(_, tcs) = serialize_and_read(Suite("test", [tc]))[0]
317+
verify_test_case(
318+
tcs[0],
319+
{"name": "Multiple skipped"},
320+
skipped=[
321+
{"message": "First skipped", "output": "First skipped message"},
322+
{"message": "Second skipped", "output": "Second skipped message"},
323+
],
324+
)

0 commit comments

Comments
 (0)