11be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania#!/usr/bin/env python
21be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania#
31be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# Copyright 2006, Google Inc.
41be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# All rights reserved.
51be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania#
61be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# Redistribution and use in source and binary forms, with or without
71be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# modification, are permitted provided that the following conditions are
81be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# met:
91be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania#
101be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania#     * Redistributions of source code must retain the above copyright
111be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# notice, this list of conditions and the following disclaimer.
121be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania#     * Redistributions in binary form must reproduce the above
131be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# copyright notice, this list of conditions and the following disclaimer
141be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# in the documentation and/or other materials provided with the
151be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# distribution.
161be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania#     * Neither the name of Google Inc. nor the names of its
171be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# contributors may be used to endorse or promote products derived from
181be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# this software without specific prior written permission.
191be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania#
201be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
211be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
221be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
231be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
241be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
251be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
261be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
271be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
281be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
291be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
301be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
311be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
321be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania"""Unit test utilities for gtest_xml_output"""
331be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
341be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania__author__ = 'eefacm@gmail.com (Sean Mcafee)'
351be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
361be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport re
371be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniafrom xml.dom import minidom, Node
381be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
3941d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabotimport gtest_test_utils
4041d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot
4141d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot
421be2c9def7187e4e643c00a31dd9986395795d7dNicolas CataniaGTEST_OUTPUT_FLAG         = "--gtest_output"
431be2c9def7187e4e643c00a31dd9986395795d7dNicolas CataniaGTEST_DEFAULT_OUTPUT_FILE = "test_detail.xml"
441be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
4541d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabotclass GTestXMLTestCase(gtest_test_utils.TestCase):
461be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania  """
471be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania  Base class for tests of Google Test's XML output functionality.
481be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania  """
491be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
501be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
511be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania  def AssertEquivalentNodes(self, expected_node, actual_node):
521be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    """
531be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    Asserts that actual_node (a DOM node object) is equivalent to
541be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    expected_node (another DOM node object), in that either both of
551be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    them are CDATA nodes and have the same value, or both are DOM
561be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    elements and actual_node meets all of the following conditions:
571be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
581be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    *  It has the same tag name as expected_node.
591be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    *  It has the same set of attributes as expected_node, each with
601be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania       the same value as the corresponding attribute of expected_node.
6141d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot       Exceptions are any attribute named "time", which needs only be
6241d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot       convertible to a floating-point number and any attribute named
6341d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot       "type_param" which only has to be non-empty.
641be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    *  It has an equivalent set of child nodes (including elements and
651be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania       CDATA sections) as expected_node.  Note that we ignore the
661be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania       order of the children as they are not guaranteed to be in any
671be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania       particular order.
681be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    """
691be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
701be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    if expected_node.nodeType == Node.CDATA_SECTION_NODE:
711be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      self.assertEquals(Node.CDATA_SECTION_NODE, actual_node.nodeType)
721be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      self.assertEquals(expected_node.nodeValue, actual_node.nodeValue)
731be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      return
741be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
751be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    self.assertEquals(Node.ELEMENT_NODE, actual_node.nodeType)
761be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    self.assertEquals(Node.ELEMENT_NODE, expected_node.nodeType)
771be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    self.assertEquals(expected_node.tagName, actual_node.tagName)
781be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
791be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    expected_attributes = expected_node.attributes
801be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    actual_attributes   = actual_node  .attributes
8141d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    self.assertEquals(
8241d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot        expected_attributes.length, actual_attributes.length,
8341d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot        "attribute numbers differ in element " + actual_node.tagName)
841be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    for i in range(expected_attributes.length):
851be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      expected_attr = expected_attributes.item(i)
861be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      actual_attr   = actual_attributes.get(expected_attr.name)
8741d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot      self.assert_(
8841d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot          actual_attr is not None,
8941d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot          "expected attribute %s not found in element %s" %
9041d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot          (expected_attr.name, actual_node.tagName))
9141d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot      self.assertEquals(expected_attr.value, actual_attr.value,
9241d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot                        " values of attribute %s in element %s differ" %
9341d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot                        (expected_attr.name, actual_node.tagName))
941be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
951be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    expected_children = self._GetChildren(expected_node)
961be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    actual_children = self._GetChildren(actual_node)
9741d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    self.assertEquals(
9841d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot        len(expected_children), len(actual_children),
9941d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot        "number of child elements differ in element " + actual_node.tagName)
1001be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    for child_id, child in expected_children.iteritems():
1011be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      self.assert_(child_id in actual_children,
10241d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot                   '<%s> is not in <%s> (in element %s)' %
10341d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot                   (child_id, actual_children, actual_node.tagName))
1041be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      self.AssertEquivalentNodes(child, actual_children[child_id])
1051be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1061be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania  identifying_attribute = {
10741d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    "testsuites": "name",
1081be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    "testsuite": "name",
1091be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    "testcase":  "name",
1101be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    "failure":   "message",
1111be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    }
1121be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1131be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania  def _GetChildren(self, element):
1141be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    """
1151be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    Fetches all of the child nodes of element, a DOM Element object.
1161be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    Returns them as the values of a dictionary keyed by the IDs of the
11741d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    children.  For <testsuites>, <testsuite> and <testcase> elements, the ID
11841d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    is the value of their "name" attribute; for <failure> elements, it is
11941d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    the value of the "message" attribute; CDATA sections and non-whitespace
12041d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    text nodes are concatenated into a single CDATA section with ID
12141d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    "detail".  An exception is raised if any element other than the above
12241d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    four is encountered, if two child elements with the same identifying
12341d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    attributes are encountered, or if any other type of node is encountered.
1241be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    """
1251be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1261be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    children = {}
1271be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    for child in element.childNodes:
1281be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      if child.nodeType == Node.ELEMENT_NODE:
1291be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        self.assert_(child.tagName in self.identifying_attribute,
1301be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                     "Encountered unknown element <%s>" % child.tagName)
1311be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        childID = child.getAttribute(self.identifying_attribute[child.tagName])
1321be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        self.assert_(childID not in children)
1331be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        children[childID] = child
13441d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot      elif child.nodeType in [Node.TEXT_NODE, Node.CDATA_SECTION_NODE]:
13541d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot        if "detail" not in children:
13641d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot          if (child.nodeType == Node.CDATA_SECTION_NODE or
13741d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot              not child.nodeValue.isspace()):
13841d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot            children["detail"] = child.ownerDocument.createCDATASection(
13941d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot                child.nodeValue)
14041d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot        else:
14141d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot          children["detail"].nodeValue += child.nodeValue
1421be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      else:
1431be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        self.fail("Encountered unexpected node type %d" % child.nodeType)
1441be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    return children
1451be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1461be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania  def NormalizeXml(self, element):
1471be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    """
1481be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    Normalizes Google Test's XML output to eliminate references to transient
1491be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    information that may change from run to run.
1501be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
15141d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    *  The "time" attribute of <testsuites>, <testsuite> and <testcase>
15241d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot       elements is replaced with a single asterisk, if it contains
15341d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot       only digit characters.
15441d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    *  The "type_param" attribute of <testcase> elements is replaced with a
15541d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot       single asterisk (if it sn non-empty) as it is the type name returned
15641d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot       by the compiler and is platform dependent.
1571be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    *  The line number reported in the first line of the "message"
1581be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania       attribute of <failure> elements is replaced with a single asterisk.
1591be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    *  The directory names in file paths are removed.
1601be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    *  The stack traces are removed.
1611be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    """
1621be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
16341d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot    if element.tagName in ("testsuites", "testsuite", "testcase"):
1641be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      time = element.getAttributeNode("time")
1651be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      time.value = re.sub(r"^\d+(\.\d+)?$", "*", time.value)
16641d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot      type_param = element.getAttributeNode("type_param")
16741d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot      if type_param and type_param.value:
16841d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot        type_param.value = "*"
1691be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    elif element.tagName == "failure":
1701be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      for child in element.childNodes:
1711be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        if child.nodeType == Node.CDATA_SECTION_NODE:
1721be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania          # Removes the source line number.
17341d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot          cdata = re.sub(r"^.*[/\\](.*:)\d+\n", "\\1*\n", child.nodeValue)
1741be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania          # Removes the actual stack trace.
1751be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania          child.nodeValue = re.sub(r"\nStack trace:\n(.|\n)*",
1761be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                                   "", cdata)
1771be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    for child in element.childNodes:
1781be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania      if child.nodeType == Node.ELEMENT_NODE:
1791be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        self.NormalizeXml(child)
180