3 # Copyright 2006, Google Inc.
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following disclaimer
14 # in the documentation and/or other materials provided with the
16 # * Neither the name of Google Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived from
18 # this software without specific prior written permission.
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 """Unit test utilities for Google C++ Testing Framework."""
34 __author__
= 'wan@google.com (Zhanyong Wan)'
42 _test_module
= unittest
44 # Suppresses the 'Import not at the top of the file' lint complaint.
45 # pylint: disable-msg=C6204
48 _SUBPROCESS_MODULE_AVAILABLE
= True
51 _SUBPROCESS_MODULE_AVAILABLE
= False
52 # pylint: enable-msg=C6204
54 GTEST_OUTPUT_VAR_NAME
= 'GTEST_OUTPUT'
56 IS_WINDOWS
= os
.name
== 'nt'
57 IS_CYGWIN
= os
.name
== 'posix' and 'CYGWIN' in os
.uname()[0]
59 # The environment variable for specifying the path to the premature-exit file.
60 PREMATURE_EXIT_FILE_ENV_VAR
= 'TEST_PREMATURE_EXIT_FILE'
62 environ
= os
.environ
.copy()
65 def SetEnvVar(env_var
, value
):
66 """Sets/unsets an environment variable to a given value."""
69 environ
[env_var
] = value
70 elif env_var
in environ
:
74 # Here we expose a class from a particular module, depending on the
75 # environment. The comment suppresses the 'Invalid variable name' lint
77 TestCase
= _test_module
.TestCase
# pylint: disable-msg=C6409
79 # Initially maps a flag to its default value. After
80 # _ParseAndStripGTestFlags() is called, maps a flag to its actual value.
81 _flag_map
= {'source_dir': os
.path
.dirname(sys
.argv
[0]),
82 'build_dir': os
.path
.dirname(sys
.argv
[0])}
83 _gtest_flags_are_parsed
= False
86 def _ParseAndStripGTestFlags(argv
):
87 """Parses and strips Google Test flags from argv. This is idempotent."""
89 # Suppresses the lint complaint about a global variable since we need it
90 # here to maintain module-wide state.
91 global _gtest_flags_are_parsed
# pylint: disable-msg=W0603
92 if _gtest_flags_are_parsed
:
95 _gtest_flags_are_parsed
= True
96 for flag
in _flag_map
:
97 # The environment variable overrides the default value.
98 if flag
.upper() in os
.environ
:
99 _flag_map
[flag
] = os
.environ
[flag
.upper()]
101 # The command line flag overrides the environment variable.
102 i
= 1 # Skips the program name.
104 prefix
= '--' + flag
+ '='
105 if argv
[i
].startswith(prefix
):
106 _flag_map
[flag
] = argv
[i
][len(prefix
):]
110 # We don't increment i in case we just found a --gtest_* flag
111 # and removed it from argv.
116 """Returns the value of the given flag."""
118 # In case GetFlag() is called before Main(), we always call
119 # _ParseAndStripGTestFlags() here to make sure the --gtest_* flags
121 _ParseAndStripGTestFlags(sys
.argv
)
123 return _flag_map
[flag
]
127 """Returns the absolute path of the directory where the .py files are."""
129 return os
.path
.abspath(GetFlag('source_dir'))
133 """Returns the absolute path of the directory where the test binaries are."""
135 return os
.path
.abspath(GetFlag('build_dir'))
140 def _RemoveTempDir():
142 shutil
.rmtree(_temp_dir
, ignore_errors
=True)
144 atexit
.register(_RemoveTempDir
)
148 """Returns a directory for temporary files."""
152 _temp_dir
= tempfile
.mkdtemp()
156 def GetTestExecutablePath(executable_name
, build_dir
=None):
157 """Returns the absolute path of the test binary given its name.
159 The function will print a message and abort the program if the resulting file
163 executable_name: name of the test binary that the test script runs.
164 build_dir: directory where to look for executables, by default
165 the result of GetBuildDir().
168 The absolute path of the test binary.
171 path
= os
.path
.abspath(os
.path
.join(build_dir
or GetBuildDir(),
173 if (IS_WINDOWS
or IS_CYGWIN
) and not path
.endswith('.exe'):
176 if not os
.path
.exists(path
):
178 'Unable to find the test binary "%s". Please make sure to provide\n'
179 'a path to the binary via the --build_dir flag or the BUILD_DIR\n'
180 'environment variable.' % path
)
181 sys
.stdout
.write(message
)
187 def GetExitStatus(exit_code
):
188 """Returns the argument to exit(), or -1 if exit() wasn't called.
191 exit_code: the result value of os.system(command).
195 # On Windows, os.WEXITSTATUS() doesn't work and os.system() returns
196 # the argument to exit() directly.
199 # On Unix, os.WEXITSTATUS() must be used to extract the exit status
200 # from the result of os.system().
201 if os
.WIFEXITED(exit_code
):
202 return os
.WEXITSTATUS(exit_code
)
208 def __init__(self
, command
, working_dir
=None, capture_stderr
=True, env
=None):
209 """Changes into a specified directory, if provided, and executes a command.
211 Restores the old directory afterwards.
214 command: The command to run, in the form of sys.argv.
215 working_dir: The directory to change into.
216 capture_stderr: Determines whether to capture stderr in the output member
218 env: Dictionary with environment to pass to the subprocess.
221 An object that represents outcome of the executed process. It has the
222 following attributes:
223 terminated_by_signal True iff the child process has been terminated
225 signal Sygnal that terminated the child process.
226 exited True iff the child process exited normally.
227 exit_code The code with which the child process exited.
228 output Child process's stdout and stderr output
229 combined in a string.
232 # The subprocess module is the preferrable way of running programs
233 # since it is available and behaves consistently on all platforms,
234 # including Windows. But it is only available starting in python 2.4.
235 # In earlier python versions, we revert to the popen2 module, which is
236 # available in python 2.0 and later but doesn't provide required
237 # functionality (Popen4) under Windows. This allows us to support Mac
238 # OS X 10.4 Tiger, which has python 2.3 installed.
239 if _SUBPROCESS_MODULE_AVAILABLE
:
241 stderr
= subprocess
.STDOUT
243 stderr
= subprocess
.PIPE
245 p
= subprocess
.Popen(command
,
246 stdout
=subprocess
.PIPE
, stderr
=stderr
,
247 cwd
=working_dir
, universal_newlines
=True, env
=env
)
248 # communicate returns a tuple with the file obect for the child's
250 self
.output
= p
.communicate()[0]
251 self
._return
_code
= p
.returncode
253 old_dir
= os
.getcwd()
255 def _ReplaceEnvDict(dest
, src
):
256 # Changes made by os.environ.clear are not inheritable by child
257 # processes until Python 2.6. To produce inheritable changes we have
258 # to delete environment items with the del statement.
259 for key
in dest
.keys():
263 # When 'env' is not None, backup the environment variables and replace
264 # them with the passed 'env'. When 'env' is None, we simply use the
265 # current 'os.environ' for compatibility with the subprocess.Popen
266 # semantics used above.
268 old_environ
= os
.environ
.copy()
269 _ReplaceEnvDict(os
.environ
, env
)
272 if working_dir
is not None:
273 os
.chdir(working_dir
)
275 p
= popen2
.Popen4(command
)
277 p
= popen2
.Popen3(command
)
279 self
.output
= p
.fromchild
.read()
284 # Restore the old environment variables
285 # if they were replaced.
287 _ReplaceEnvDict(os
.environ
, old_environ
)
289 # Converts ret_code to match the semantics of
290 # subprocess.Popen.returncode.
291 if os
.WIFSIGNALED(ret_code
):
292 self
._return
_code
= -os
.WTERMSIG(ret_code
)
293 else: # os.WIFEXITED(ret_code) should return True here.
294 self
._return
_code
= os
.WEXITSTATUS(ret_code
)
296 if self
._return
_code
< 0:
297 self
.terminated_by_signal
= True
299 self
.signal
= -self
._return
_code
301 self
.terminated_by_signal
= False
303 self
.exit_code
= self
._return
_code
307 """Runs the unit test."""
309 # We must call _ParseAndStripGTestFlags() before calling
310 # unittest.main(). Otherwise the latter will be confused by the
312 _ParseAndStripGTestFlags(sys
.argv
)
313 # The tested binaries should not be writing XML output files unless the
314 # script explicitly instructs them to.
315 # TODO(vladl@google.com): Move this into Subprocess when we implement
316 # passing environment into it as a parameter.
317 if GTEST_OUTPUT_VAR_NAME
in os
.environ
:
318 del os
.environ
[GTEST_OUTPUT_VAR_NAME
]