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)