1# Copyright 2006, Google Inc. 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are 6# met: 7# 8# * Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# * Redistributions in binary form must reproduce the above 11# copyright notice, this list of conditions and the following disclaimer 12# in the documentation and/or other materials provided with the 13# distribution. 14# * Neither the name of Google Inc. nor the names of its 15# contributors may be used to endorse or promote products derived from 16# this software without specific prior written permission. 17# 18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30"""Unit test utilities for Google C++ Testing and Mocking Framework.""" 31# Suppresses the 'Import not at the top of the file' lint complaint. 32# pylint: disable=g-import-not-at-top 33 34import os 35import subprocess 36import sys 37 38IS_WINDOWS = os.name == 'nt' 39IS_CYGWIN = os.name == 'posix' and 'CYGWIN' in os.uname()[0] 40IS_OS2 = os.name == 'os2' 41 42import atexit 43import shutil 44import tempfile 45import unittest as _test_module 46# pylint: enable=g-import-not-at-top 47 48GTEST_OUTPUT_VAR_NAME = 'GTEST_OUTPUT' 49 50# The environment variable for specifying the path to the premature-exit file. 51PREMATURE_EXIT_FILE_ENV_VAR = 'TEST_PREMATURE_EXIT_FILE' 52 53environ = os.environ.copy() 54 55 56def SetEnvVar(env_var, value): 57 """Sets/unsets an environment variable to a given value.""" 58 59 if value is not None: 60 environ[env_var] = value 61 elif env_var in environ: 62 del environ[env_var] 63 64 65# Here we expose a class from a particular module, depending on the 66# environment. 67TestCase = _test_module.TestCase 68 69# Initially maps a flag to its default value. After 70# _ParseAndStripGTestFlags() is called, maps a flag to its actual value. 71_flag_map = { 72 'source_dir': os.path.dirname(sys.argv[0]), 73 'build_dir': os.path.dirname(sys.argv[0]), 74} 75_gtest_flags_are_parsed = False 76 77 78def _ParseAndStripGTestFlags(argv): 79 """Parses and strips Google Test flags from argv. This is idempotent.""" 80 81 global _gtest_flags_are_parsed 82 if _gtest_flags_are_parsed: 83 return 84 85 _gtest_flags_are_parsed = True 86 for flag in _flag_map: 87 # The environment variable overrides the default value. 88 if flag.upper() in os.environ: 89 _flag_map[flag] = os.environ[flag.upper()] 90 91 # The command line flag overrides the environment variable. 92 i = 1 # Skips the program name. 93 while i < len(argv): 94 prefix = '--' + flag + '=' 95 if argv[i].startswith(prefix): 96 _flag_map[flag] = argv[i][len(prefix) :] 97 del argv[i] 98 break 99 else: 100 # We don't increment i in case we just found a --gtest_* flag 101 # and removed it from argv. 102 i += 1 103 104 105def GetFlag(flag): 106 """Returns the value of the given flag.""" 107 108 # In case GetFlag() is called before Main(), we always call 109 # _ParseAndStripGTestFlags() here to make sure the --gtest_* flags 110 # are parsed. 111 _ParseAndStripGTestFlags(sys.argv) 112 113 return _flag_map[flag] 114 115 116def GetSourceDir(): 117 """Returns the absolute path of the directory where the .py files are.""" 118 119 return os.path.abspath(GetFlag('source_dir')) 120 121 122def GetBuildDir(): 123 """Returns the absolute path of the directory where the test binaries are.""" 124 125 return os.path.abspath(GetFlag('build_dir')) 126 127 128_temp_dir = None 129 130def _RemoveTempDir(): 131 if _temp_dir: 132 shutil.rmtree(_temp_dir, ignore_errors=True) 133 134atexit.register(_RemoveTempDir) 135 136 137def GetTempDir(): 138 global _temp_dir 139 if not _temp_dir: 140 _temp_dir = tempfile.mkdtemp() 141 return _temp_dir 142 143 144def GetTestExecutablePath(executable_name, build_dir=None): 145 """Returns the absolute path of the test binary given its name. 146 147 The function will print a message and abort the program if the resulting file 148 doesn't exist. 149 150 Args: 151 executable_name: name of the test binary that the test script runs. 152 build_dir: directory where to look for executables, by default the 153 result of GetBuildDir(). 154 155 Returns: 156 The absolute path of the test binary. 157 """ 158 159 path = os.path.abspath( 160 os.path.join(build_dir or GetBuildDir(), executable_name) 161 ) 162 if (IS_WINDOWS or IS_CYGWIN or IS_OS2) and not path.endswith('.exe'): 163 path += '.exe' 164 165 if not os.path.exists(path): 166 message = ( 167 'Unable to find the test binary "%s". Please make sure to provide\n' 168 'a path to the binary via the --build_dir flag or the BUILD_DIR\n' 169 'environment variable.' % path 170 ) 171 print(message, file=sys.stderr) 172 sys.exit(1) 173 174 return path 175 176 177def GetExitStatus(exit_code): 178 """Returns the argument to exit(), or -1 if exit() wasn't called. 179 180 Args: 181 exit_code: the result value of os.system(command). 182 """ 183 184 if os.name == 'nt': 185 # On Windows, os.WEXITSTATUS() doesn't work and os.system() returns 186 # the argument to exit() directly. 187 return exit_code 188 else: 189 # On Unix, os.WEXITSTATUS() must be used to extract the exit status 190 # from the result of os.system(). 191 if os.WIFEXITED(exit_code): 192 return os.WEXITSTATUS(exit_code) 193 else: 194 return -1 195 196 197class Subprocess: 198 199 def __init__(self, command, working_dir=None, capture_stderr=True, env=None): 200 """Changes into a specified directory, if provided, and executes a command. 201 202 Restores the old directory afterwards. 203 204 Args: 205 command: The command to run, in the form of sys.argv. 206 working_dir: The directory to change into. 207 capture_stderr: Determines whether to capture stderr in the output member 208 or to discard it. 209 env: Dictionary with environment to pass to the subprocess. 210 211 Returns: 212 An object that represents outcome of the executed process. It has the 213 following attributes: 214 terminated_by_signal True if and only if the child process has been 215 terminated by a signal. 216 exited True if and only if the child process exited 217 normally. 218 exit_code The code with which the child process exited. 219 output Child process's stdout and stderr output 220 combined in a string. 221 """ 222 223 if capture_stderr: 224 stderr = subprocess.STDOUT 225 else: 226 stderr = subprocess.PIPE 227 228 p = subprocess.Popen( 229 command, 230 stdout=subprocess.PIPE, 231 stderr=stderr, 232 cwd=working_dir, 233 universal_newlines=True, 234 env=env, 235 ) 236 # communicate returns a tuple with the file object for the child's 237 # output. 238 self.output = p.communicate()[0] 239 self._return_code = p.returncode 240 241 if bool(self._return_code & 0x80000000): 242 self.terminated_by_signal = True 243 self.exited = False 244 else: 245 self.terminated_by_signal = False 246 self.exited = True 247 self.exit_code = self._return_code 248 249 250def Main(): 251 """Runs the unit test.""" 252 253 # We must call _ParseAndStripGTestFlags() before calling 254 # unittest.main(). Otherwise the latter will be confused by the 255 # --gtest_* flags. 256 _ParseAndStripGTestFlags(sys.argv) 257 # The tested binaries should not be writing XML output files unless the 258 # script explicitly instructs them to. 259 if GTEST_OUTPUT_VAR_NAME in os.environ: 260 del os.environ[GTEST_OUTPUT_VAR_NAME] 261 262 _test_module.main() 263