2
# junitxml: extensions to Python unittest to get output junitxml
3
# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
5
# Copying permitted under the LGPL-3 licence, included with this library.
7
"""unittest compatible JUnit XML output."""
12
from xml.sax.saxutils import escape
17
return junitxml.tests.test_suite()
20
class JUnitXmlResult(unittest.TestResult):
21
"""A TestResult which outputs JUnit compatible XML."""
23
def __init__(self, stream):
24
"""Create a JUnitXmlResult.
26
:param stream: A stream to write results to. Note that due to the
27
nature of JUnit XML output, nnothing will be written to the stream
28
until stopTestRun() is called.
30
super(JUnitXmlResult, self).__init__()
34
self._test_start = None
35
self._run_start = None
38
return super(JUnitXmlResult, self)
40
def startTestRun(self):
41
"""Start a test run."""
42
self._run_start = self._now()
45
if self._set_time is not None:
48
return datetime.datetime.utcnow()
50
def time(self, a_datetime):
51
self._set_time = a_datetime
53
def startTest(self, test):
54
self._test_start = self._now()
56
def _duration(self, from_datetime):
57
delta = self._now() - from_datetime
58
seconds = delta.days * 3600*24 + delta.seconds
59
return seconds + 0.000001 * delta.microseconds
61
def _test_case_string(self, test):
62
duration = self._duration(self._test_start)
63
prefix_suffix = test.id().rsplit('.', 1)
64
if len(prefix_suffix) == 1:
66
name = prefix_suffix[0]
68
classname = prefix_suffix[0]
69
name = prefix_suffix[1]
70
self._results.append('<testcase classname="%s" name="%s" '
71
'time="%0.3f"' % (escape(classname), escape(name), duration))
73
def stopTestRun(self):
76
This allows JUnitXmlResult to output the XML representation of the test
79
duration = self._duration(self._run_start)
80
self._stream.write('<testsuite errors="%d" failures="%d" name="" tests="0" time="%0.3f">\n' % (len(self.errors), len(self.failures), duration))
81
self._stream.write(''.join(self._results))
82
self._stream.write('</testsuite>\n')
84
def addError(self, test, error):
85
self._super().addError(test, error)
86
self._test_case_string(test)
87
self._results.append('>\n')
88
self._results.append('<error type="%s.%s">%s</error>\n</testcase>\n'% (escape(error[0].__module__), escape(error[0].__name__), escape(self._exc_info_to_string(error, test))))
90
def addFailure(self, test, error):
91
self._super().addFailure(test, error)
92
self._test_case_string(test)
93
self._results.append('>\n')
94
self._results.append('<failure type="%s.%s">%s</failure>\n</testcase>\n'% (escape(error[0].__module__), escape(error[0].__name__), escape(self._exc_info_to_string(error, test))))
96
def addSuccess(self, test):
97
self._super().addSuccess(test)
98
self._test_case_string(test)
99
self._results.append(' />\n')
101
def addSkip(self, test, reason):
102
self._super().addSkip(test, reason)
103
self._test_case_string(test)
104
self._results.append('>\n')
105
self._results.append('<skip type="%s.%s">%s</skip>\n</testcase>\n'% (escape(error[0].__module__), escape(error[0].__name__), escape(reason)))
107
def addUnexpectedSuccess(self, test):
108
self._super().addUnexpectedSuccess(test, error)
109
self._test_case_string(test)
110
self._results.append(' />\n')
112
def addExpectedFailure(self, test, error):
113
self._super().addExpectedFailure(test, error)
114
self._test_case_string(test)
115
self._results.append('>\n')
116
self._results.append('<failure type="%s.%s">%s</failure>\n</testcase>\n'% (escape(error[0].__module__), escape(error[0].__name__), escape(self._exc_info_to_string(error, test))))