Changeset View
Changeset View
Standalone View
Standalone View
google/googletest/dist/googletest/test/gtest_test_utils.py
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:executable | null | * \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
# Copyright 2006, Google Inc. | |||||
# All rights reserved. | |||||
# | |||||
# Redistribution and use in source and binary forms, with or without | |||||
# modification, are permitted provided that the following conditions are | |||||
# met: | |||||
# | |||||
# * Redistributions of source code must retain the above copyright | |||||
# notice, this list of conditions and the following disclaimer. | |||||
# * Redistributions in binary form must reproduce the above | |||||
# copyright notice, this list of conditions and the following disclaimer | |||||
# in the documentation and/or other materials provided with the | |||||
# distribution. | |||||
# * Neither the name of Google Inc. nor the names of its | |||||
# contributors may be used to endorse or promote products derived from | |||||
# this software without specific prior written permission. | |||||
# | |||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
"""Unit test utilities for Google C++ Testing and Mocking Framework.""" | |||||
# Suppresses the 'Import not at the top of the file' lint complaint. | |||||
# pylint: disable-msg=C6204 | |||||
import os | |||||
import sys | |||||
IS_WINDOWS = os.name == 'nt' | |||||
IS_CYGWIN = os.name == 'posix' and 'CYGWIN' in os.uname()[0] | |||||
import atexit | |||||
import shutil | |||||
import tempfile | |||||
import unittest as _test_module | |||||
try: | |||||
import subprocess | |||||
_SUBPROCESS_MODULE_AVAILABLE = True | |||||
except: | |||||
import popen2 | |||||
_SUBPROCESS_MODULE_AVAILABLE = False | |||||
# pylint: enable-msg=C6204 | |||||
GTEST_OUTPUT_VAR_NAME = 'GTEST_OUTPUT' | |||||
# The environment variable for specifying the path to the premature-exit file. | |||||
PREMATURE_EXIT_FILE_ENV_VAR = 'TEST_PREMATURE_EXIT_FILE' | |||||
environ = os.environ.copy() | |||||
def SetEnvVar(env_var, value): | |||||
"""Sets/unsets an environment variable to a given value.""" | |||||
if value is not None: | |||||
environ[env_var] = value | |||||
elif env_var in environ: | |||||
del environ[env_var] | |||||
# Here we expose a class from a particular module, depending on the | |||||
# environment. The comment suppresses the 'Invalid variable name' lint | |||||
# complaint. | |||||
TestCase = _test_module.TestCase # pylint: disable=C6409 | |||||
# Initially maps a flag to its default value. After | |||||
# _ParseAndStripGTestFlags() is called, maps a flag to its actual value. | |||||
_flag_map = {'source_dir': os.path.dirname(sys.argv[0]), | |||||
'build_dir': os.path.dirname(sys.argv[0])} | |||||
_gtest_flags_are_parsed = False | |||||
def _ParseAndStripGTestFlags(argv): | |||||
"""Parses and strips Google Test flags from argv. This is idempotent.""" | |||||
# Suppresses the lint complaint about a global variable since we need it | |||||
# here to maintain module-wide state. | |||||
global _gtest_flags_are_parsed # pylint: disable=W0603 | |||||
if _gtest_flags_are_parsed: | |||||
return | |||||
_gtest_flags_are_parsed = True | |||||
for flag in _flag_map: | |||||
# The environment variable overrides the default value. | |||||
if flag.upper() in os.environ: | |||||
_flag_map[flag] = os.environ[flag.upper()] | |||||
# The command line flag overrides the environment variable. | |||||
i = 1 # Skips the program name. | |||||
while i < len(argv): | |||||
prefix = '--' + flag + '=' | |||||
if argv[i].startswith(prefix): | |||||
_flag_map[flag] = argv[i][len(prefix):] | |||||
del argv[i] | |||||
break | |||||
else: | |||||
# We don't increment i in case we just found a --gtest_* flag | |||||
# and removed it from argv. | |||||
i += 1 | |||||
def GetFlag(flag): | |||||
"""Returns the value of the given flag.""" | |||||
# In case GetFlag() is called before Main(), we always call | |||||
# _ParseAndStripGTestFlags() here to make sure the --gtest_* flags | |||||
# are parsed. | |||||
_ParseAndStripGTestFlags(sys.argv) | |||||
return _flag_map[flag] | |||||
def GetSourceDir(): | |||||
"""Returns the absolute path of the directory where the .py files are.""" | |||||
return os.path.abspath(GetFlag('source_dir')) | |||||
def GetBuildDir(): | |||||
"""Returns the absolute path of the directory where the test binaries are.""" | |||||
return os.path.abspath(GetFlag('build_dir')) | |||||
_temp_dir = None | |||||
def _RemoveTempDir(): | |||||
if _temp_dir: | |||||
shutil.rmtree(_temp_dir, ignore_errors=True) | |||||
atexit.register(_RemoveTempDir) | |||||
def GetTempDir(): | |||||
global _temp_dir | |||||
if not _temp_dir: | |||||
_temp_dir = tempfile.mkdtemp() | |||||
return _temp_dir | |||||
def GetTestExecutablePath(executable_name, build_dir=None): | |||||
"""Returns the absolute path of the test binary given its name. | |||||
The function will print a message and abort the program if the resulting file | |||||
doesn't exist. | |||||
Args: | |||||
executable_name: name of the test binary that the test script runs. | |||||
build_dir: directory where to look for executables, by default | |||||
the result of GetBuildDir(). | |||||
Returns: | |||||
The absolute path of the test binary. | |||||
""" | |||||
path = os.path.abspath(os.path.join(build_dir or GetBuildDir(), | |||||
executable_name)) | |||||
if (IS_WINDOWS or IS_CYGWIN) and not path.endswith('.exe'): | |||||
path += '.exe' | |||||
if not os.path.exists(path): | |||||
message = ( | |||||
'Unable to find the test binary "%s". Please make sure to provide\n' | |||||
'a path to the binary via the --build_dir flag or the BUILD_DIR\n' | |||||
'environment variable.' % path) | |||||
print >> sys.stderr, message | |||||
sys.exit(1) | |||||
return path | |||||
def GetExitStatus(exit_code): | |||||
"""Returns the argument to exit(), or -1 if exit() wasn't called. | |||||
Args: | |||||
exit_code: the result value of os.system(command). | |||||
""" | |||||
if os.name == 'nt': | |||||
# On Windows, os.WEXITSTATUS() doesn't work and os.system() returns | |||||
# the argument to exit() directly. | |||||
return exit_code | |||||
else: | |||||
# On Unix, os.WEXITSTATUS() must be used to extract the exit status | |||||
# from the result of os.system(). | |||||
if os.WIFEXITED(exit_code): | |||||
return os.WEXITSTATUS(exit_code) | |||||
else: | |||||
return -1 | |||||
class Subprocess: | |||||
def __init__(self, command, working_dir=None, capture_stderr=True, env=None): | |||||
"""Changes into a specified directory, if provided, and executes a command. | |||||
Restores the old directory afterwards. | |||||
Args: | |||||
command: The command to run, in the form of sys.argv. | |||||
working_dir: The directory to change into. | |||||
capture_stderr: Determines whether to capture stderr in the output member | |||||
or to discard it. | |||||
env: Dictionary with environment to pass to the subprocess. | |||||
Returns: | |||||
An object that represents outcome of the executed process. It has the | |||||
following attributes: | |||||
terminated_by_signal True iff the child process has been terminated | |||||
by a signal. | |||||
signal Sygnal that terminated the child process. | |||||
exited True iff the child process exited normally. | |||||
exit_code The code with which the child process exited. | |||||
output Child process's stdout and stderr output | |||||
combined in a string. | |||||
""" | |||||
# The subprocess module is the preferrable way of running programs | |||||
# since it is available and behaves consistently on all platforms, | |||||
# including Windows. But it is only available starting in python 2.4. | |||||
# In earlier python versions, we revert to the popen2 module, which is | |||||
# available in python 2.0 and later but doesn't provide required | |||||
# functionality (Popen4) under Windows. This allows us to support Mac | |||||
# OS X 10.4 Tiger, which has python 2.3 installed. | |||||
if _SUBPROCESS_MODULE_AVAILABLE: | |||||
if capture_stderr: | |||||
stderr = subprocess.STDOUT | |||||
else: | |||||
stderr = subprocess.PIPE | |||||
p = subprocess.Popen(command, | |||||
stdout=subprocess.PIPE, stderr=stderr, | |||||
cwd=working_dir, universal_newlines=True, env=env) | |||||
# communicate returns a tuple with the file object for the child's | |||||
# output. | |||||
self.output = p.communicate()[0] | |||||
self._return_code = p.returncode | |||||
else: | |||||
old_dir = os.getcwd() | |||||
def _ReplaceEnvDict(dest, src): | |||||
# Changes made by os.environ.clear are not inheritable by child | |||||
# processes until Python 2.6. To produce inheritable changes we have | |||||
# to delete environment items with the del statement. | |||||
for key in dest.keys(): | |||||
del dest[key] | |||||
dest.update(src) | |||||
# When 'env' is not None, backup the environment variables and replace | |||||
# them with the passed 'env'. When 'env' is None, we simply use the | |||||
# current 'os.environ' for compatibility with the subprocess.Popen | |||||
# semantics used above. | |||||
if env is not None: | |||||
old_environ = os.environ.copy() | |||||
_ReplaceEnvDict(os.environ, env) | |||||
try: | |||||
if working_dir is not None: | |||||
os.chdir(working_dir) | |||||
if capture_stderr: | |||||
p = popen2.Popen4(command) | |||||
else: | |||||
p = popen2.Popen3(command) | |||||
p.tochild.close() | |||||
self.output = p.fromchild.read() | |||||
ret_code = p.wait() | |||||
finally: | |||||
os.chdir(old_dir) | |||||
# Restore the old environment variables | |||||
# if they were replaced. | |||||
if env is not None: | |||||
_ReplaceEnvDict(os.environ, old_environ) | |||||
# Converts ret_code to match the semantics of | |||||
# subprocess.Popen.returncode. | |||||
if os.WIFSIGNALED(ret_code): | |||||
self._return_code = -os.WTERMSIG(ret_code) | |||||
else: # os.WIFEXITED(ret_code) should return True here. | |||||
self._return_code = os.WEXITSTATUS(ret_code) | |||||
if self._return_code < 0: | |||||
self.terminated_by_signal = True | |||||
self.exited = False | |||||
self.signal = -self._return_code | |||||
else: | |||||
self.terminated_by_signal = False | |||||
self.exited = True | |||||
self.exit_code = self._return_code | |||||
def Main(): | |||||
"""Runs the unit test.""" | |||||
# We must call _ParseAndStripGTestFlags() before calling | |||||
# unittest.main(). Otherwise the latter will be confused by the | |||||
# --gtest_* flags. | |||||
_ParseAndStripGTestFlags(sys.argv) | |||||
# The tested binaries should not be writing XML output files unless the | |||||
# script explicitly instructs them to. | |||||
# FIXME: Move this into Subprocess when we implement | |||||
# passing environment into it as a parameter. | |||||
if GTEST_OUTPUT_VAR_NAME in os.environ: | |||||
del os.environ[GTEST_OUTPUT_VAR_NAME] | |||||
_test_module.main() |