- # Copyright 2006, Google Inc. 
- # All rights reserved. 
- # 
- # Redistribution and use in source and binary forms, with or without 
- # modification, are permitted provided that the following conditions are 
- # met: 
- # 
- #     * Redistributions of source code must retain the above copyright 
- # notice, this list of conditions and the following disclaimer. 
- #     * Redistributions in binary form must reproduce the above 
- # copyright notice, this list of conditions and the following disclaimer 
- # in the documentation and/or other materials provided with the 
- # distribution. 
- #     * Neither the name of Google Inc. nor the names of its 
- # contributors may be used to endorse or promote products derived from 
- # this software without specific prior written permission. 
- # 
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
- # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
- # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
- # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
- # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
- # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
- # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
-   
- """Unit test utilities for gtest_xml_output""" 
-   
- import re 
- from xml.dom import minidom, Node 
- import gtest_test_utils 
-   
- GTEST_DEFAULT_OUTPUT_FILE = 'test_detail.xml' 
-   
- class GTestXMLTestCase(gtest_test_utils.TestCase): 
-   """ 
-   Base class for tests of Google Test's XML output functionality. 
-   """ 
-   
-   
-   def AssertEquivalentNodes(self, expected_node, actual_node): 
-     """ 
-     Asserts that actual_node (a DOM node object) is equivalent to 
-     expected_node (another DOM node object), in that either both of 
-     them are CDATA nodes and have the same value, or both are DOM 
-     elements and actual_node meets all of the following conditions: 
-   
-     *  It has the same tag name as expected_node. 
-     *  It has the same set of attributes as expected_node, each with 
-        the same value as the corresponding attribute of expected_node. 
-        Exceptions are any attribute named "time", which needs only be 
-        convertible to a floating-point number and any attribute named 
-        "type_param" which only has to be non-empty. 
-     *  It has an equivalent set of child nodes (including elements and 
-        CDATA sections) as expected_node.  Note that we ignore the 
-        order of the children as they are not guaranteed to be in any 
-        particular order. 
-     """ 
-   
-     if expected_node.nodeType == Node.CDATA_SECTION_NODE: 
-       self.assertEquals(Node.CDATA_SECTION_NODE, actual_node.nodeType) 
-       self.assertEquals(expected_node.nodeValue, actual_node.nodeValue) 
-       return 
-   
-     self.assertEquals(Node.ELEMENT_NODE, actual_node.nodeType) 
-     self.assertEquals(Node.ELEMENT_NODE, expected_node.nodeType) 
-     self.assertEquals(expected_node.tagName, actual_node.tagName) 
-   
-     expected_attributes = expected_node.attributes 
-     actual_attributes   = actual_node  .attributes 
-     self.assertEquals( 
-         expected_attributes.length, actual_attributes.length, 
-         'attribute numbers differ in element %s:\nExpected: %r\nActual: %r' % ( 
-             actual_node.tagName, expected_attributes.keys(), 
-             actual_attributes.keys())) 
-     for i in range(expected_attributes.length): 
-       expected_attr = expected_attributes.item(i) 
-       actual_attr   = actual_attributes.get(expected_attr.name) 
-       self.assert_( 
-           actual_attr is not None, 
-           'expected attribute %s not found in element %s' % 
-           (expected_attr.name, actual_node.tagName)) 
-       self.assertEquals( 
-           expected_attr.value, actual_attr.value, 
-           ' values of attribute %s in element %s differ: %s vs %s' % 
-           (expected_attr.name, actual_node.tagName, 
-            expected_attr.value, actual_attr.value)) 
-   
-     expected_children = self._GetChildren(expected_node) 
-     actual_children = self._GetChildren(actual_node) 
-     self.assertEquals( 
-         len(expected_children), len(actual_children), 
-         'number of child elements differ in element ' + actual_node.tagName) 
-     for child_id, child in expected_children.iteritems(): 
-       self.assert_(child_id in actual_children, 
-                    '<%s> is not in <%s> (in element %s)' % 
-                    (child_id, actual_children, actual_node.tagName)) 
-       self.AssertEquivalentNodes(child, actual_children[child_id]) 
-   
-   identifying_attribute = { 
-       'testsuites': 'name', 
-       'testsuite': 'name', 
-       'testcase': 'name', 
-       'failure': 'message', 
-       'property': 'name', 
-   } 
-   
-   def _GetChildren(self, element): 
-     """ 
-     Fetches all of the child nodes of element, a DOM Element object. 
-     Returns them as the values of a dictionary keyed by the IDs of the 
-     children.  For <testsuites>, <testsuite>, <testcase>, and <property> 
-     elements, the ID is the value of their "name" attribute; for <failure> 
-     elements, it is the value of the "message" attribute; for <properties> 
-     elements, it is the value of their parent's "name" attribute plus the 
-     literal string "properties"; CDATA sections and non-whitespace 
-     text nodes are concatenated into a single CDATA section with ID 
-     "detail".  An exception is raised if any element other than the above 
-     four is encountered, if two child elements with the same identifying 
-     attributes are encountered, or if any other type of node is encountered. 
-     """ 
-   
-     children = {} 
-     for child in element.childNodes: 
-       if child.nodeType == Node.ELEMENT_NODE: 
-         if child.tagName == 'properties': 
-           self.assert_(child.parentNode is not None, 
-                        'Encountered <properties> element without a parent') 
-           child_id = child.parentNode.getAttribute('name') + '-properties' 
-         else: 
-           self.assert_(child.tagName in self.identifying_attribute, 
-                        'Encountered unknown element <%s>' % child.tagName) 
-           child_id = child.getAttribute( 
-               self.identifying_attribute[child.tagName]) 
-         self.assert_(child_id not in children) 
-         children[child_id] = child 
-       elif child.nodeType in [Node.TEXT_NODE, Node.CDATA_SECTION_NODE]: 
-         if 'detail' not in children: 
-           if (child.nodeType == Node.CDATA_SECTION_NODE or 
-               not child.nodeValue.isspace()): 
-             children['detail'] = child.ownerDocument.createCDATASection( 
-                 child.nodeValue) 
-         else: 
-           children['detail'].nodeValue += child.nodeValue 
-       else: 
-         self.fail('Encountered unexpected node type %d' % child.nodeType) 
-     return children 
-   
-   def NormalizeXml(self, element): 
-     """ 
-     Normalizes Google Test's XML output to eliminate references to transient 
-     information that may change from run to run. 
-   
-     *  The "time" attribute of <testsuites>, <testsuite> and <testcase> 
-        elements is replaced with a single asterisk, if it contains 
-        only digit characters. 
-     *  The "timestamp" attribute of <testsuites> elements is replaced with a 
-        single asterisk, if it contains a valid ISO8601 datetime value. 
-     *  The "type_param" attribute of <testcase> elements is replaced with a 
-        single asterisk (if it sn non-empty) as it is the type name returned 
-        by the compiler and is platform dependent. 
-     *  The line info reported in the first line of the "message" 
-        attribute and CDATA section of <failure> elements is replaced with the 
-        file's basename and a single asterisk for the line number. 
-     *  The directory names in file paths are removed. 
-     *  The stack traces are removed. 
-     """ 
-   
-     if element.tagName == 'testsuites': 
-       timestamp = element.getAttributeNode('timestamp') 
-       timestamp.value = re.sub(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d$', 
-                                '*', timestamp.value) 
-     if element.tagName in ('testsuites', 'testsuite', 'testcase'): 
-       time = element.getAttributeNode('time') 
-       time.value = re.sub(r'^\d+(\.\d+)?$', '*', time.value) 
-       type_param = element.getAttributeNode('type_param') 
-       if type_param and type_param.value: 
-         type_param.value = '*' 
-     elif element.tagName == 'failure': 
-       source_line_pat = r'^.*[/\\](.*:)\d+\n' 
-       # Replaces the source line information with a normalized form. 
-       message = element.getAttributeNode('message') 
-       message.value = re.sub(source_line_pat, '\\1*\n', message.value) 
-       for child in element.childNodes: 
-         if child.nodeType == Node.CDATA_SECTION_NODE: 
-           # Replaces the source line information with a normalized form. 
-           cdata = re.sub(source_line_pat, '\\1*\n', child.nodeValue) 
-           # Removes the actual stack trace. 
-           child.nodeValue = re.sub(r'Stack trace:\n(.|\n)*', 
-                                    'Stack trace:\n*', cdata) 
-     for child in element.childNodes: 
-       if child.nodeType == Node.ELEMENT_NODE: 
-         self.NormalizeXml(child) 
-