Blame googletest/test/gtest_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 Google C++ Testing Framework."""
Packit bd1cd8
Packit bd1cd8
__author__ = 'wan@google.com (Zhanyong Wan)'
Packit bd1cd8
Packit bd1cd8
import atexit
Packit bd1cd8
import os
Packit bd1cd8
import shutil
Packit bd1cd8
import sys
Packit bd1cd8
import tempfile
Packit bd1cd8
import unittest
Packit bd1cd8
_test_module = unittest
Packit bd1cd8
Packit bd1cd8
# Suppresses the 'Import not at the top of the file' lint complaint.
Packit bd1cd8
# pylint: disable-msg=C6204
Packit bd1cd8
try:
Packit bd1cd8
  import subprocess
Packit bd1cd8
  _SUBPROCESS_MODULE_AVAILABLE = True
Packit bd1cd8
except:
Packit bd1cd8
  import popen2
Packit bd1cd8
  _SUBPROCESS_MODULE_AVAILABLE = False
Packit bd1cd8
# pylint: enable-msg=C6204
Packit bd1cd8
Packit bd1cd8
GTEST_OUTPUT_VAR_NAME = 'GTEST_OUTPUT'
Packit bd1cd8
Packit bd1cd8
IS_WINDOWS = os.name == 'nt'
Packit bd1cd8
IS_CYGWIN = os.name == 'posix' and 'CYGWIN' in os.uname()[0]
Packit bd1cd8
Packit bd1cd8
# The environment variable for specifying the path to the premature-exit file.
Packit bd1cd8
PREMATURE_EXIT_FILE_ENV_VAR = 'TEST_PREMATURE_EXIT_FILE'
Packit bd1cd8
Packit bd1cd8
environ = os.environ.copy()
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def SetEnvVar(env_var, value):
Packit bd1cd8
  """Sets/unsets an environment variable to a given value."""
Packit bd1cd8
Packit bd1cd8
  if value is not None:
Packit bd1cd8
    environ[env_var] = value
Packit bd1cd8
  elif env_var in environ:
Packit bd1cd8
    del environ[env_var]
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
# Here we expose a class from a particular module, depending on the
Packit bd1cd8
# environment. The comment suppresses the 'Invalid variable name' lint
Packit bd1cd8
# complaint.
Packit bd1cd8
TestCase = _test_module.TestCase  # pylint: disable-msg=C6409
Packit bd1cd8
Packit bd1cd8
# Initially maps a flag to its default value. After
Packit bd1cd8
# _ParseAndStripGTestFlags() is called, maps a flag to its actual value.
Packit bd1cd8
_flag_map = {'source_dir': os.path.dirname(sys.argv[0]),
Packit bd1cd8
             'build_dir': os.path.dirname(sys.argv[0])}
Packit bd1cd8
_gtest_flags_are_parsed = False
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def _ParseAndStripGTestFlags(argv):
Packit bd1cd8
  """Parses and strips Google Test flags from argv.  This is idempotent."""
Packit bd1cd8
Packit bd1cd8
  # Suppresses the lint complaint about a global variable since we need it
Packit bd1cd8
  # here to maintain module-wide state.
Packit bd1cd8
  global _gtest_flags_are_parsed  # pylint: disable-msg=W0603
Packit bd1cd8
  if _gtest_flags_are_parsed:
Packit bd1cd8
    return
Packit bd1cd8
Packit bd1cd8
  _gtest_flags_are_parsed = True
Packit bd1cd8
  for flag in _flag_map:
Packit bd1cd8
    # The environment variable overrides the default value.
Packit bd1cd8
    if flag.upper() in os.environ:
Packit bd1cd8
      _flag_map[flag] = os.environ[flag.upper()]
Packit bd1cd8
Packit bd1cd8
    # The command line flag overrides the environment variable.
Packit bd1cd8
    i = 1  # Skips the program name.
Packit bd1cd8
    while i < len(argv):
Packit bd1cd8
      prefix = '--' + flag + '='
Packit bd1cd8
      if argv[i].startswith(prefix):
Packit bd1cd8
        _flag_map[flag] = argv[i][len(prefix):]
Packit bd1cd8
        del argv[i]
Packit bd1cd8
        break
Packit bd1cd8
      else:
Packit bd1cd8
        # We don't increment i in case we just found a --gtest_* flag
Packit bd1cd8
        # and removed it from argv.
Packit bd1cd8
        i += 1
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def GetFlag(flag):
Packit bd1cd8
  """Returns the value of the given flag."""
Packit bd1cd8
Packit bd1cd8
  # In case GetFlag() is called before Main(), we always call
Packit bd1cd8
  # _ParseAndStripGTestFlags() here to make sure the --gtest_* flags
Packit bd1cd8
  # are parsed.
Packit bd1cd8
  _ParseAndStripGTestFlags(sys.argv)
Packit bd1cd8
Packit bd1cd8
  return _flag_map[flag]
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def GetSourceDir():
Packit bd1cd8
  """Returns the absolute path of the directory where the .py files are."""
Packit bd1cd8
Packit bd1cd8
  return os.path.abspath(GetFlag('source_dir'))
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def GetBuildDir():
Packit bd1cd8
  """Returns the absolute path of the directory where the test binaries are."""
Packit bd1cd8
Packit bd1cd8
  return os.path.abspath(GetFlag('build_dir'))
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
_temp_dir = None
Packit bd1cd8
Packit bd1cd8
def _RemoveTempDir():
Packit bd1cd8
  if _temp_dir:
Packit bd1cd8
    shutil.rmtree(_temp_dir, ignore_errors=True)
Packit bd1cd8
Packit bd1cd8
atexit.register(_RemoveTempDir)
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def GetTempDir():
Packit bd1cd8
  """Returns a directory for temporary files."""
Packit bd1cd8
Packit bd1cd8
  global _temp_dir
Packit bd1cd8
  if not _temp_dir:
Packit bd1cd8
    _temp_dir = tempfile.mkdtemp()
Packit bd1cd8
  return _temp_dir
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def GetTestExecutablePath(executable_name, build_dir=None):
Packit bd1cd8
  """Returns the absolute path of the test binary given its name.
Packit bd1cd8
Packit bd1cd8
  The function will print a message and abort the program if the resulting file
Packit bd1cd8
  doesn't exist.
Packit bd1cd8
Packit bd1cd8
  Args:
Packit bd1cd8
    executable_name: name of the test binary that the test script runs.
Packit bd1cd8
    build_dir:       directory where to look for executables, by default
Packit bd1cd8
                     the result of GetBuildDir().
Packit bd1cd8
Packit bd1cd8
  Returns:
Packit bd1cd8
    The absolute path of the test binary.
Packit bd1cd8
  """
Packit bd1cd8
Packit bd1cd8
  path = os.path.abspath(os.path.join(build_dir or GetBuildDir(),
Packit bd1cd8
                                      executable_name))
Packit bd1cd8
  if (IS_WINDOWS or IS_CYGWIN) and not path.endswith('.exe'):
Packit bd1cd8
    path += '.exe'
Packit bd1cd8
Packit bd1cd8
  if not os.path.exists(path):
Packit bd1cd8
    message = (
Packit bd1cd8
        'Unable to find the test binary "%s". Please make sure to provide\n'
Packit bd1cd8
        'a path to the binary via the --build_dir flag or the BUILD_DIR\n'
Packit bd1cd8
        'environment variable.' % path)
Packit bd1cd8
    sys.stdout.write(message)
Packit bd1cd8
    sys.exit(1)
Packit bd1cd8
Packit bd1cd8
  return path
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def GetExitStatus(exit_code):
Packit bd1cd8
  """Returns the argument to exit(), or -1 if exit() wasn't called.
Packit bd1cd8
Packit bd1cd8
  Args:
Packit bd1cd8
    exit_code: the result value of os.system(command).
Packit bd1cd8
  """
Packit bd1cd8
Packit bd1cd8
  if os.name == 'nt':
Packit bd1cd8
    # On Windows, os.WEXITSTATUS() doesn't work and os.system() returns
Packit bd1cd8
    # the argument to exit() directly.
Packit bd1cd8
    return exit_code
Packit bd1cd8
  else:
Packit bd1cd8
    # On Unix, os.WEXITSTATUS() must be used to extract the exit status
Packit bd1cd8
    # from the result of os.system().
Packit bd1cd8
    if os.WIFEXITED(exit_code):
Packit bd1cd8
      return os.WEXITSTATUS(exit_code)
Packit bd1cd8
    else:
Packit bd1cd8
      return -1
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
class Subprocess:
Packit bd1cd8
  def __init__(self, command, working_dir=None, capture_stderr=True, env=None):
Packit bd1cd8
    """Changes into a specified directory, if provided, and executes a command.
Packit bd1cd8
Packit bd1cd8
    Restores the old directory afterwards.
Packit bd1cd8
Packit bd1cd8
    Args:
Packit bd1cd8
      command:        The command to run, in the form of sys.argv.
Packit bd1cd8
      working_dir:    The directory to change into.
Packit bd1cd8
      capture_stderr: Determines whether to capture stderr in the output member
Packit bd1cd8
                      or to discard it.
Packit bd1cd8
      env:            Dictionary with environment to pass to the subprocess.
Packit bd1cd8
Packit bd1cd8
    Returns:
Packit bd1cd8
      An object that represents outcome of the executed process. It has the
Packit bd1cd8
      following attributes:
Packit bd1cd8
        terminated_by_signal   True iff the child process has been terminated
Packit bd1cd8
                               by a signal.
Packit bd1cd8
        signal                 Sygnal that terminated the child process.
Packit bd1cd8
        exited                 True iff the child process exited normally.
Packit bd1cd8
        exit_code              The code with which the child process exited.
Packit bd1cd8
        output                 Child process's stdout and stderr output
Packit bd1cd8
                               combined in a string.
Packit bd1cd8
    """
Packit bd1cd8
Packit bd1cd8
    # The subprocess module is the preferrable way of running programs
Packit bd1cd8
    # since it is available and behaves consistently on all platforms,
Packit bd1cd8
    # including Windows. But it is only available starting in python 2.4.
Packit bd1cd8
    # In earlier python versions, we revert to the popen2 module, which is
Packit bd1cd8
    # available in python 2.0 and later but doesn't provide required
Packit bd1cd8
    # functionality (Popen4) under Windows. This allows us to support Mac
Packit bd1cd8
    # OS X 10.4 Tiger, which has python 2.3 installed.
Packit bd1cd8
    if _SUBPROCESS_MODULE_AVAILABLE:
Packit bd1cd8
      if capture_stderr:
Packit bd1cd8
        stderr = subprocess.STDOUT
Packit bd1cd8
      else:
Packit bd1cd8
        stderr = subprocess.PIPE
Packit bd1cd8
Packit bd1cd8
      p = subprocess.Popen(command,
Packit bd1cd8
                           stdout=subprocess.PIPE, stderr=stderr,
Packit bd1cd8
                           cwd=working_dir, universal_newlines=True, env=env)
Packit bd1cd8
      # communicate returns a tuple with the file obect for the child's
Packit bd1cd8
      # output.
Packit bd1cd8
      self.output = p.communicate()[0]
Packit bd1cd8
      self._return_code = p.returncode
Packit bd1cd8
    else:
Packit bd1cd8
      old_dir = os.getcwd()
Packit bd1cd8
Packit bd1cd8
      def _ReplaceEnvDict(dest, src):
Packit bd1cd8
        # Changes made by os.environ.clear are not inheritable by child
Packit bd1cd8
        # processes until Python 2.6. To produce inheritable changes we have
Packit bd1cd8
        # to delete environment items with the del statement.
Packit bd1cd8
        for key in dest.keys():
Packit bd1cd8
          del dest[key]
Packit bd1cd8
        dest.update(src)
Packit bd1cd8
Packit bd1cd8
      # When 'env' is not None, backup the environment variables and replace
Packit bd1cd8
      # them with the passed 'env'. When 'env' is None, we simply use the
Packit bd1cd8
      # current 'os.environ' for compatibility with the subprocess.Popen
Packit bd1cd8
      # semantics used above.
Packit bd1cd8
      if env is not None:
Packit bd1cd8
        old_environ = os.environ.copy()
Packit bd1cd8
        _ReplaceEnvDict(os.environ, env)
Packit bd1cd8
Packit bd1cd8
      try:
Packit bd1cd8
        if working_dir is not None:
Packit bd1cd8
          os.chdir(working_dir)
Packit bd1cd8
        if capture_stderr:
Packit bd1cd8
          p = popen2.Popen4(command)
Packit bd1cd8
        else:
Packit bd1cd8
          p = popen2.Popen3(command)
Packit bd1cd8
        p.tochild.close()
Packit bd1cd8
        self.output = p.fromchild.read()
Packit bd1cd8
        ret_code = p.wait()
Packit bd1cd8
      finally:
Packit bd1cd8
        os.chdir(old_dir)
Packit bd1cd8
Packit bd1cd8
        # Restore the old environment variables
Packit bd1cd8
        # if they were replaced.
Packit bd1cd8
        if env is not None:
Packit bd1cd8
          _ReplaceEnvDict(os.environ, old_environ)
Packit bd1cd8
Packit bd1cd8
      # Converts ret_code to match the semantics of
Packit bd1cd8
      # subprocess.Popen.returncode.
Packit bd1cd8
      if os.WIFSIGNALED(ret_code):
Packit bd1cd8
        self._return_code = -os.WTERMSIG(ret_code)
Packit bd1cd8
      else:  # os.WIFEXITED(ret_code) should return True here.
Packit bd1cd8
        self._return_code = os.WEXITSTATUS(ret_code)
Packit bd1cd8
Packit bd1cd8
    if self._return_code < 0:
Packit bd1cd8
      self.terminated_by_signal = True
Packit bd1cd8
      self.exited = False
Packit bd1cd8
      self.signal = -self._return_code
Packit bd1cd8
    else:
Packit bd1cd8
      self.terminated_by_signal = False
Packit bd1cd8
      self.exited = True
Packit bd1cd8
      self.exit_code = self._return_code
Packit bd1cd8
Packit bd1cd8
Packit bd1cd8
def Main():
Packit bd1cd8
  """Runs the unit test."""
Packit bd1cd8
Packit bd1cd8
  # We must call _ParseAndStripGTestFlags() before calling
Packit bd1cd8
  # unittest.main().  Otherwise the latter will be confused by the
Packit bd1cd8
  # --gtest_* flags.
Packit bd1cd8
  _ParseAndStripGTestFlags(sys.argv)
Packit bd1cd8
  # The tested binaries should not be writing XML output files unless the
Packit bd1cd8
  # script explicitly instructs them to.
Packit bd1cd8
  # TODO(vladl@google.com): Move this into Subprocess when we implement
Packit bd1cd8
  # passing environment into it as a parameter.
Packit bd1cd8
  if GTEST_OUTPUT_VAR_NAME in os.environ:
Packit bd1cd8
    del os.environ[GTEST_OUTPUT_VAR_NAME]
Packit bd1cd8
Packit bd1cd8
  _test_module.main()