185 Builds the XML document for the JUnit test suite.
186 Produces clean unicode strings and decodes non-unicode with the help of encoding.
187 @param encoding: Used to decode encoded strings.
188 @return: XML document with unicode string elements
192 test_suite_attributes = dict()
193 test_suite_attributes[
'name'] = self.
name
194 if any(c.assertions
for c
in self.
test_cases):
195 test_suite_attributes[
'assertions'] = \
196 str(sum([int(c.assertions)
for c
in self.
test_cases if c.assertions]))
197 test_suite_attributes[
'disabled'] = \
198 str(len([c
for c
in self.
test_cases if not c.is_enabled]))
199 test_suite_attributes[
'failures'] = \
200 str(len([c
for c
in self.
test_cases if c.is_failure()]))
201 test_suite_attributes[
'errors'] = \
202 str(len([c
for c
in self.
test_cases if c.is_error()]))
203 test_suite_attributes[
'skipped'] = \
204 str(len([c
for c
in self.
test_cases if c.is_skipped()]))
205 test_suite_attributes[
'time'] = \
206 str(sum(c.elapsed_sec
for c
in self.
test_cases if c.elapsed_sec))
207 test_suite_attributes[
'tests'] = str(len(self.
test_cases))
210 test_suite_attributes[
'hostname'] = self.
hostname
212 test_suite_attributes[
'id'] = self.
id
214 test_suite_attributes[
'package'] = self.
package
216 test_suite_attributes[
'timestamp'] = self.
timestamp
218 test_suite_attributes[
'file'] = self.
file
220 test_suite_attributes[
'log'] = self.
log
222 test_suite_attributes[
'url'] = self.
url
224 xml_element = ET.Element(
"testsuite", test_suite_attributes)
228 props_element = ET.SubElement(xml_element,
"properties")
234 ET.SubElement(props_element,
"property", attrs)
238 stdout_element = ET.SubElement(xml_element,
"system-out")
239 stdout_element.text = self.
stdout
243 stderr_element = ET.SubElement(xml_element,
"system-err")
244 stderr_element.text = self.
stderr
248 test_case_attributes = dict()
249 test_case_attributes[
'name'] = case.name
252 test_case_attributes[
'assertions'] =
"%d" % case.assertions
254 test_case_attributes[
'time'] =
"%f" % case.elapsed_sec
256 test_case_attributes[
'timestamp'] = case.timestamp
258 test_case_attributes[
'classname'] = case.classname
260 test_case_attributes[
'status'] = case.status
262 test_case_attributes[
'class'] = case.category
264 test_case_attributes[
'file'] = case.file
266 test_case_attributes[
'line'] = case.line
268 test_case_attributes[
'log'] = case.log
270 test_case_attributes[
'url'] = case.url
272 test_case_element = ET.SubElement(xml_element,
"testcase",
273 test_case_attributes)
276 if case.is_failure():
277 attrs = {
'type':
'failure'}
278 if case.failure_message:
279 attrs[
'message'] = case.failure_message
280 if case.failure_type:
281 attrs[
'type'] = case.failure_type
282 failure_element = ET.Element(
"failure", attrs)
283 if case.failure_output:
284 failure_element.text = case.failure_output
285 test_case_element.append(failure_element)
289 attrs = {
'type':
'error'}
290 if case.error_message:
291 attrs[
'message'] = case.error_message
293 attrs[
'type'] = case.error_type
294 error_element = ET.Element(
"error", attrs)
295 if case.error_output:
296 error_element.text = case.error_output
297 test_case_element.append(error_element)
300 if case.is_skipped():
301 attrs = {
'type':
'skipped'}
302 if case.skipped_message:
303 attrs[
'message'] = case.skipped_message
304 skipped_element = ET.Element(
"skipped", attrs)
305 if case.skipped_output:
306 skipped_element.text = case.skipped_output
307 test_case_element.append(skipped_element)
311 stdout_element = ET.Element(
"system-out")
312 stdout_element.text = case.stdout
313 test_case_element.append(stdout_element)
317 stderr_element = ET.Element(
"system-err")
318 stderr_element.text = case.stderr
319 test_case_element.append(stderr_element)
326 Returns the string representation of the JUnit XML document.
327 @param encoding: The encoding of the input.
328 @return: unicode string
334 raise Exception(
'test_suites must be a list of test suites')
336 xml_element = ET.Element(
"testsuites")
337 attributes = defaultdict(int)
338 for ts
in test_suites:
339 ts_xml = ts.build_xml_doc(encoding=encoding)
340 for key
in [
'failures',
'errors',
'tests',
'disabled']:
341 attributes[key] += int(ts_xml.get(key, 0))
343 attributes[key] += float(ts_xml.get(key, 0))
344 xml_element.append(ts_xml)
345 for key, value
in attributes.items():
346 xml_element.set(key, str(value))
348 xml_string = ET.tostring(xml_element, encoding=encoding)
350 xml_string = TestSuite._clean_illegal_xml_chars(
351 xml_string.decode(encoding
or 'utf-8'))
356 xml_string = xml_string.encode(encoding
or 'utf-8')
357 xml_string = xml.dom.minidom.parseString(xml_string)
359 xml_string = xml_string.toprettyxml(encoding=encoding)
361 xml_string = xml_string.decode(encoding)
366 def to_file(file_descriptor, test_suites, prettyprint=True, encoding=None):
368 Writes the JUnit XML document to a file.
370 xml_string = TestSuite.to_xml_string(test_suites,
371 prettyprint=prettyprint,
374 file_descriptor.write(xml_string)
379 Removes any illegal unicode characters from the given XML string.
380 @see: http://stackoverflow.com/questions/1707890/fast-way-to-filter-illegal-xml-unicode-chars-in-python
383 illegal_unichrs = [(0x00, 0x08), (0x0B, 0x1F), (0x7F, 0x84),
384 (0x86, 0x9F), (0xD800, 0xDFFF), (0xFDD0, 0xFDDF),
385 (0xFFFE, 0xFFFF), (0x1FFFE, 0x1FFFF),
386 (0x2FFFE, 0x2FFFF), (0x3FFFE, 0x3FFFF),
387 (0x4FFFE, 0x4FFFF), (0x5FFFE, 0x5FFFF),
388 (0x6FFFE, 0x6FFFF), (0x7FFFE, 0x7FFFF),
389 (0x8FFFE, 0x8FFFF), (0x9FFFE, 0x9FFFF),
390 (0xAFFFE, 0xAFFFF), (0xBFFFE, 0xBFFFF),
391 (0xCFFFE, 0xCFFFF), (0xDFFFE, 0xDFFFF),
392 (0xEFFFE, 0xEFFFF), (0xFFFFE, 0xFFFFF),
393 (0x10FFFE, 0x10FFFF)]
397 for (low, high)
in illegal_unichrs
if low < sys.maxunicode
400 illegal_xml_re = re.compile(
'[%s]' %
''.join(illegal_ranges))
401 return illegal_xml_re.sub(
'', string_to_clean)