Blame googletest/test/gtest_xml_test_utils.py

Packit bd1cd8
#!/usr/bin/env python
Packit bd1cd8
#
Packit bd1cd8
# Copyright 2006, Google Inc.
Packit bd1cd8
# All rights reserved.
Packit bd1cd8
#
Packit bd1cd8
# Redistribution and use in source and binary forms, with or without
Packit bd1cd8
# modification, are permitted provided that the following conditions are
Packit bd1cd8
# met:
Packit bd1cd8
#
Packit bd1cd8
#     * Redistributions of source code must retain the above copyright
Packit bd1cd8
# notice, this list of conditions and the following disclaimer.
Packit bd1cd8
#     * Redistributions in binary form must reproduce the above
Packit bd1cd8
# copyright notice, this list of conditions and the following disclaimer
Packit bd1cd8
# in the documentation and/or other materials provided with the
Packit bd1cd8
# distribution.
Packit bd1cd8
#     * Neither the name of Google Inc. nor the names of its
Packit bd1cd8
# contributors may be used to endorse or promote products derived from
Packit bd1cd8
# this software without specific prior written permission.
Packit bd1cd8
#
Packit bd1cd8
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit bd1cd8
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit bd1cd8
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
Packit bd1cd8
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
Packit bd1cd8
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
Packit bd1cd8
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
Packit bd1cd8
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit bd1cd8
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit bd1cd8
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit bd1cd8
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
Packit bd1cd8
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit bd1cd8
Packit bd1cd8
"""Unit test utilities for gtest_xml_output"""
Packit bd1cd8
Packit bd1cd8
__author__ = 'eefacm@gmail.com (Sean Mcafee)'
Packit bd1cd8
Packit bd1cd8
import re
Packit bd1cd8
from xml.dom import minidom, Node
Packit bd1cd8
Packit bd1cd8
import gtest_test_utils
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
GTEST_OUTPUT_FLAG         = '--gtest_output'
Packit bd1cd8
GTEST_DEFAULT_OUTPUT_FILE = 'test_detail.xml'
Packit bd1cd8
Packit bd1cd8
class GTestXMLTestCase(gtest_test_utils.TestCase):
Packit bd1cd8
  """
Packit bd1cd8
  Base class for tests of Google Test's XML output functionality.
Packit bd1cd8
  """
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
  def AssertEquivalentNodes(self, expected_node, actual_node):
Packit bd1cd8
    """
Packit bd1cd8
    Asserts that actual_node (a DOM node object) is equivalent to
Packit bd1cd8
    expected_node (another DOM node object), in that either both of
Packit bd1cd8
    them are CDATA nodes and have the same value, or both are DOM
Packit bd1cd8
    elements and actual_node meets all of the following conditions:
Packit bd1cd8
Packit bd1cd8
    *  It has the same tag name as expected_node.
Packit bd1cd8
    *  It has the same set of attributes as expected_node, each with
Packit bd1cd8
       the same value as the corresponding attribute of expected_node.
Packit bd1cd8
       Exceptions are any attribute named "time", which needs only be
Packit bd1cd8
       convertible to a floating-point number and any attribute named
Packit bd1cd8
       "type_param" which only has to be non-empty.
Packit bd1cd8
    *  It has an equivalent set of child nodes (including elements and
Packit bd1cd8
       CDATA sections) as expected_node.  Note that we ignore the
Packit bd1cd8
       order of the children as they are not guaranteed to be in any
Packit bd1cd8
       particular order.
Packit bd1cd8
    """
Packit bd1cd8
Packit bd1cd8
    if expected_node.nodeType == Node.CDATA_SECTION_NODE:
Packit bd1cd8
      self.assertEquals(Node.CDATA_SECTION_NODE, actual_node.nodeType)
Packit bd1cd8
      self.assertEquals(expected_node.nodeValue, actual_node.nodeValue)
Packit bd1cd8
      return
Packit bd1cd8
Packit bd1cd8
    self.assertEquals(Node.ELEMENT_NODE, actual_node.nodeType)
Packit bd1cd8
    self.assertEquals(Node.ELEMENT_NODE, expected_node.nodeType)
Packit bd1cd8
    self.assertEquals(expected_node.tagName, actual_node.tagName)
Packit bd1cd8
Packit bd1cd8
    expected_attributes = expected_node.attributes
Packit bd1cd8
    actual_attributes   = actual_node  .attributes
Packit bd1cd8
    self.assertEquals(
Packit bd1cd8
        expected_attributes.length, actual_attributes.length,
Packit bd1cd8
        'attribute numbers differ in element %s:\nExpected: %r\nActual: %r' % (
Packit bd1cd8
            actual_node.tagName, expected_attributes.keys(),
Packit bd1cd8
            actual_attributes.keys()))
Packit bd1cd8
    for i in range(expected_attributes.length):
Packit bd1cd8
      expected_attr = expected_attributes.item(i)
Packit bd1cd8
      actual_attr   = actual_attributes.get(expected_attr.name)
Packit bd1cd8
      self.assert_(
Packit bd1cd8
          actual_attr is not None,
Packit bd1cd8
          'expected attribute %s not found in element %s' %
Packit bd1cd8
          (expected_attr.name, actual_node.tagName))
Packit bd1cd8
      self.assertEquals(
Packit bd1cd8
          expected_attr.value, actual_attr.value,
Packit bd1cd8
          ' values of attribute %s in element %s differ: %s vs %s' %
Packit bd1cd8
          (expected_attr.name, actual_node.tagName,
Packit bd1cd8
           expected_attr.value, actual_attr.value))
Packit bd1cd8
Packit bd1cd8
    expected_children = self._GetChildren(expected_node)
Packit bd1cd8
    actual_children = self._GetChildren(actual_node)
Packit bd1cd8
    self.assertEquals(
Packit bd1cd8
        len(expected_children), len(actual_children),
Packit bd1cd8
        'number of child elements differ in element ' + actual_node.tagName)
Packit bd1cd8
    for child_id, child in expected_children.items():
Packit bd1cd8
      self.assert_(child_id in actual_children,
Packit bd1cd8
                   '<%s> is not in <%s> (in element %s)' %
Packit bd1cd8
                   (child_id, actual_children, actual_node.tagName))
Packit bd1cd8
      self.AssertEquivalentNodes(child, actual_children[child_id])
Packit bd1cd8
Packit bd1cd8
  identifying_attribute = {
Packit bd1cd8
    'testsuites': 'name',
Packit bd1cd8
    'testsuite': 'name',
Packit bd1cd8
    'testcase':  'name',
Packit bd1cd8
    'failure':   'message',
Packit bd1cd8
    }
Packit bd1cd8
Packit bd1cd8
  def _GetChildren(self, element):
Packit bd1cd8
    """
Packit bd1cd8
    Fetches all of the child nodes of element, a DOM Element object.
Packit bd1cd8
    Returns them as the values of a dictionary keyed by the IDs of the
Packit bd1cd8
    children.  For <testsuites>, <testsuite> and <testcase> elements, the ID
Packit bd1cd8
    is the value of their "name" attribute; for <failure> elements, it is
Packit bd1cd8
    the value of the "message" attribute; CDATA sections and non-whitespace
Packit bd1cd8
    text nodes are concatenated into a single CDATA section with ID
Packit bd1cd8
    "detail".  An exception is raised if any element other than the above
Packit bd1cd8
    four is encountered, if two child elements with the same identifying
Packit bd1cd8
    attributes are encountered, or if any other type of node is encountered.
Packit bd1cd8
    """
Packit bd1cd8
Packit bd1cd8
    children = {}
Packit bd1cd8
    for child in element.childNodes:
Packit bd1cd8
      if child.nodeType == Node.ELEMENT_NODE:
Packit bd1cd8
        self.assert_(child.tagName in self.identifying_attribute,
Packit bd1cd8
                     'Encountered unknown element <%s>' % child.tagName)
Packit bd1cd8
        childID = child.getAttribute(self.identifying_attribute[child.tagName])
Packit bd1cd8
        self.assert_(childID not in children)
Packit bd1cd8
        children[childID] = child
Packit bd1cd8
      elif child.nodeType in [Node.TEXT_NODE, Node.CDATA_SECTION_NODE]:
Packit bd1cd8
        if 'detail' not in children:
Packit bd1cd8
          if (child.nodeType == Node.CDATA_SECTION_NODE or
Packit bd1cd8
              not child.nodeValue.isspace()):
Packit bd1cd8
            children['detail'] = child.ownerDocument.createCDATASection(
Packit bd1cd8
                child.nodeValue)
Packit bd1cd8
        else:
Packit bd1cd8
          children['detail'].nodeValue += child.nodeValue
Packit bd1cd8
      else:
Packit bd1cd8
        self.fail('Encountered unexpected node type %d' % child.nodeType)
Packit bd1cd8
    return children
Packit bd1cd8
Packit bd1cd8
  def NormalizeXml(self, element):
Packit bd1cd8
    """
Packit bd1cd8
    Normalizes Google Test's XML output to eliminate references to transient
Packit bd1cd8
    information that may change from run to run.
Packit bd1cd8
Packit bd1cd8
    *  The "time" attribute of <testsuites>, <testsuite> and <testcase>
Packit bd1cd8
       elements is replaced with a single asterisk, if it contains
Packit bd1cd8
       only digit characters.
Packit bd1cd8
    *  The "timestamp" attribute of <testsuites> elements is replaced with a
Packit bd1cd8
       single asterisk, if it contains a valid ISO8601 datetime value.
Packit bd1cd8
    *  The "type_param" attribute of <testcase> elements is replaced with a
Packit bd1cd8
       single asterisk (if it sn non-empty) as it is the type name returned
Packit bd1cd8
       by the compiler and is platform dependent.
Packit bd1cd8
    *  The line info reported in the first line of the "message"
Packit bd1cd8
       attribute and CDATA section of <failure> elements is replaced with the
Packit bd1cd8
       file's basename and a single asterisk for the line number.
Packit bd1cd8
    *  The directory names in file paths are removed.
Packit bd1cd8
    *  The stack traces are removed.
Packit bd1cd8
    """
Packit bd1cd8
Packit bd1cd8
    if element.tagName == 'testsuites':
Packit bd1cd8
      timestamp = element.getAttributeNode('timestamp')
Packit bd1cd8
      timestamp.value = re.sub(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d$',
Packit bd1cd8
                               '*', timestamp.value)
Packit bd1cd8
    if element.tagName in ('testsuites', 'testsuite', 'testcase'):
Packit bd1cd8
      time = element.getAttributeNode('time')
Packit bd1cd8
      time.value = re.sub(r'^\d+(\.\d+)?$', '*', time.value)
Packit bd1cd8
      type_param = element.getAttributeNode('type_param')
Packit bd1cd8
      if type_param and type_param.value:
Packit bd1cd8
        type_param.value = '*'
Packit bd1cd8
    elif element.tagName == 'failure':
Packit bd1cd8
      source_line_pat = r'^.*[/\\](.*:)\d+\n'
Packit bd1cd8
      # Replaces the source line information with a normalized form.
Packit bd1cd8
      message = element.getAttributeNode('message')
Packit bd1cd8
      message.value = re.sub(source_line_pat, '\\1*\n', message.value)
Packit bd1cd8
      for child in element.childNodes:
Packit bd1cd8
        if child.nodeType == Node.CDATA_SECTION_NODE:
Packit bd1cd8
          # Replaces the source line information with a normalized form.
Packit bd1cd8
          cdata = re.sub(source_line_pat, '\\1*\n', child.nodeValue)
Packit bd1cd8
          # Removes the actual stack trace.
Packit bd1cd8
          child.nodeValue = re.sub(r'\nStack trace:\n(.|\n)*',
Packit bd1cd8
                                   '', cdata)
Packit bd1cd8
    for child in element.childNodes:
Packit bd1cd8
      if child.nodeType == Node.ELEMENT_NODE:
Packit bd1cd8
        self.NormalizeXml(child)