1 # Copyright 2006, Google Inc.
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
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
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.
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.
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-msg=C6204
37 IS_WINDOWS
= os
.name
== 'nt'
38 IS_CYGWIN
= os
.name
== 'posix' and 'CYGWIN' in os
.uname()[0]
39 IS_OS2
= os
.name
== 'os2'
44 import unittest
as _test_module
48 _SUBPROCESS_MODULE_AVAILABLE
= True
51 _SUBPROCESS_MODULE_AVAILABLE
= False
52 # pylint: enable-msg=C6204
54 GTEST_OUTPUT_VAR_NAME
= 'GTEST_OUTPUT'
56 # The environment variable for specifying the path to the premature-exit file.
57 PREMATURE_EXIT_FILE_ENV_VAR
= 'TEST_PREMATURE_EXIT_FILE'
59 environ
= os
.environ
.copy()
62 def SetEnvVar(env_var
, value
):
63 """Sets/unsets an environment variable to a given value."""
66 environ
[env_var
] = value
67 elif env_var
in environ
:
71 # Here we expose a class from a particular module, depending on the
72 # environment. The comment suppresses the 'Invalid variable name' lint
74 TestCase
= _test_module
.TestCase
# pylint: disable=C6409
76 # Initially maps a flag to its default value. After
77 # _ParseAndStripGTestFlags() is called, maps a flag to its actual value.
78 _flag_map
= {'source_dir': os
.path
.dirname(sys
.argv
[0]),
79 'build_dir': os
.path
.dirname(sys
.argv
[0])}
80 _gtest_flags_are_parsed
= False
83 def _ParseAndStripGTestFlags(argv
):
84 """Parses and strips Google Test flags from argv. This is idempotent."""
86 # Suppresses the lint complaint about a global variable since we need it
87 # here to maintain module-wide state.
88 global _gtest_flags_are_parsed
# pylint: disable=W0603
89 if _gtest_flags_are_parsed
:
92 _gtest_flags_are_parsed
= True
93 for flag
in _flag_map
:
94 # The environment variable overrides the default value.
95 if flag
.upper() in os
.environ
:
96 _flag_map
[flag
] = os
.environ
[flag
.upper()]
98 # The command line flag overrides the environment variable.
99 i
= 1 # Skips the program name.
101 prefix
= '--' + flag
+ '='
102 if argv
[i
].startswith(prefix
):
103 _flag_map
[flag
] = argv
[i
][len(prefix
):]
107 # We don't increment i in case we just found a --gtest_* flag
108 # and removed it from argv.
113 """Returns the value of the given flag."""
115 # In case GetFlag() is called before Main(), we always call
116 # _ParseAndStripGTestFlags() here to make sure the --gtest_* flags
118 _ParseAndStripGTestFlags(sys
.argv
)
120 return _flag_map
[flag
]
124 """Returns the absolute path of the directory where the .py files are."""
126 return os
.path
.abspath(GetFlag('source_dir'))
130 """Returns the absolute path of the directory where the test binaries are."""
132 return os
.path
.abspath(GetFlag('build_dir'))
137 def _RemoveTempDir():
139 shutil
.rmtree(_temp_dir
, ignore_errors
=True)
141 atexit
.register(_RemoveTempDir
)
147 _temp_dir
= tempfile
.mkdtemp()
151 def GetTestExecutablePath(executable_name
, build_dir
=None):
152 """Returns the absolute path of the test binary given its name.
154 The function will print a message and abort the program if the resulting file
158 executable_name: name of the test binary that the test script runs.
159 build_dir: directory where to look for executables, by default
160 the result of GetBuildDir().
163 The absolute path of the test binary.
166 path
= os
.path
.abspath(os
.path
.join(build_dir
or GetBuildDir(),
168 if (IS_WINDOWS
or IS_CYGWIN
or IS_OS2
) and not path
.endswith('.exe'):
171 if not os
.path
.exists(path
):
173 'Unable to find the test binary "%s". Please make sure to provide\n'
174 'a path to the binary via the --build_dir flag or the BUILD_DIR\n'
175 'environment variable.' % path
)
176 print >> sys
.stderr
, message
182 def GetExitStatus(exit_code
):
183 """Returns the argument to exit(), or -1 if exit() wasn't called.
186 exit_code: the result value of os.system(command).
190 # On Windows, os.WEXITSTATUS() doesn't work and os.system() returns
191 # the argument to exit() directly.
194 # On Unix, os.WEXITSTATUS() must be used to extract the exit status
195 # from the result of os.system().
196 if os
.WIFEXITED(exit_code
):
197 return os
.WEXITSTATUS(exit_code
)
203 def __init__(self
, command
, working_dir
=None, capture_stderr
=True, env
=None):
204 """Changes into a specified directory, if provided, and executes a command.
206 Restores the old directory afterwards.
209 command: The command to run, in the form of sys.argv.
210 working_dir: The directory to change into.
211 capture_stderr: Determines whether to capture stderr in the output member
213 env: Dictionary with environment to pass to the subprocess.
216 An object that represents outcome of the executed process. It has the
217 following attributes:
218 terminated_by_signal True if and only if the child process has been
219 terminated by a signal.
220 signal Sygnal that terminated the child process.
221 exited True if and only if the child process exited
223 exit_code The code with which the child process exited.
224 output Child process's stdout and stderr output
225 combined in a string.
228 # The subprocess module is the preferrable way of running programs
229 # since it is available and behaves consistently on all platforms,
230 # including Windows. But it is only available starting in python 2.4.
231 # In earlier python versions, we revert to the popen2 module, which is
232 # available in python 2.0 and later but doesn't provide required
233 # functionality (Popen4) under Windows. This allows us to support Mac
234 # OS X 10.4 Tiger, which has python 2.3 installed.
235 if _SUBPROCESS_MODULE_AVAILABLE
:
237 stderr
= subprocess
.STDOUT
239 stderr
= subprocess
.PIPE
241 p
= subprocess
.Popen(command
,
242 stdout
=subprocess
.PIPE
, stderr
=stderr
,
243 cwd
=working_dir
, universal_newlines
=True, env
=env
)
244 # communicate returns a tuple with the file object for the child's
246 self
.output
= p
.communicate()[0]
247 self
._return
_code
= p
.returncode
249 old_dir
= os
.getcwd()
251 def _ReplaceEnvDict(dest
, src
):
252 # Changes made by os.environ.clear are not inheritable by child
253 # processes until Python 2.6. To produce inheritable changes we have
254 # to delete environment items with the del statement.
255 for key
in dest
.keys():
259 # When 'env' is not None, backup the environment variables and replace
260 # them with the passed 'env'. When 'env' is None, we simply use the
261 # current 'os.environ' for compatibility with the subprocess.Popen
262 # semantics used above.
264 old_environ
= os
.environ
.copy()
265 _ReplaceEnvDict(os
.environ
, env
)
268 if working_dir
is not None:
269 os
.chdir(working_dir
)
271 p
= popen2
.Popen4(command
)
273 p
= popen2
.Popen3(command
)
275 self
.output
= p
.fromchild
.read()
280 # Restore the old environment variables
281 # if they were replaced.
283 _ReplaceEnvDict(os
.environ
, old_environ
)
285 # Converts ret_code to match the semantics of
286 # subprocess.Popen.returncode.
287 if os
.WIFSIGNALED(ret_code
):
288 self
._return
_code
= -os
.WTERMSIG(ret_code
)
289 else: # os.WIFEXITED(ret_code) should return True here.
290 self
._return
_code
= os
.WEXITSTATUS(ret_code
)
292 if self
._return
_code
< 0:
293 self
.terminated_by_signal
= True
295 self
.signal
= -self
._return
_code
297 self
.terminated_by_signal
= False
299 self
.exit_code
= self
._return
_code
303 """Runs the unit test."""
305 # We must call _ParseAndStripGTestFlags() before calling
306 # unittest.main(). Otherwise the latter will be confused by the
308 _ParseAndStripGTestFlags(sys
.argv
)
309 # The tested binaries should not be writing XML output files unless the
310 # script explicitly instructs them to.
311 if GTEST_OUTPUT_VAR_NAME
in os
.environ
:
312 del os
.environ
[GTEST_OUTPUT_VAR_NAME
]