gtest_xml_test_utils.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Copyright 2006, Google Inc.
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions are
00008 # met:
00009 #
00010 #     * Redistributions of source code must retain the above copyright
00011 # notice, this list of conditions and the following disclaimer.
00012 #     * Redistributions in binary form must reproduce the above
00013 # copyright notice, this list of conditions and the following disclaimer
00014 # in the documentation and/or other materials provided with the
00015 # distribution.
00016 #     * Neither the name of Google Inc. nor the names of its
00017 # contributors may be used to endorse or promote products derived from
00018 # this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00023 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00024 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00025 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00026 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00027 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00028 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00029 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00030 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031 
00032 """Unit test utilities for gtest_xml_output"""
00033 
00034 __author__ = 'eefacm@gmail.com (Sean Mcafee)'
00035 
00036 import re
00037 from xml.dom import minidom, Node
00038 
00039 import gtest_test_utils
00040 
00041 
00042 GTEST_OUTPUT_FLAG         = '--gtest_output'
00043 GTEST_DEFAULT_OUTPUT_FILE = 'test_detail.xml'
00044 
00045 class GTestXMLTestCase(gtest_test_utils.TestCase):
00046   """
00047   Base class for tests of Google Test's XML output functionality.
00048   """
00049 
00050 
00051   def AssertEquivalentNodes(self, expected_node, actual_node):
00052     """
00053     Asserts that actual_node (a DOM node object) is equivalent to
00054     expected_node (another DOM node object), in that either both of
00055     them are CDATA nodes and have the same value, or both are DOM
00056     elements and actual_node meets all of the following conditions:
00057 
00058     *  It has the same tag name as expected_node.
00059     *  It has the same set of attributes as expected_node, each with
00060        the same value as the corresponding attribute of expected_node.
00061        Exceptions are any attribute named "time", which needs only be
00062        convertible to a floating-point number and any attribute named
00063        "type_param" which only has to be non-empty.
00064     *  It has an equivalent set of child nodes (including elements and
00065        CDATA sections) as expected_node.  Note that we ignore the
00066        order of the children as they are not guaranteed to be in any
00067        particular order.
00068     """
00069 
00070     if expected_node.nodeType == Node.CDATA_SECTION_NODE:
00071       self.assertEquals(Node.CDATA_SECTION_NODE, actual_node.nodeType)
00072       self.assertEquals(expected_node.nodeValue, actual_node.nodeValue)
00073       return
00074 
00075     self.assertEquals(Node.ELEMENT_NODE, actual_node.nodeType)
00076     self.assertEquals(Node.ELEMENT_NODE, expected_node.nodeType)
00077     self.assertEquals(expected_node.tagName, actual_node.tagName)
00078 
00079     expected_attributes = expected_node.attributes
00080     actual_attributes   = actual_node  .attributes
00081     self.assertEquals(
00082         expected_attributes.length, actual_attributes.length,
00083         'attribute numbers differ in element %s:\nExpected: %r\nActual: %r' % (
00084             actual_node.tagName, expected_attributes.keys(),
00085             actual_attributes.keys()))
00086     for i in range(expected_attributes.length):
00087       expected_attr = expected_attributes.item(i)
00088       actual_attr   = actual_attributes.get(expected_attr.name)
00089       self.assert_(
00090           actual_attr is not None,
00091           'expected attribute %s not found in element %s' %
00092           (expected_attr.name, actual_node.tagName))
00093       self.assertEquals(
00094           expected_attr.value, actual_attr.value,
00095           ' values of attribute %s in element %s differ: %s vs %s' %
00096           (expected_attr.name, actual_node.tagName,
00097            expected_attr.value, actual_attr.value))
00098 
00099     expected_children = self._GetChildren(expected_node)
00100     actual_children = self._GetChildren(actual_node)
00101     self.assertEquals(
00102         len(expected_children), len(actual_children),
00103         'number of child elements differ in element ' + actual_node.tagName)
00104     for child_id, child in expected_children.iteritems():
00105       self.assert_(child_id in actual_children,
00106                    '<%s> is not in <%s> (in element %s)' %
00107                    (child_id, actual_children, actual_node.tagName))
00108       self.AssertEquivalentNodes(child, actual_children[child_id])
00109 
00110   identifying_attribute = {
00111     'testsuites': 'name',
00112     'testsuite': 'name',
00113     'testcase':  'name',
00114     'failure':   'message',
00115     }
00116 
00117   def _GetChildren(self, element):
00118     """
00119     Fetches all of the child nodes of element, a DOM Element object.
00120     Returns them as the values of a dictionary keyed by the IDs of the
00121     children.  For <testsuites>, <testsuite> and <testcase> elements, the ID
00122     is the value of their "name" attribute; for <failure> elements, it is
00123     the value of the "message" attribute; CDATA sections and non-whitespace
00124     text nodes are concatenated into a single CDATA section with ID
00125     "detail".  An exception is raised if any element other than the above
00126     four is encountered, if two child elements with the same identifying
00127     attributes are encountered, or if any other type of node is encountered.
00128     """
00129 
00130     children = {}
00131     for child in element.childNodes:
00132       if child.nodeType == Node.ELEMENT_NODE:
00133         self.assert_(child.tagName in self.identifying_attribute,
00134                      'Encountered unknown element <%s>' % child.tagName)
00135         childID = child.getAttribute(self.identifying_attribute[child.tagName])
00136         self.assert_(childID not in children)
00137         children[childID] = child
00138       elif child.nodeType in [Node.TEXT_NODE, Node.CDATA_SECTION_NODE]:
00139         if 'detail' not in children:
00140           if (child.nodeType == Node.CDATA_SECTION_NODE or
00141               not child.nodeValue.isspace()):
00142             children['detail'] = child.ownerDocument.createCDATASection(
00143                 child.nodeValue)
00144         else:
00145           children['detail'].nodeValue += child.nodeValue
00146       else:
00147         self.fail('Encountered unexpected node type %d' % child.nodeType)
00148     return children
00149 
00150   def NormalizeXml(self, element):
00151     """
00152     Normalizes Google Test's XML output to eliminate references to transient
00153     information that may change from run to run.
00154 
00155     *  The "time" attribute of <testsuites>, <testsuite> and <testcase>
00156        elements is replaced with a single asterisk, if it contains
00157        only digit characters.
00158     *  The "timestamp" attribute of <testsuites> elements is replaced with a
00159        single asterisk, if it contains a valid ISO8601 datetime value.
00160     *  The "type_param" attribute of <testcase> elements is replaced with a
00161        single asterisk (if it sn non-empty) as it is the type name returned
00162        by the compiler and is platform dependent.
00163     *  The line info reported in the first line of the "message"
00164        attribute and CDATA section of <failure> elements is replaced with the
00165        file's basename and a single asterisk for the line number.
00166     *  The directory names in file paths are removed.
00167     *  The stack traces are removed.
00168     """
00169 
00170     if element.tagName == 'testsuites':
00171       timestamp = element.getAttributeNode('timestamp')
00172       timestamp.value = re.sub(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d$',
00173                                '*', timestamp.value)
00174     if element.tagName in ('testsuites', 'testsuite', 'testcase'):
00175       time = element.getAttributeNode('time')
00176       time.value = re.sub(r'^\d+(\.\d+)?$', '*', time.value)
00177       type_param = element.getAttributeNode('type_param')
00178       if type_param and type_param.value:
00179         type_param.value = '*'
00180     elif element.tagName == 'failure':
00181       source_line_pat = r'^.*[/\\](.*:)\d+\n'
00182       # Replaces the source line information with a normalized form.
00183       message = element.getAttributeNode('message')
00184       message.value = re.sub(source_line_pat, '\\1*\n', message.value)
00185       for child in element.childNodes:
00186         if child.nodeType == Node.CDATA_SECTION_NODE:
00187           # Replaces the source line information with a normalized form.
00188           cdata = re.sub(source_line_pat, '\\1*\n', child.nodeValue)
00189           # Removes the actual stack trace.
00190           child.nodeValue = re.sub(r'\nStack trace:\n(.|\n)*',
00191                                    '', cdata)
00192     for child in element.childNodes:
00193       if child.nodeType == Node.ELEMENT_NODE:
00194         self.NormalizeXml(child)


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:04