Blame googletest/test/gtest_output_test.py

Packit bd1cd8
#!/usr/bin/env python
Packit bd1cd8
#
Packit bd1cd8
# Copyright 2008, 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
"""Tests the text output of Google C++ Testing Framework.
Packit bd1cd8
Packit bd1cd8
SYNOPSIS
Packit bd1cd8
       gtest_output_test.py --build_dir=BUILD/DIR --gengolden
Packit bd1cd8
         # where BUILD/DIR contains the built gtest_output_test_ file.
Packit bd1cd8
       gtest_output_test.py --gengolden
Packit bd1cd8
       gtest_output_test.py
Packit bd1cd8
"""
Packit bd1cd8
Packit bd1cd8
__author__ = 'wan@google.com (Zhanyong Wan)'
Packit bd1cd8
Packit bd1cd8
import difflib
Packit bd1cd8
import os
Packit bd1cd8
import re
Packit bd1cd8
import sys
Packit bd1cd8
import gtest_test_utils
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
# The flag for generating the golden file
Packit bd1cd8
GENGOLDEN_FLAG = '--gengolden'
Packit bd1cd8
CATCH_EXCEPTIONS_ENV_VAR_NAME = 'GTEST_CATCH_EXCEPTIONS'
Packit bd1cd8
Packit bd1cd8
IS_WINDOWS = os.name == 'nt'
Packit bd1cd8
Packit bd1cd8
# TODO(vladl@google.com): remove the _lin suffix.
Packit bd1cd8
GOLDEN_NAME = 'gtest_output_test_golden_lin.txt'
Packit bd1cd8
Packit bd1cd8
PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath('gtest_output_test_')
Packit bd1cd8
Packit bd1cd8
# At least one command we exercise must not have the
Packit bd1cd8
# 'internal_skip_environment_and_ad_hoc_tests' argument.
Packit bd1cd8
COMMAND_LIST_TESTS = ({}, [PROGRAM_PATH, '--gtest_list_tests'])
Packit bd1cd8
COMMAND_WITH_COLOR = ({}, [PROGRAM_PATH, '--gtest_color=yes'])
Packit bd1cd8
COMMAND_WITH_TIME = ({}, [PROGRAM_PATH,
Packit bd1cd8
                          '--gtest_print_time',
Packit bd1cd8
                          'internal_skip_environment_and_ad_hoc_tests',
Packit bd1cd8
                          '--gtest_filter=FatalFailureTest.*:LoggingTest.*'])
Packit bd1cd8
COMMAND_WITH_DISABLED = (
Packit bd1cd8
    {}, [PROGRAM_PATH,
Packit bd1cd8
         '--gtest_also_run_disabled_tests',
Packit bd1cd8
         'internal_skip_environment_and_ad_hoc_tests',
Packit bd1cd8
         '--gtest_filter=*DISABLED_*'])
Packit bd1cd8
COMMAND_WITH_SHARDING = (
Packit bd1cd8
    {'GTEST_SHARD_INDEX': '1', 'GTEST_TOTAL_SHARDS': '2'},
Packit bd1cd8
    [PROGRAM_PATH,
Packit bd1cd8
     'internal_skip_environment_and_ad_hoc_tests',
Packit bd1cd8
     '--gtest_filter=PassingTest.*'])
Packit bd1cd8
Packit bd1cd8
GOLDEN_PATH = os.path.join(gtest_test_utils.GetSourceDir(), GOLDEN_NAME)
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def ToUnixLineEnding(s):
Packit bd1cd8
  """Changes all Windows/Mac line endings in s to UNIX line endings."""
Packit bd1cd8
Packit bd1cd8
  return s.replace('\r\n', '\n').replace('\r', '\n')
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def RemoveLocations(test_output):
Packit bd1cd8
  """Removes all file location info from a Google Test program's output.
Packit bd1cd8
Packit bd1cd8
  Args:
Packit bd1cd8
       test_output:  the output of a Google Test program.
Packit bd1cd8
Packit bd1cd8
  Returns:
Packit bd1cd8
       output with all file location info (in the form of
Packit bd1cd8
       'DIRECTORY/FILE_NAME:LINE_NUMBER: 'or
Packit bd1cd8
       'DIRECTORY\\FILE_NAME(LINE_NUMBER): ') replaced by
Packit bd1cd8
       'FILE_NAME:#: '.
Packit bd1cd8
  """
Packit bd1cd8
Packit bd1cd8
  return re.sub(r'.*[/\\](.+)(\:\d+|\(\d+\))\: ', r'\1:#: ', test_output)
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def RemoveStackTraceDetails(output):
Packit bd1cd8
  """Removes all stack traces from a Google Test program's output."""
Packit bd1cd8
Packit bd1cd8
  # *? means "find the shortest string that matches".
Packit bd1cd8
  return re.sub(r'Stack trace:(.|\n)*?\n\n',
Packit bd1cd8
                'Stack trace: (omitted)\n\n', output)
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def RemoveStackTraces(output):
Packit bd1cd8
  """Removes all traces of stack traces from a Google Test program's output."""
Packit bd1cd8
Packit bd1cd8
  # *? means "find the shortest string that matches".
Packit bd1cd8
  return re.sub(r'Stack trace:(.|\n)*?\n\n', '', output)
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def RemoveTime(output):
Packit bd1cd8
  """Removes all time information from a Google Test program's output."""
Packit bd1cd8
Packit bd1cd8
  return re.sub(r'\(\d+ ms', '(? ms', output)
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def RemoveTypeInfoDetails(test_output):
Packit bd1cd8
  """Removes compiler-specific type info from Google Test program's output.
Packit bd1cd8
Packit bd1cd8
  Args:
Packit bd1cd8
       test_output:  the output of a Google Test program.
Packit bd1cd8
Packit bd1cd8
  Returns:
Packit bd1cd8
       output with type information normalized to canonical form.
Packit bd1cd8
  """
Packit bd1cd8
Packit bd1cd8
  # some compilers output the name of type 'unsigned int' as 'unsigned'
Packit bd1cd8
  return re.sub(r'unsigned int', 'unsigned', test_output)
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def NormalizeToCurrentPlatform(test_output):
Packit bd1cd8
  """Normalizes platform specific output details for easier comparison."""
Packit bd1cd8
Packit bd1cd8
  if IS_WINDOWS:
Packit bd1cd8
    # Removes the color information that is not present on Windows.
Packit bd1cd8
    test_output = re.sub('\x1b\\[(0;3\d)?m', '', test_output)
Packit bd1cd8
    # Changes failure message headers into the Windows format.
Packit bd1cd8
    test_output = re.sub(r': Failure\n', r': error: ', test_output)
Packit bd1cd8
    # Changes file(line_number) to file:line_number.
Packit bd1cd8
    test_output = re.sub(r'((\w|\.)+)\((\d+)\):', r'\1:\3:', test_output)
Packit bd1cd8
Packit bd1cd8
  return test_output
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def RemoveTestCounts(output):
Packit bd1cd8
  """Removes test counts from a Google Test program's output."""
Packit bd1cd8
Packit bd1cd8
  output = re.sub(r'\d+ tests?, listed below',
Packit bd1cd8
                  '? tests, listed below', output)
Packit bd1cd8
  output = re.sub(r'\d+ FAILED TESTS',
Packit bd1cd8
                  '? FAILED TESTS', output)
Packit bd1cd8
  output = re.sub(r'\d+ tests? from \d+ test cases?',
Packit bd1cd8
                  '? tests from ? test cases', output)
Packit bd1cd8
  output = re.sub(r'\d+ tests? from ([a-zA-Z_])',
Packit bd1cd8
                  r'? tests from \1', output)
Packit bd1cd8
  return re.sub(r'\d+ tests?\.', '? tests.', output)
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def RemoveMatchingTests(test_output, pattern):
Packit bd1cd8
  """Removes output of specified tests from a Google Test program's output.
Packit bd1cd8
Packit bd1cd8
  This function strips not only the beginning and the end of a test but also
Packit bd1cd8
  all output in between.
Packit bd1cd8
Packit bd1cd8
  Args:
Packit bd1cd8
    test_output:       A string containing the test output.
Packit bd1cd8
    pattern:           A regex string that matches names of test cases or
Packit bd1cd8
                       tests to remove.
Packit bd1cd8
Packit bd1cd8
  Returns:
Packit bd1cd8
    Contents of test_output with tests whose names match pattern removed.
Packit bd1cd8
  """
Packit bd1cd8
Packit bd1cd8
  test_output = re.sub(
Packit bd1cd8
      r'.*\[ RUN      \] .*%s(.|\n)*?\[(  FAILED  |       OK )\] .*%s.*\n' % (
Packit bd1cd8
          pattern, pattern),
Packit bd1cd8
      '',
Packit bd1cd8
      test_output)
Packit bd1cd8
  return re.sub(r'.*%s.*\n' % pattern, '', test_output)
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def NormalizeOutput(output):
Packit bd1cd8
  """Normalizes output (the output of gtest_output_test_.exe)."""
Packit bd1cd8
Packit bd1cd8
  output = ToUnixLineEnding(output)
Packit bd1cd8
  output = RemoveLocations(output)
Packit bd1cd8
  output = RemoveStackTraceDetails(output)
Packit bd1cd8
  output = RemoveTime(output)
Packit bd1cd8
  return output
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def GetShellCommandOutput(env_cmd):
Packit bd1cd8
  """Runs a command in a sub-process, and returns its output in a string.
Packit bd1cd8
Packit bd1cd8
  Args:
Packit bd1cd8
    env_cmd: The shell command. A 2-tuple where element 0 is a dict of extra
Packit bd1cd8
             environment variables to set, and element 1 is a string with
Packit bd1cd8
             the command and any flags.
Packit bd1cd8
Packit bd1cd8
  Returns:
Packit bd1cd8
    A string with the command's combined standard and diagnostic output.
Packit bd1cd8
  """
Packit bd1cd8
Packit bd1cd8
  # Spawns cmd in a sub-process, and gets its standard I/O file objects.
Packit bd1cd8
  # Set and save the environment properly.
Packit bd1cd8
  environ = os.environ.copy()
Packit bd1cd8
  environ.update(env_cmd[0])
Packit bd1cd8
  p = gtest_test_utils.Subprocess(env_cmd[1], env=environ)
Packit bd1cd8
Packit bd1cd8
  return p.output
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def GetCommandOutput(env_cmd):
Packit bd1cd8
  """Runs a command and returns its output with all file location
Packit bd1cd8
  info stripped off.
Packit bd1cd8
Packit bd1cd8
  Args:
Packit bd1cd8
    env_cmd:  The shell command. A 2-tuple where element 0 is a dict of extra
Packit bd1cd8
              environment variables to set, and element 1 is a string with
Packit bd1cd8
              the command and any flags.
Packit bd1cd8
  """
Packit bd1cd8
Packit bd1cd8
  # Disables exception pop-ups on Windows.
Packit bd1cd8
  environ, cmdline = env_cmd
Packit bd1cd8
  environ = dict(environ)  # Ensures we are modifying a copy.
Packit bd1cd8
  environ[CATCH_EXCEPTIONS_ENV_VAR_NAME] = '1'
Packit bd1cd8
  return NormalizeOutput(GetShellCommandOutput((environ, cmdline)))
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def GetOutputOfAllCommands():
Packit bd1cd8
  """Returns concatenated output from several representative commands."""
Packit bd1cd8
Packit bd1cd8
  return (GetCommandOutput(COMMAND_WITH_COLOR) +
Packit bd1cd8
          GetCommandOutput(COMMAND_WITH_TIME) +
Packit bd1cd8
          GetCommandOutput(COMMAND_WITH_DISABLED) +
Packit bd1cd8
          GetCommandOutput(COMMAND_WITH_SHARDING))
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
test_list = GetShellCommandOutput(COMMAND_LIST_TESTS)
Packit bd1cd8
SUPPORTS_DEATH_TESTS = 'DeathTest' in test_list
Packit bd1cd8
SUPPORTS_TYPED_TESTS = 'TypedTest' in test_list
Packit bd1cd8
SUPPORTS_THREADS = 'ExpectFailureWithThreadsTest' in test_list
Packit bd1cd8
SUPPORTS_STACK_TRACES = False
Packit bd1cd8
Packit bd1cd8
CAN_GENERATE_GOLDEN_FILE = (SUPPORTS_DEATH_TESTS and
Packit bd1cd8
                            SUPPORTS_TYPED_TESTS and
Packit bd1cd8
                            SUPPORTS_THREADS and
Packit bd1cd8
                            not IS_WINDOWS)
Packit bd1cd8
Packit bd1cd8
class GTestOutputTest(gtest_test_utils.TestCase):
Packit bd1cd8
  def RemoveUnsupportedTests(self, test_output):
Packit bd1cd8
    if not SUPPORTS_DEATH_TESTS:
Packit bd1cd8
      test_output = RemoveMatchingTests(test_output, 'DeathTest')
Packit bd1cd8
    if not SUPPORTS_TYPED_TESTS:
Packit bd1cd8
      test_output = RemoveMatchingTests(test_output, 'TypedTest')
Packit bd1cd8
      test_output = RemoveMatchingTests(test_output, 'TypedDeathTest')
Packit bd1cd8
      test_output = RemoveMatchingTests(test_output, 'TypeParamDeathTest')
Packit bd1cd8
    if not SUPPORTS_THREADS:
Packit bd1cd8
      test_output = RemoveMatchingTests(test_output,
Packit bd1cd8
                                        'ExpectFailureWithThreadsTest')
Packit bd1cd8
      test_output = RemoveMatchingTests(test_output,
Packit bd1cd8
                                        'ScopedFakeTestPartResultReporterTest')
Packit bd1cd8
      test_output = RemoveMatchingTests(test_output,
Packit bd1cd8
                                        'WorksConcurrently')
Packit bd1cd8
    if not SUPPORTS_STACK_TRACES:
Packit bd1cd8
      test_output = RemoveStackTraces(test_output)
Packit bd1cd8
Packit bd1cd8
    return test_output
Packit bd1cd8
Packit bd1cd8
  def testOutput(self):
Packit bd1cd8
    output = GetOutputOfAllCommands()
Packit bd1cd8
Packit bd1cd8
    golden_file = open(GOLDEN_PATH, 'r')
Packit bd1cd8
    # A mis-configured source control system can cause \r appear in EOL
Packit bd1cd8
    # sequences when we read the golden file irrespective of an operating
Packit bd1cd8
    # system used. Therefore, we need to strip those \r's from newlines
Packit bd1cd8
    # unconditionally.
Packit bd1cd8
    golden = ToUnixLineEnding(golden_file.read())
Packit bd1cd8
    golden_file.close()
Packit bd1cd8
Packit bd1cd8
    # We want the test to pass regardless of certain features being
Packit bd1cd8
    # supported or not.
Packit bd1cd8
Packit bd1cd8
    # We still have to remove type name specifics in all cases.
Packit bd1cd8
    normalized_actual = RemoveTypeInfoDetails(output)
Packit bd1cd8
    normalized_golden = RemoveTypeInfoDetails(golden)
Packit bd1cd8
Packit bd1cd8
    if CAN_GENERATE_GOLDEN_FILE:
Packit bd1cd8
      self.assertEqual(normalized_golden, normalized_actual,
Packit bd1cd8
                       '\n'.join(difflib.unified_diff(
Packit bd1cd8
                           normalized_golden.split('\n'),
Packit bd1cd8
                           normalized_actual.split('\n'),
Packit bd1cd8
                           'golden', 'actual')))
Packit bd1cd8
    else:
Packit bd1cd8
      normalized_actual = NormalizeToCurrentPlatform(
Packit bd1cd8
          RemoveTestCounts(normalized_actual))
Packit bd1cd8
      normalized_golden = NormalizeToCurrentPlatform(
Packit bd1cd8
          RemoveTestCounts(self.RemoveUnsupportedTests(normalized_golden)))
Packit bd1cd8
Packit bd1cd8
      # This code is very handy when debugging golden file differences:
Packit bd1cd8
      if os.getenv('DEBUG_GTEST_OUTPUT_TEST'):
Packit bd1cd8
        open(os.path.join(
Packit bd1cd8
            gtest_test_utils.GetSourceDir(),
Packit bd1cd8
            '_gtest_output_test_normalized_actual.txt'), 'wb').write(
Packit bd1cd8
                normalized_actual)
Packit bd1cd8
        open(os.path.join(
Packit bd1cd8
            gtest_test_utils.GetSourceDir(),
Packit bd1cd8
            '_gtest_output_test_normalized_golden.txt'), 'wb').write(
Packit bd1cd8
                normalized_golden)
Packit bd1cd8
Packit bd1cd8
      self.assertEqual(normalized_golden, normalized_actual)
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
if __name__ == '__main__':
Packit bd1cd8
  if sys.argv[1:] == [GENGOLDEN_FLAG]:
Packit bd1cd8
    if CAN_GENERATE_GOLDEN_FILE:
Packit bd1cd8
      output = GetOutputOfAllCommands()
Packit bd1cd8
      golden_file = open(GOLDEN_PATH, 'wb')
Packit bd1cd8
      golden_file.write(output)
Packit bd1cd8
      golden_file.close()
Packit bd1cd8
    else:
Packit bd1cd8
      message = (
Packit bd1cd8
          """Unable to write a golden file when compiled in an environment
Packit bd1cd8
that does not support all the required features (death tests, typed tests,
Packit bd1cd8
and multiple threads).  Please generate the golden file using a binary built
Packit bd1cd8
with those features enabled.""")
Packit bd1cd8
Packit bd1cd8
      sys.stderr.write(message)
Packit bd1cd8
      sys.exit(1)
Packit bd1cd8
  else:
Packit bd1cd8
    gtest_test_utils.Main()