30 """Unit test utilities for gtest_xml_output"""
33 from xml.dom
import minidom, Node
34 import gtest_test_utils
36 GTEST_DEFAULT_OUTPUT_FILE =
'test_detail.xml'
40 Base class for tests of Google Test's XML output functionality.
46 Asserts that actual_node (a DOM node object) is equivalent to
47 expected_node (another DOM node object), in that either both of
48 them are CDATA nodes and have the same value, or both are DOM
49 elements and actual_node meets all of the following conditions:
51 * It has the same tag name as expected_node.
52 * It has the same set of attributes as expected_node, each with
53 the same value as the corresponding attribute of expected_node.
54 Exceptions are any attribute named "time", which needs only be
55 convertible to a floating-point number and any attribute named
56 "type_param" which only has to be non-empty.
57 * It has an equivalent set of child nodes (including elements and
58 CDATA sections) as expected_node. Note that we ignore the
59 order of the children as they are not guaranteed to be in any
63 if expected_node.nodeType == Node.CDATA_SECTION_NODE:
64 self.assertEquals(Node.CDATA_SECTION_NODE, actual_node.nodeType)
65 self.assertEquals(expected_node.nodeValue, actual_node.nodeValue)
68 self.assertEquals(Node.ELEMENT_NODE, actual_node.nodeType)
69 self.assertEquals(Node.ELEMENT_NODE, expected_node.nodeType)
70 self.assertEquals(expected_node.tagName, actual_node.tagName)
72 expected_attributes = expected_node.attributes
73 actual_attributes = actual_node .attributes
75 expected_attributes.length, actual_attributes.length,
76 'attribute numbers differ in element %s:\nExpected: %r\nActual: %r' % (
77 actual_node.tagName, expected_attributes.keys(),
78 actual_attributes.keys()))
79 for i
in range(expected_attributes.length):
80 expected_attr = expected_attributes.item(i)
81 actual_attr = actual_attributes.get(expected_attr.name)
83 actual_attr
is not None,
84 'expected attribute %s not found in element %s' %
85 (expected_attr.name, actual_node.tagName))
87 expected_attr.value, actual_attr.value,
88 ' values of attribute %s in element %s differ: %s vs %s' %
89 (expected_attr.name, actual_node.tagName,
90 expected_attr.value, actual_attr.value))
95 len(expected_children),
len(actual_children),
96 'number of child elements differ in element ' + actual_node.tagName)
97 for child_id, child
in expected_children.items():
98 self.assert_(child_id
in actual_children,
99 '<%s> is not in <%s> (in element %s)' %
100 (child_id, actual_children, actual_node.tagName))
103 identifying_attribute = {
104 'testsuites':
'name',
107 'failure':
'message',
113 Fetches all of the child nodes of element, a DOM Element object.
114 Returns them as the values of a dictionary keyed by the IDs of the
115 children. For <testsuites>, <testsuite>, <testcase>, and <property>
116 elements, the ID is the value of their "name" attribute; for <failure>
117 elements, it is the value of the "message" attribute; for <properties>
118 elements, it is the value of their parent's "name" attribute plus the
119 literal string "properties"; 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.
127 for child
in element.childNodes:
128 if child.nodeType == Node.ELEMENT_NODE:
129 if child.tagName ==
'properties':
130 self.assert_(child.parentNode
is not None,
131 'Encountered <properties> element without a parent')
132 child_id = child.parentNode.getAttribute(
'name') +
'-properties'
135 'Encountered unknown element <%s>' % child.tagName)
136 child_id = child.getAttribute(
138 self.assert_(child_id
not in children)
139 children[child_id] = child
140 elif child.nodeType
in [Node.TEXT_NODE, Node.CDATA_SECTION_NODE]:
141 if 'detail' not in children:
142 if (child.nodeType == Node.CDATA_SECTION_NODE
or
143 not child.nodeValue.isspace()):
144 children[
'detail'] = child.ownerDocument.createCDATASection(
147 children[
'detail'].nodeValue += child.nodeValue
149 self.fail(
'Encountered unexpected node type %d' % child.nodeType)
154 Normalizes Google Test's XML output to eliminate references to transient
155 information that may change from run to run.
157 * The "time" attribute of <testsuites>, <testsuite> and <testcase>
158 elements is replaced with a single asterisk, if it contains
159 only digit characters.
160 * The "timestamp" attribute of <testsuites> elements is replaced with a
161 single asterisk, if it contains a valid ISO8601 datetime value.
162 * The "type_param" attribute of <testcase> elements is replaced with a
163 single asterisk (if it sn non-empty) as it is the type name returned
164 by the compiler and is platform dependent.
165 * The line info reported in the first line of the "message"
166 attribute and CDATA section of <failure> elements is replaced with the
167 file's basename and a single asterisk for the line number.
168 * The directory names in file paths are removed.
169 * The stack traces are removed.
172 if element.tagName ==
'testsuites':
173 timestamp = element.getAttributeNode(
'timestamp')
174 timestamp.value = re.sub(
r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d$',
175 '*', timestamp.value)
176 if element.tagName
in (
'testsuites',
'testsuite',
'testcase'):
177 time = element.getAttributeNode(
'time')
178 time.value = re.sub(
r'^\d+(\.\d+)?$',
'*', time.value)
179 type_param = element.getAttributeNode(
'type_param')
180 if type_param
and type_param.value:
181 type_param.value =
'*'
182 elif element.tagName ==
'failure':
183 source_line_pat =
r'^.*[/\\](.*:)\d+\n'
185 message = element.getAttributeNode(
'message')
186 message.value = re.sub(source_line_pat,
'\\1*\n', message.value)
187 for child
in element.childNodes:
188 if child.nodeType == Node.CDATA_SECTION_NODE:
190 cdata = re.sub(source_line_pat,
'\\1*\n', child.nodeValue)
192 child.nodeValue = re.sub(
r'Stack trace:\n(.|\n)*',
193 'Stack trace:\n*', cdata)
194 for child
in element.childNodes:
195 if child.nodeType == Node.ELEMENT_NODE: