3
# Copyright 2006, Google Inc.
6
# Redistribution and use in source and binary forms, with or without
7
# modification, are permitted provided that the following conditions are
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
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.
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.
32
"""Unit test utilities for gtest_xml_output"""
34
__author__ = 'eefacm@gmail.com (Sean Mcafee)'
37
from xml.dom import minidom, Node
39
import gtest_test_utils
42
GTEST_OUTPUT_FLAG = "--gtest_output"
43
GTEST_DEFAULT_OUTPUT_FILE = "test_detail.xml"
45
class GTestXMLTestCase(gtest_test_utils.TestCase):
47
Base class for tests of Google Test's XML output functionality.
51
def AssertEquivalentNodes(self, expected_node, actual_node):
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:
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
An exception is any attribute named "time", which needs only be
62
convertible to a floating-point number.
63
* It has an equivalent set of child nodes (including elements and
64
CDATA sections) as expected_node. Note that we ignore the
65
order of the children as they are not guaranteed to be in any
69
if expected_node.nodeType == Node.CDATA_SECTION_NODE:
70
self.assertEquals(Node.CDATA_SECTION_NODE, actual_node.nodeType)
71
self.assertEquals(expected_node.nodeValue, actual_node.nodeValue)
74
self.assertEquals(Node.ELEMENT_NODE, actual_node.nodeType)
75
self.assertEquals(Node.ELEMENT_NODE, expected_node.nodeType)
76
self.assertEquals(expected_node.tagName, actual_node.tagName)
78
expected_attributes = expected_node.attributes
79
actual_attributes = actual_node .attributes
81
expected_attributes.length, actual_attributes.length,
82
"attribute numbers differ in element " + actual_node.tagName)
83
for i in range(expected_attributes.length):
84
expected_attr = expected_attributes.item(i)
85
actual_attr = actual_attributes.get(expected_attr.name)
87
actual_attr is not None,
88
"expected attribute %s not found in element %s" %
89
(expected_attr.name, actual_node.tagName))
90
self.assertEquals(expected_attr.value, actual_attr.value,
91
" values of attribute %s in element %s differ" %
92
(expected_attr.name, actual_node.tagName))
94
expected_children = self._GetChildren(expected_node)
95
actual_children = self._GetChildren(actual_node)
97
len(expected_children), len(actual_children),
98
"number of child elements differ in element " + actual_node.tagName)
99
for child_id, child in expected_children.iteritems():
100
self.assert_(child_id in actual_children,
101
'<%s> is not in <%s> (in element %s)' %
102
(child_id, actual_children, actual_node.tagName))
103
self.AssertEquivalentNodes(child, actual_children[child_id])
105
identifying_attribute = {
106
"testsuites": "name",
109
"failure": "message",
112
def _GetChildren(self, element):
114
Fetches all of the child nodes of element, a DOM Element object.
115
Returns them as the values of a dictionary keyed by the IDs of the
116
children. For <testsuites>, <testsuite> and <testcase> elements, the ID
117
is the value of their "name" attribute; for <failure> elements, it is
118
the value of the "message" attribute; CDATA sections and non-whitespace
119
text nodes are concatenated into a single CDATA section with ID
120
"detail". An exception is raised if any element other than the above
121
four is encountered, if two child elements with the same identifying
122
attributes are encountered, or if any other type of node is encountered.
126
for child in element.childNodes:
127
if child.nodeType == Node.ELEMENT_NODE:
128
self.assert_(child.tagName in self.identifying_attribute,
129
"Encountered unknown element <%s>" % child.tagName)
130
childID = child.getAttribute(self.identifying_attribute[child.tagName])
131
self.assert_(childID not in children)
132
children[childID] = child
133
elif child.nodeType in [Node.TEXT_NODE, Node.CDATA_SECTION_NODE]:
134
if "detail" not in children:
135
if (child.nodeType == Node.CDATA_SECTION_NODE or
136
not child.nodeValue.isspace()):
137
children["detail"] = child.ownerDocument.createCDATASection(
140
children["detail"].nodeValue += child.nodeValue
142
self.fail("Encountered unexpected node type %d" % child.nodeType)
145
def NormalizeXml(self, element):
147
Normalizes Google Test's XML output to eliminate references to transient
148
information that may change from run to run.
150
* The "time" attribute of <testsuites>, <testsuite> and <testcase>
151
elements is replaced with a single asterisk, if it contains
152
only digit characters.
153
* The line number reported in the first line of the "message"
154
attribute of <failure> elements is replaced with a single asterisk.
155
* The directory names in file paths are removed.
156
* The stack traces are removed.
159
if element.tagName in ("testsuites", "testsuite", "testcase"):
160
time = element.getAttributeNode("time")
161
time.value = re.sub(r"^\d+(\.\d+)?$", "*", time.value)
162
elif element.tagName == "failure":
163
for child in element.childNodes:
164
if child.nodeType == Node.CDATA_SECTION_NODE:
165
# Removes the source line number.
166
cdata = re.sub(r"^.*[/\\](.*:)\d+\n", "\\1*\n", child.nodeValue)
167
# Removes the actual stack trace.
168
child.nodeValue = re.sub(r"\nStack trace:\n(.|\n)*",
170
for child in element.childNodes:
171
if child.nodeType == Node.ELEMENT_NODE:
172
self.NormalizeXml(child)