| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # | |
| 3 # Copyright 2006, Google Inc. | |
| 4 # All rights reserved. | |
| 5 # | |
| 6 # Redistribution and use in source and binary forms, with or without | |
| 7 # modification, are permitted provided that the following conditions are | |
| 8 # met: | |
| 9 # | |
| 10 # * Redistributions of source code must retain the above copyright | |
| 11 # notice, this list of conditions and the following disclaimer. | |
| 12 # * Redistributions in binary form must reproduce the above | |
| 13 # copyright notice, this list of conditions and the following disclaimer | |
| 14 # in the documentation and/or other materials provided with the | |
| 15 # distribution. | |
| 16 # * Neither the name of Google Inc. nor the names of its | |
| 17 # contributors may be used to endorse or promote products derived from | |
| 18 # this software without specific prior written permission. | |
| 19 # | |
| 20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 23 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 24 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 25 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 26 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 27 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 28 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 31 | |
| 32 """Unit test utilities for gtest_xml_output""" | |
| 33 | |
| 34 __author__ = 'eefacm@gmail.com (Sean Mcafee)' | |
| 35 | |
| 36 import re | |
| 37 from xml.dom import minidom, Node | |
| 38 | |
| 39 import gtest_test_utils | |
| 40 | |
| 41 | |
| 42 GTEST_OUTPUT_FLAG = "--gtest_output" | |
| 43 GTEST_DEFAULT_OUTPUT_FILE = "test_detail.xml" | |
| 44 | |
| 45 class GTestXMLTestCase(gtest_test_utils.TestCase): | |
| 46 """ | |
| 47 Base class for tests of Google Test's XML output functionality. | |
| 48 """ | |
| 49 | |
| 50 | |
| 51 def AssertEquivalentNodes(self, expected_node, actual_node): | |
| 52 """ | |
| 53 Asserts that actual_node (a DOM node object) is equivalent to | |
| 54 expected_node (another DOM node object), in that either both of | |
| 55 them are CDATA nodes and have the same value, or both are DOM | |
| 56 elements and actual_node meets all of the following conditions: | |
| 57 | |
| 58 * It has the same tag name as expected_node. | |
| 59 * It has the same set of attributes as expected_node, each with | |
| 60 the same value as the corresponding attribute of expected_node. | |
| 61 Exceptions are any attribute named "time", which needs only be | |
| 62 convertible to a floating-point number and any attribute named | |
| 63 "type_param" which only has to be non-empty. | |
| 64 * It has an equivalent set of child nodes (including elements and | |
| 65 CDATA sections) as expected_node. Note that we ignore the | |
| 66 order of the children as they are not guaranteed to be in any | |
| 67 particular order. | |
| 68 """ | |
| 69 | |
| 70 if expected_node.nodeType == Node.CDATA_SECTION_NODE: | |
| 71 self.assertEquals(Node.CDATA_SECTION_NODE, actual_node.nodeType) | |
| 72 self.assertEquals(expected_node.nodeValue, actual_node.nodeValue) | |
| 73 return | |
| 74 | |
| 75 self.assertEquals(Node.ELEMENT_NODE, actual_node.nodeType) | |
| 76 self.assertEquals(Node.ELEMENT_NODE, expected_node.nodeType) | |
| 77 self.assertEquals(expected_node.tagName, actual_node.tagName) | |
| 78 | |
| 79 expected_attributes = expected_node.attributes | |
| 80 actual_attributes = actual_node .attributes | |
| 81 self.assertEquals( | |
| 82 expected_attributes.length, actual_attributes.length, | |
| 83 "attribute numbers differ in element " + actual_node.tagName) | |
| 84 for i in range(expected_attributes.length): | |
| 85 expected_attr = expected_attributes.item(i) | |
| 86 actual_attr = actual_attributes.get(expected_attr.name) | |
| 87 self.assert_( | |
| 88 actual_attr is not None, | |
| 89 "expected attribute %s not found in element %s" % | |
| 90 (expected_attr.name, actual_node.tagName)) | |
| 91 self.assertEquals(expected_attr.value, actual_attr.value, | |
| 92 " values of attribute %s in element %s differ" % | |
| 93 (expected_attr.name, actual_node.tagName)) | |
| 94 | |
| 95 expected_children = self._GetChildren(expected_node) | |
| 96 actual_children = self._GetChildren(actual_node) | |
| 97 self.assertEquals( | |
| 98 len(expected_children), len(actual_children), | |
| 99 "number of child elements differ in element " + actual_node.tagName) | |
| 100 for child_id, child in expected_children.iteritems(): | |
| 101 self.assert_(child_id in actual_children, | |
| 102 '<%s> is not in <%s> (in element %s)' % | |
| 103 (child_id, actual_children, actual_node.tagName)) | |
| 104 self.AssertEquivalentNodes(child, actual_children[child_id]) | |
| 105 | |
| 106 identifying_attribute = { | |
| 107 "testsuites": "name", | |
| 108 "testsuite": "name", | |
| 109 "testcase": "name", | |
| 110 "failure": "message", | |
| 111 } | |
| 112 | |
| 113 def _GetChildren(self, element): | |
| 114 """ | |
| 115 Fetches all of the child nodes of element, a DOM Element object. | |
| 116 Returns them as the values of a dictionary keyed by the IDs of the | |
| 117 children. For <testsuites>, <testsuite> and <testcase> elements, the ID | |
| 118 is the value of their "name" attribute; for <failure> elements, it is | |
| 119 the value of the "message" attribute; CDATA sections and non-whitespace | |
| 120 text nodes are concatenated into a single CDATA section with ID | |
| 121 "detail". An exception is raised if any element other than the above | |
| 122 four is encountered, if two child elements with the same identifying | |
| 123 attributes are encountered, or if any other type of node is encountered. | |
| 124 """ | |
| 125 | |
| 126 children = {} | |
| 127 for child in element.childNodes: | |
| 128 if child.nodeType == Node.ELEMENT_NODE: | |
| 129 self.assert_(child.tagName in self.identifying_attribute, | |
| 130 "Encountered unknown element <%s>" % child.tagName) | |
| 131 childID = child.getAttribute(self.identifying_attribute[child.tagName]) | |
| 132 self.assert_(childID not in children) | |
| 133 children[childID] = child | |
| 134 elif child.nodeType in [Node.TEXT_NODE, Node.CDATA_SECTION_NODE]: | |
| 135 if "detail" not in children: | |
| 136 if (child.nodeType == Node.CDATA_SECTION_NODE or | |
| 137 not child.nodeValue.isspace()): | |
| 138 children["detail"] = child.ownerDocument.createCDATASection( | |
| 139 child.nodeValue) | |
| 140 else: | |
| 141 children["detail"].nodeValue += child.nodeValue | |
| 142 else: | |
| 143 self.fail("Encountered unexpected node type %d" % child.nodeType) | |
| 144 return children | |
| 145 | |
| 146 def NormalizeXml(self, element): | |
| 147 """ | |
| 148 Normalizes Google Test's XML output to eliminate references to transient | |
| 149 information that may change from run to run. | |
| 150 | |
| 151 * The "time" attribute of <testsuites>, <testsuite> and <testcase> | |
| 152 elements is replaced with a single asterisk, if it contains | |
| 153 only digit characters. | |
| 154 * The "type_param" attribute of <testcase> elements is replaced with a | |
| 155 single asterisk (if it sn non-empty) as it is the type name returned | |
| 156 by the compiler and is platform dependent. | |
| 157 * The line number reported in the first line of the "message" | |
| 158 attribute of <failure> elements is replaced with a single asterisk. | |
| 159 * The directory names in file paths are removed. | |
| 160 * The stack traces are removed. | |
| 161 """ | |
| 162 | |
| 163 if element.tagName in ("testsuites", "testsuite", "testcase"): | |
| 164 time = element.getAttributeNode("time") | |
| 165 time.value = re.sub(r"^\d+(\.\d+)?$", "*", time.value) | |
| 166 type_param = element.getAttributeNode("type_param") | |
| 167 if type_param and type_param.value: | |
| 168 type_param.value = "*" | |
| 169 elif element.tagName == "failure": | |
| 170 for child in element.childNodes: | |
| 171 if child.nodeType == Node.CDATA_SECTION_NODE: | |
| 172 # Removes the source line number. | |
| 173 cdata = re.sub(r"^.*[/\\](.*:)\d+\n", "\\1*\n", child.nodeValue) | |
| 174 # Removes the actual stack trace. | |
| 175 child.nodeValue = re.sub(r"\nStack trace:\n(.|\n)*", | |
| 176 "", cdata) | |
| 177 for child in element.childNodes: | |
| 178 if child.nodeType == Node.ELEMENT_NODE: | |
| 179 self.NormalizeXml(child) | |
| OLD | NEW |