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