From 7fa242a44b47ce74d7246440b14571f7a5dd1b17 Mon Sep 17 00:00:00 2001 From: "zhanyong.wan" Date: Thu, 9 Apr 2009 02:57:38 +0000 Subject: [PATCH] Makes the Python tests more stable (by Vlad Losev); fixes a memory leak in GetThreadCount() on Mac (by Vlad Losev); improves fuse_gtest_files.py to support fusing Google Mock files (by Zhanyong Wan). --- scripts/fuse_gtest_files.py | 107 ++++++++++++------------ src/gtest-port.cc | 28 +++++-- test/gtest_break_on_failure_unittest.py | 4 +- test/gtest_color_test.py | 21 +++-- test/gtest_env_var_test.py | 40 ++------- test/gtest_filter_unittest.py | 74 +++++++++++----- test/gtest_help_test.py | 7 +- test/gtest_list_tests_unittest.py | 3 +- test/gtest_output_test.py | 69 +++++++++++---- test/gtest_test_utils.py | 38 ++++++++- test/gtest_throw_on_failure_test.py | 4 +- test/gtest_uninitialized_test.py | 41 ++------- test/gtest_xml_outfiles_test.py | 3 +- test/gtest_xml_output_unittest.py | 10 +-- test/gtest_xml_test_utils.py | 2 +- 15 files changed, 254 insertions(+), 197 deletions(-) diff --git a/scripts/fuse_gtest_files.py b/scripts/fuse_gtest_files.py index edffb1d1..148444ca 100755 --- a/scripts/fuse_gtest_files.py +++ b/scripts/fuse_gtest_files.py @@ -29,7 +29,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""fuse_gtest_files.py v0.1.0 +"""fuse_gtest_files.py v0.2.0 Fuses Google Test source code into a .h file and a .cc file. SYNOPSIS @@ -42,8 +42,8 @@ SYNOPSIS two files contain everything you need to use Google Test. Hence you can "install" Google Test by copying them to wherever you want. - GTEST_ROOT_DIR can be omitted and defaults to the parent directory - of the directory holding the fuse_gtest_files.py script. + GTEST_ROOT_DIR can be omitted and defaults to the parent + directory of the directory holding this script. EXAMPLES ./fuse_gtest_files.py fused_gtest @@ -63,13 +63,17 @@ import re import sets import sys +# We assume that this file is in the scripts/ directory in the Google +# Test root directory. +DEFAULT_GTEST_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..') + # Regex for matching '#include '. INCLUDE_GTEST_FILE_REGEX = re.compile(r'^\s*#\s*include\s*<(gtest/.+)>') # Regex for matching '#include "src/..."'. INCLUDE_SRC_FILE_REGEX = re.compile(r'^\s*#\s*include\s*"(src/.+)"') -# Where to find the source files. +# Where to find the source seed files. GTEST_H_SEED = 'include/gtest/gtest.h' GTEST_SPI_H_SEED = 'include/gtest/gtest-spi.h' GTEST_ALL_CC_SEED = 'src/gtest-all.cc' @@ -79,18 +83,18 @@ GTEST_H_OUTPUT = 'gtest/gtest.h' GTEST_ALL_CC_OUTPUT = 'gtest/gtest-all.cc' -def GetGTestRootDir(): - """Returns the absolute path to the Google Test root directory. +def VerifyFileExists(directory, relative_path): + """Verifies that the given file exists; aborts on failure. - We assume that this script is in a sub-directory of the Google Test root. + relative_path is the file path relative to the given directory. """ - my_path = sys.argv[0] # Path to this script. - my_dir = os.path.dirname(my_path) - if not my_dir: - my_dir = '.' - - return os.path.abspath(os.path.join(my_dir, '..')) + if not os.path.isfile(os.path.join(directory, relative_path)): + print 'ERROR: Cannot find %s in directory %s.' % (relative_path, + directory) + print ('Please either specify a valid project root directory ' + 'or omit it on the command line.') + sys.exit(1) def ValidateGTestRootDir(gtest_root): @@ -99,21 +103,34 @@ def ValidateGTestRootDir(gtest_root): The function aborts the program on failure. """ - def VerifyFileExists(relative_path): - """Verifies that the given file exists; aborts on failure. + VerifyFileExists(gtest_root, GTEST_H_SEED) + VerifyFileExists(gtest_root, GTEST_ALL_CC_SEED) - relative_path is the file path relative to the gtest root. - """ - if not os.path.isfile(os.path.join(gtest_root, relative_path)): - print 'ERROR: Cannot find %s in directory %s.' % (relative_path, - gtest_root) - print ('Please either specify a valid Google Test root directory ' - 'or omit it on the command line.') +def VerifyOutputFile(output_dir, relative_path): + """Verifies that the given output file path is valid. + + relative_path is relative to the output_dir directory. + """ + + # Makes sure the output file either doesn't exist or can be overwritten. + output_file = os.path.join(output_dir, relative_path) + if os.path.exists(output_file): + # TODO(wan@google.com): The following user-interaction doesn't + # work with automated processes. We should provide a way for the + # Makefile to force overwriting the files. + print ('%s already exists in directory %s - overwrite it? (y/N) ' % + (relative_path, output_dir)) + answer = sys.stdin.readline().strip() + if answer not in ['y', 'Y']: + print 'ABORTED.' sys.exit(1) - VerifyFileExists(GTEST_H_SEED) - VerifyFileExists(GTEST_ALL_CC_SEED) + # Makes sure the directory holding the output file exists; creates + # it and all its ancestors if necessary. + parent_directory = os.path.dirname(output_file) + if not os.path.isdir(parent_directory): + os.makedirs(parent_directory) def ValidateOutputDir(output_dir): @@ -122,30 +139,8 @@ def ValidateOutputDir(output_dir): The function aborts the program on failure. """ - def VerifyOutputFile(relative_path): - """Verifies that the given output file path is valid. - - relative_path is relative to the output_dir directory. - """ - - # Makes sure the output file either doesn't exist or can be overwritten. - output_file = os.path.join(output_dir, relative_path) - if os.path.exists(output_file): - print ('%s already exists in directory %s - overwrite it? (y/N) ' % - (relative_path, output_dir)) - answer = sys.stdin.readline().strip() - if answer not in ['y', 'Y']: - print 'ABORTED.' - sys.exit(1) - - # Makes sure the directory holding the output file exists; creates - # it and all its ancestors if necessary. - parent_directory = os.path.dirname(output_file) - if not os.path.isdir(parent_directory): - os.makedirs(parent_directory) - - VerifyOutputFile(GTEST_H_OUTPUT) - VerifyOutputFile(GTEST_ALL_CC_OUTPUT) + VerifyOutputFile(output_dir, GTEST_H_OUTPUT) + VerifyOutputFile(output_dir, GTEST_ALL_CC_OUTPUT) def FuseGTestH(gtest_root, output_dir): @@ -177,10 +172,9 @@ def FuseGTestH(gtest_root, output_dir): output_file.close() -def FuseGTestAllCc(gtest_root, output_dir): - """Scans folder gtest_root to generate gtest/gtest-all.cc in output_dir.""" +def FuseGTestAllCcToFile(gtest_root, output_file): + """Scans folder gtest_root to generate gtest/gtest-all.cc in output_file.""" - output_file = file(os.path.join(output_dir, GTEST_ALL_CC_OUTPUT), 'w') processed_files = sets.Set() def ProcessFile(gtest_source_file): @@ -219,10 +213,19 @@ def FuseGTestAllCc(gtest_root, output_dir): output_file.write(line) ProcessFile(GTEST_ALL_CC_SEED) + + +def FuseGTestAllCc(gtest_root, output_dir): + """Scans folder gtest_root to generate gtest/gtest-all.cc in output_dir.""" + + output_file = file(os.path.join(output_dir, GTEST_ALL_CC_OUTPUT), 'w') + FuseGTestAllCcToFile(gtest_root, output_file) output_file.close() def FuseGTest(gtest_root, output_dir): + """Fuses gtest.h and gtest-all.cc.""" + ValidateGTestRootDir(gtest_root) ValidateOutputDir(output_dir) @@ -234,7 +237,7 @@ def main(): argc = len(sys.argv) if argc == 2: # fuse_gtest_files.py OUTPUT_DIR - FuseGTest(GetGTestRootDir(), sys.argv[1]) + FuseGTest(DEFAULT_GTEST_ROOT_DIR, sys.argv[1]) elif argc == 3: # fuse_gtest_files.py GTEST_ROOT_DIR OUTPUT_DIR FuseGTest(sys.argv[1], sys.argv[2]) diff --git a/src/gtest-port.cc b/src/gtest-port.cc index 193f5323..3c1e80c2 100644 --- a/src/gtest-port.cc +++ b/src/gtest-port.cc @@ -45,6 +45,7 @@ #if GTEST_OS_MAC #include #include +#include #endif // GTEST_OS_MAC #ifdef _WIN32_WCE @@ -74,22 +75,37 @@ const int kStdErrFileno = 2; const int kStdErrFileno = STDERR_FILENO; #endif // _MSC_VER +#if GTEST_OS_MAC + // Returns the number of threads running in the process, or 0 to indicate that // we cannot detect it. size_t GetThreadCount() { -#if GTEST_OS_MAC + const task_t task = mach_task_self(); mach_msg_type_number_t thread_count; - thread_act_port_array_t thread_list; - kern_return_t status = task_threads(mach_task_self(), - &thread_list, &thread_count); - return status == KERN_SUCCESS ? static_cast(thread_count) : 0; + thread_act_array_t thread_list; + const kern_return_t status = task_threads(task, &thread_list, &thread_count); + if (status == KERN_SUCCESS) { + // task_threads allocates resources in thread_list and we need to free them + // to avoid leaks. + vm_deallocate(task, + reinterpret_cast(thread_list), + sizeof(thread_t) * thread_count); + return static_cast(thread_count); + } else { + return 0; + } +} + #else + +size_t GetThreadCount() { // There's no portable way to detect the number of threads, so we just // return 0 to indicate that we cannot detect it. return 0; -#endif // GTEST_OS_MAC } +#endif // GTEST_OS_MAC + #if GTEST_USES_POSIX_RE // Implements RE. Currently only needed for death tests. diff --git a/test/gtest_break_on_failure_unittest.py b/test/gtest_break_on_failure_unittest.py index 9c2855fd..c9dd0081 100755 --- a/test/gtest_break_on_failure_unittest.py +++ b/test/gtest_break_on_failure_unittest.py @@ -58,8 +58,8 @@ BREAK_ON_FAILURE_FLAG = 'gtest_break_on_failure' THROW_ON_FAILURE_ENV_VAR = 'GTEST_THROW_ON_FAILURE' # Path to the gtest_break_on_failure_unittest_ program. -EXE_PATH = os.path.join(gtest_test_utils.GetBuildDir(), - 'gtest_break_on_failure_unittest_') +EXE_PATH = gtest_test_utils.GetTestExecutablePath( + 'gtest_break_on_failure_unittest_') # Utilities. diff --git a/test/gtest_color_test.py b/test/gtest_color_test.py index 5260a899..32db4b9a 100755 --- a/test/gtest_color_test.py +++ b/test/gtest_color_test.py @@ -38,11 +38,11 @@ import os import sys import unittest +IS_WINDOWS = os.name = 'nt' COLOR_ENV_VAR = 'GTEST_COLOR' COLOR_FLAG = 'gtest_color' -COMMAND = os.path.join(gtest_test_utils.GetBuildDir(), - 'gtest_color_test_') +COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_color_test_') def SetEnvVar(env_var, value): @@ -69,11 +69,12 @@ class GTestColorTest(unittest.TestCase): def testNoEnvVarNoFlag(self): """Tests the case when there's neither GTEST_COLOR nor --gtest_color.""" - self.assert_(not UsesColor('dumb', None, None)) - self.assert_(not UsesColor('emacs', None, None)) - self.assert_(not UsesColor('xterm-mono', None, None)) - self.assert_(not UsesColor('unknown', None, None)) - self.assert_(not UsesColor(None, None, None)) + if not IS_WINDOWS: + self.assert_(not UsesColor('dumb', None, None)) + self.assert_(not UsesColor('emacs', None, None)) + self.assert_(not UsesColor('xterm-mono', None, None)) + self.assert_(not UsesColor('unknown', None, None)) + self.assert_(not UsesColor(None, None, None)) self.assert_(UsesColor('cygwin', None, None)) self.assert_(UsesColor('xterm', None, None)) self.assert_(UsesColor('xterm-color', None, None)) @@ -83,7 +84,8 @@ class GTestColorTest(unittest.TestCase): self.assert_(not UsesColor('dumb', None, 'no')) self.assert_(not UsesColor('xterm-color', None, 'no')) - self.assert_(not UsesColor('emacs', None, 'auto')) + if not IS_WINDOWS: + self.assert_(not UsesColor('emacs', None, 'auto')) self.assert_(UsesColor('xterm', None, 'auto')) self.assert_(UsesColor('dumb', None, 'yes')) self.assert_(UsesColor('xterm', None, 'yes')) @@ -93,7 +95,8 @@ class GTestColorTest(unittest.TestCase): self.assert_(not UsesColor('dumb', 'no', None)) self.assert_(not UsesColor('xterm-color', 'no', None)) - self.assert_(not UsesColor('dumb', 'auto', None)) + if not IS_WINDOWS: + self.assert_(not UsesColor('dumb', 'auto', None)) self.assert_(UsesColor('xterm-color', 'auto', None)) self.assert_(UsesColor('dumb', 'yes', None)) self.assert_(UsesColor('xterm-color', 'yes', None)) diff --git a/test/gtest_env_var_test.py b/test/gtest_env_var_test.py index c5acc5ca..92b9cd8a 100755 --- a/test/gtest_env_var_test.py +++ b/test/gtest_env_var_test.py @@ -41,18 +41,7 @@ import unittest IS_WINDOWS = os.name == 'nt' IS_LINUX = os.name == 'posix' -if IS_WINDOWS: - BUILD_DIRS = [ - 'build.dbg\\', - 'build.opt\\', - 'build.dbg8\\', - 'build.opt8\\', - ] - COMMAND = 'gtest_env_var_test_.exe' - -if IS_LINUX: - COMMAND = os.path.join(gtest_test_utils.GetBuildDir(), - 'gtest_env_var_test_') +COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_env_var_test_') def AssertEq(expected, actual): @@ -104,34 +93,19 @@ def TestEnvVarAffectsFlag(command): TestFlag(command, 'print_time', '1', '0') TestFlag(command, 'repeat', '999', '1') TestFlag(command, 'throw_on_failure', '1', '0') + TestFlag(command, 'death_test_style', 'threadsafe', 'fast') if IS_WINDOWS: TestFlag(command, 'catch_exceptions', '1', '0') if IS_LINUX: TestFlag(command, 'stack_trace_depth', '0', '100') - TestFlag(command, 'death_test_style', 'thread-safe', 'fast') TestFlag(command, 'death_test_use_fork', '1', '0') -if IS_WINDOWS: - - def main(): - for build_dir in BUILD_DIRS: - command = build_dir + COMMAND - print 'Testing with %s . . .' % (command,) - TestEnvVarAffectsFlag(command) - return 0 - - if __name__ == '__main__': - main() +class GTestEnvVarTest(unittest.TestCase): + def testEnvVarAffectsFlag(self): + TestEnvVarAffectsFlag(COMMAND) -if IS_LINUX: - - class GTestEnvVarTest(unittest.TestCase): - def testEnvVarAffectsFlag(self): - TestEnvVarAffectsFlag(COMMAND) - - - if __name__ == '__main__': - gtest_test_utils.Main() +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/test/gtest_filter_unittest.py b/test/gtest_filter_unittest.py index cd88cdf7..6002fac9 100755 --- a/test/gtest_filter_unittest.py +++ b/test/gtest_filter_unittest.py @@ -52,6 +52,8 @@ import gtest_test_utils # Constants. +IS_WINDOWS = os.name == 'nt' + # The environment variable for specifying the test filters. FILTER_ENV_VAR = 'GTEST_FILTER' @@ -67,8 +69,7 @@ FILTER_FLAG = 'gtest_filter' ALSO_RUN_DISABED_TESTS_FLAG = 'gtest_also_run_disabled_tests' # Command to run the gtest_filter_unittest_ program. -COMMAND = os.path.join(gtest_test_utils.GetBuildDir(), - 'gtest_filter_unittest_') +COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_filter_unittest_') # Regex for determining whether parameterized tests are enabled in the binary. PARAM_TEST_REGEX = re.compile(r'/ParamTest') @@ -204,23 +205,36 @@ class GTestFilterUnitTest(unittest.TestCase): self.assertEqual(len(set_var), len(full_partition)) self.assertEqual(sets.Set(set_var), sets.Set(full_partition)) + def AdjustForParameterizedTests(self, tests_to_run): + """Adjust tests_to_run in case value parameterized tests are disabled + in the binary. + """ + global param_tests_present + if not param_tests_present: + return list(sets.Set(tests_to_run) - sets.Set(PARAM_TESTS)) + else: + return tests_to_run + def RunAndVerify(self, gtest_filter, tests_to_run): """Runs gtest_flag_unittest_ with the given filter, and verifies that the right set of tests were run. """ - # Adjust tests_to_run in case value parameterized tests are disabled - # in the binary. - global param_tests_present - if not param_tests_present: - tests_to_run = list(sets.Set(tests_to_run) - sets.Set(PARAM_TESTS)) + tests_to_run = self.AdjustForParameterizedTests(tests_to_run) # First, tests using GTEST_FILTER. - SetEnvVar(FILTER_ENV_VAR, gtest_filter) - tests_run = Run(COMMAND)[0] - SetEnvVar(FILTER_ENV_VAR, None) - - self.AssertSetEqual(tests_run, tests_to_run) + # Windows removes empty variables from the environment when passing it + # to a new process. This means it is impossible to pass an empty filter + # into a process using the GTEST_FILTER environment variable. However, + # we can still test the case when the variable is not supplied (i.e., + # gtest_filter is None). + # pylint: disable-msg=C6403 + if not IS_WINDOWS or gtest_filter != '': + SetEnvVar(FILTER_ENV_VAR, gtest_filter) + tests_run = Run(COMMAND)[0] + SetEnvVar(FILTER_ENV_VAR, None) + self.AssertSetEqual(tests_run, tests_to_run) + # pylint: enable-msg=C6403 # Next, tests using --gtest_filter. @@ -239,21 +253,33 @@ class GTestFilterUnitTest(unittest.TestCase): on each shard should be identical to tests_to_run, without duplicates. If check_exit_0, make sure that all shards returned 0. """ - SetEnvVar(FILTER_ENV_VAR, gtest_filter) - partition = [] - for i in range(0, total_shards): - (tests_run, exit_code) = RunWithSharding(total_shards, i, command) - if check_exit_0: - self.assert_(exit_code is None) - partition.append(tests_run) + tests_to_run = self.AdjustForParameterizedTests(tests_to_run) - self.AssertPartitionIsValid(tests_to_run, partition) - SetEnvVar(FILTER_ENV_VAR, None) + # Windows removes empty variables from the environment when passing it + # to a new process. This means it is impossible to pass an empty filter + # into a process using the GTEST_FILTER environment variable. However, + # we can still test the case when the variable is not supplied (i.e., + # gtest_filter is None). + # pylint: disable-msg=C6403 + if not IS_WINDOWS or gtest_filter != '': + SetEnvVar(FILTER_ENV_VAR, gtest_filter) + partition = [] + for i in range(0, total_shards): + (tests_run, exit_code) = RunWithSharding(total_shards, i, command) + if check_exit_0: + self.assert_(exit_code is None) + partition.append(tests_run) + + self.AssertPartitionIsValid(tests_to_run, partition) + SetEnvVar(FILTER_ENV_VAR, None) + # pylint: enable-msg=C6403 def RunAndVerifyAllowingDisabled(self, gtest_filter, tests_to_run): """Runs gtest_flag_unittest_ with the given filter, and enables disabled tests. Verifies that the right set of tests were run. """ + tests_to_run = self.AdjustForParameterizedTests(tests_to_run) + # Construct the command line. command = '%s --%s' % (COMMAND, ALSO_RUN_DISABED_TESTS_FLAG) if gtest_filter is not None: @@ -263,8 +289,10 @@ class GTestFilterUnitTest(unittest.TestCase): self.AssertSetEqual(tests_run, tests_to_run) def setUp(self): - """Sets up test case. Determines whether value-parameterized tests are - enabled in the binary and sets flags accordingly. + """Sets up test case. + + Determines whether value-parameterized tests are enabled in the binary and + sets the flags accordingly. """ global param_tests_present if param_tests_present is None: diff --git a/test/gtest_help_test.py b/test/gtest_help_test.py index 62710192..d81f149f 100755 --- a/test/gtest_help_test.py +++ b/test/gtest_help_test.py @@ -47,12 +47,7 @@ import unittest IS_WINDOWS = os.name == 'nt' -if IS_WINDOWS: - PROGRAM = 'gtest_help_test_.exe' -else: - PROGRAM = 'gtest_help_test_' - -PROGRAM_PATH = os.path.join(gtest_test_utils.GetBuildDir(), PROGRAM) +PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath('gtest_help_test_') FLAG_PREFIX = '--gtest_' CATCH_EXCEPTIONS_FLAG = FLAG_PREFIX + 'catch_exceptions' DEATH_TEST_STYLE_FLAG = FLAG_PREFIX + 'death_test_style' diff --git a/test/gtest_list_tests_unittest.py b/test/gtest_list_tests_unittest.py index 9cefa15c..147bd73a 100755 --- a/test/gtest_list_tests_unittest.py +++ b/test/gtest_list_tests_unittest.py @@ -52,8 +52,7 @@ import unittest LIST_TESTS_FLAG = 'gtest_list_tests' # Path to the gtest_list_tests_unittest_ program. -EXE_PATH = os.path.join(gtest_test_utils.GetBuildDir(), - 'gtest_list_tests_unittest_'); +EXE_PATH = gtest_test_utils.GetTestExecutablePath('gtest_list_tests_unittest_') # The expected output when running gtest_list_tests_unittest_ with # --gtest_list_tests diff --git a/test/gtest_output_test.py b/test/gtest_output_test.py index f35e0024..52894064 100755 --- a/test/gtest_output_test.py +++ b/test/gtest_output_test.py @@ -54,16 +54,15 @@ GENGOLDEN_FLAG = '--gengolden' IS_WINDOWS = os.name == 'nt' if IS_WINDOWS: - PROGRAM = r'..\build.dbg8\gtest_output_test_.exe' GOLDEN_NAME = 'gtest_output_test_golden_win.txt' else: - PROGRAM = 'gtest_output_test_' GOLDEN_NAME = 'gtest_output_test_golden_lin.txt' -PROGRAM_PATH = os.path.join(gtest_test_utils.GetBuildDir(), PROGRAM) +PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath('gtest_output_test_') # At least one command we exercise must not have the # --gtest_internal_skip_environment_and_ad_hoc_tests flag. +COMMAND_LIST_TESTS = ({}, PROGRAM_PATH + ' --gtest_list_tests') COMMAND_WITH_COLOR = ({}, PROGRAM_PATH + ' --gtest_color=yes') COMMAND_WITH_TIME = ({}, PROGRAM_PATH + ' --gtest_print_time ' '--gtest_internal_skip_environment_and_ad_hoc_tests ' @@ -76,8 +75,7 @@ COMMAND_WITH_SHARDING = ({'GTEST_SHARD_INDEX': '1', 'GTEST_TOTAL_SHARDS': '2'}, ' --gtest_internal_skip_environment_and_ad_hoc_tests ' ' --gtest_filter="PassingTest.*"') -GOLDEN_PATH = os.path.join(gtest_test_utils.GetSourceDir(), - GOLDEN_NAME) +GOLDEN_PATH = os.path.join(gtest_test_utils.GetSourceDir(), GOLDEN_NAME) def ToUnixLineEnding(s): @@ -119,15 +117,35 @@ def RemoveTime(output): def RemoveTestCounts(output): """Removes test counts from a Google Test program's output.""" + output = re.sub(r'\d+ tests, listed below', + '? tests, listed below', output) + output = re.sub(r'\d+ FAILED TESTS', + '? FAILED TESTS', output) output = re.sub(r'\d+ tests from \d+ test cases', '? tests from ? test cases', output) return re.sub(r'\d+ tests\.', '? tests.', output) -def RemoveDeathTests(output): - """Removes death test information from a Google Test program's output.""" +def RemoveMatchingTests(test_output, pattern): + """Removes typed test information from a Google Test program's output. - return re.sub(r'\n.*DeathTest.*', '', output) + This function strips not only the beginning and the end of a test but also all + output in between. + + Args: + test_output: A string containing the test output. + pattern: A string that matches names of test cases to remove. + + Returns: + Contents of test_output with removed test case whose names match pattern. + """ + + test_output = re.sub( + r'\[ RUN \] .*%s(.|\n)*?\[( FAILED | OK )\] .*%s.*\n' % ( + pattern, pattern), + '', + test_output) + return re.sub(r'.*%s.*\n' % pattern, '', test_output) def NormalizeOutput(output): @@ -220,7 +238,19 @@ def GetOutputOfAllCommands(): GetCommandOutput(COMMAND_WITH_SHARDING)) +test_list = GetShellCommandOutput(COMMAND_LIST_TESTS, '') +SUPPORTS_DEATH_TESTS = 'DeathTest' in test_list +SUPPORTS_TYPED_TESTS = 'TypedTest' in test_list + + class GTestOutputTest(unittest.TestCase): + def RemoveUnsupportedTests(self, test_output): + if not SUPPORTS_DEATH_TESTS: + test_output = RemoveMatchingTests(test_output, 'DeathTest') + if not SUPPORTS_TYPED_TESTS: + test_output = RemoveMatchingTests(test_output, 'TypedTest') + return test_output + def testOutput(self): output = GetOutputOfAllCommands() golden_file = open(GOLDEN_PATH, 'rb') @@ -229,16 +259,25 @@ class GTestOutputTest(unittest.TestCase): # We want the test to pass regardless of death tests being # supported or not. - self.assert_(output == golden or - RemoveTestCounts(output) == - RemoveTestCounts(RemoveDeathTests(golden))) + if SUPPORTS_DEATH_TESTS and SUPPORTS_TYPED_TESTS: + self.assert_(golden == output) + else: + print RemoveTestCounts(self.RemoveUnsupportedTests(golden)) + self.assert_(RemoveTestCounts(self.RemoveUnsupportedTests(golden)) == + RemoveTestCounts(output)) if __name__ == '__main__': if sys.argv[1:] == [GENGOLDEN_FLAG]: - output = GetOutputOfAllCommands() - golden_file = open(GOLDEN_PATH, 'wb') - golden_file.write(output) - golden_file.close() + if SUPPORTS_DEATH_TESTS and SUPPORTS_TYPED_TESTS: + output = GetOutputOfAllCommands() + golden_file = open(GOLDEN_PATH, 'wb') + golden_file.write(output) + golden_file.close() + else: + print >> sys.stderr, ('Unable to write a golden file when compiled in an ' + 'environment that does not support death tests and ' + 'typed tests. Are you using VC 7.1?') + sys.exit(1) else: gtest_test_utils.Main() diff --git a/test/gtest_test_utils.py b/test/gtest_test_utils.py index 8f55b075..7dc8c421 100755 --- a/test/gtest_test_utils.py +++ b/test/gtest_test_utils.py @@ -44,10 +44,10 @@ except: import popen2 _SUBPROCESS_MODULE_AVAILABLE = False +IS_WINDOWS = os.name == 'nt' -# Initially maps a flag to its default value. After -# _ParseAndStripGTestFlags() is called, maps a flag to its actual -# value. +# Initially maps a flag to its default value. After +# _ParseAndStripGTestFlags() is called, maps a flag to its actual value. _flag_map = {'gtest_source_dir': os.path.dirname(sys.argv[0]), 'gtest_build_dir': os.path.dirname(sys.argv[0])} _gtest_flags_are_parsed = False @@ -103,6 +103,38 @@ def GetBuildDir(): return os.path.abspath(GetFlag('gtest_build_dir')) +def GetTestExecutablePath(executable_name): + """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. + + Returns: + The absolute path of the test binary. + """ + + path = os.path.abspath(os.path.join(GetBuildDir(), executable_name)) + if IS_WINDOWS and not path.endswith('.exe'): + path += '.exe' + + if not os.path.exists(path): + message = ( + 'Unable to find the test binary. Please make sure to provide path\n' + 'to the binary via the --gtest_build_dir flag or the GTEST_BUILD_DIR\n' + 'environment variable. For convenient use, invoke this script via\n' + 'mk_test.py.\n' + # TODO(vladl@google.com): change mk_test.py to test.py after renaming + # the file. + 'Please run mk_test.py -h for help.') + 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. diff --git a/test/gtest_throw_on_failure_test.py b/test/gtest_throw_on_failure_test.py index a80d6172..50172d07 100755 --- a/test/gtest_throw_on_failure_test.py +++ b/test/gtest_throw_on_failure_test.py @@ -49,8 +49,8 @@ THROW_ON_FAILURE = 'gtest_throw_on_failure' # Path to the gtest_throw_on_failure_test_ program, compiled with # exceptions disabled. -EXE_PATH = os.path.join(gtest_test_utils.GetBuildDir(), - 'gtest_throw_on_failure_test_') +EXE_PATH = gtest_test_utils.GetTestExecutablePath( + 'gtest_throw_on_failure_test_') # Utilities. diff --git a/test/gtest_uninitialized_test.py b/test/gtest_uninitialized_test.py index a3ba629c..19b92e90 100755 --- a/test/gtest_uninitialized_test.py +++ b/test/gtest_uninitialized_test.py @@ -34,25 +34,11 @@ __author__ = 'wan@google.com (Zhanyong Wan)' import gtest_test_utils -import os import sys import unittest -IS_WINDOWS = os.name == 'nt' -IS_LINUX = os.name == 'posix' -if IS_WINDOWS: - BUILD_DIRS = [ - 'build.dbg\\', - 'build.opt\\', - 'build.dbg8\\', - 'build.opt8\\', - ] - COMMAND = 'gtest_uninitialized_test_.exe' - -if IS_LINUX: - COMMAND = os.path.join(gtest_test_utils.GetBuildDir(), - 'gtest_uninitialized_test_') +COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_uninitialized_test_') def Assert(condition): @@ -77,25 +63,10 @@ def TestExitCodeAndOutput(command): Assert('InitGoogleTest' in p.output) -if IS_WINDOWS: - - def main(): - for build_dir in BUILD_DIRS: - command = build_dir + COMMAND - print 'Testing with %s . . .' % (command,) - TestExitCodeAndOutput(command) - return 0 - - if __name__ == '__main__': - main() +class GTestUninitializedTest(unittest.TestCase): + def testExitCodeAndOutput(self): + TestExitCodeAndOutput(COMMAND) -if IS_LINUX: - - class GTestUninitializedTest(unittest.TestCase): - def testExitCodeAndOutput(self): - TestExitCodeAndOutput(COMMAND) - - - if __name__ == '__main__': - gtest_test_utils.Main() +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/test/gtest_xml_outfiles_test.py b/test/gtest_xml_outfiles_test.py index 3e91f6de..9d627932 100755 --- a/test/gtest_xml_outfiles_test.py +++ b/test/gtest_xml_outfiles_test.py @@ -98,8 +98,7 @@ class GTestXMLOutFilesTest(gtest_xml_test_utils.GTestXMLTestCase): self._TestOutFile(GTEST_OUTPUT_2_TEST, EXPECTED_XML_2) def _TestOutFile(self, test_name, expected_xml): - gtest_prog_path = os.path.join(gtest_test_utils.GetBuildDir(), - test_name) + gtest_prog_path = gtest_test_utils.GetTestExecutablePath(test_name) command = [gtest_prog_path, "--gtest_output=xml:%s" % self.output_dir_] p = gtest_test_utils.Subprocess(command, working_dir=tempfile.mkdtemp()) self.assert_(p.exited) diff --git a/test/gtest_xml_output_unittest.py b/test/gtest_xml_output_unittest.py index 4587c41b..622251ea 100755 --- a/test/gtest_xml_output_unittest.py +++ b/test/gtest_xml_output_unittest.py @@ -121,10 +121,9 @@ class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase): default name if no name is explicitly specified. """ temp_dir = tempfile.mkdtemp() - output_file = os.path.join(temp_dir, - GTEST_DEFAULT_OUTPUT_FILE) - gtest_prog_path = os.path.join(gtest_test_utils.GetBuildDir(), - "gtest_no_test_unittest") + output_file = os.path.join(temp_dir, GTEST_DEFAULT_OUTPUT_FILE) + gtest_prog_path = gtest_test_utils.GetTestExecutablePath( + "gtest_no_test_unittest") try: os.remove(output_file) except OSError, e: @@ -148,8 +147,7 @@ class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase): """ xml_path = os.path.join(tempfile.mkdtemp(), gtest_prog_name + "out.xml") - gtest_prog_path = os.path.join(gtest_test_utils.GetBuildDir(), - gtest_prog_name) + gtest_prog_path = gtest_test_utils.GetTestExecutablePath(gtest_prog_name) command = [gtest_prog_path, "%s=xml:%s" % (GTEST_OUTPUT_FLAG, xml_path)] p = gtest_test_utils.Subprocess(command) diff --git a/test/gtest_xml_test_utils.py b/test/gtest_xml_test_utils.py index 00a56cbf..64eebb10 100755 --- a/test/gtest_xml_test_utils.py +++ b/test/gtest_xml_test_utils.py @@ -150,7 +150,7 @@ class GTestXMLTestCase(unittest.TestCase): for child in element.childNodes: if child.nodeType == Node.CDATA_SECTION_NODE: # Removes the source line number. - cdata = re.sub(r"^.*/(.*:)\d+\n", "\\1*\n", child.nodeValue) + cdata = re.sub(r"^.*[/\\](.*:)\d+\n", "\\1*\n", child.nodeValue) # Removes the actual stack trace. child.nodeValue = re.sub(r"\nStack trace:\n(.|\n)*", "", cdata)