00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 """Tests the text output of Google C++ Testing Framework.
00033
00034 SYNOPSIS
00035 gtest_output_test.py --build_dir=BUILD/DIR --gengolden
00036 # where BUILD/DIR contains the built gtest_output_test_ file.
00037 gtest_output_test.py --gengolden
00038 gtest_output_test.py
00039 """
00040
00041 __author__ = 'wan@google.com (Zhanyong Wan)'
00042
00043 import os
00044 import re
00045 import sys
00046 import gtest_test_utils
00047
00048
00049
00050 GENGOLDEN_FLAG = '--gengolden'
00051 CATCH_EXCEPTIONS_ENV_VAR_NAME = 'GTEST_CATCH_EXCEPTIONS'
00052
00053 IS_WINDOWS = os.name == 'nt'
00054
00055
00056 GOLDEN_NAME = 'gtest_output_test_golden_lin.txt'
00057
00058 PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath('gtest_output_test_')
00059
00060
00061
00062 COMMAND_LIST_TESTS = ({}, [PROGRAM_PATH, '--gtest_list_tests'])
00063 COMMAND_WITH_COLOR = ({}, [PROGRAM_PATH, '--gtest_color=yes'])
00064 COMMAND_WITH_TIME = ({}, [PROGRAM_PATH,
00065 '--gtest_print_time',
00066 '--gtest_internal_skip_environment_and_ad_hoc_tests',
00067 '--gtest_filter=FatalFailureTest.*:LoggingTest.*'])
00068 COMMAND_WITH_DISABLED = (
00069 {}, [PROGRAM_PATH,
00070 '--gtest_also_run_disabled_tests',
00071 '--gtest_internal_skip_environment_and_ad_hoc_tests',
00072 '--gtest_filter=*DISABLED_*'])
00073 COMMAND_WITH_SHARDING = (
00074 {'GTEST_SHARD_INDEX': '1', 'GTEST_TOTAL_SHARDS': '2'},
00075 [PROGRAM_PATH,
00076 '--gtest_internal_skip_environment_and_ad_hoc_tests',
00077 '--gtest_filter=PassingTest.*'])
00078
00079 GOLDEN_PATH = os.path.join(gtest_test_utils.GetSourceDir(), GOLDEN_NAME)
00080
00081
00082 def ToUnixLineEnding(s):
00083 """Changes all Windows/Mac line endings in s to UNIX line endings."""
00084
00085 return s.replace('\r\n', '\n').replace('\r', '\n')
00086
00087
00088 def RemoveLocations(test_output):
00089 """Removes all file location info from a Google Test program's output.
00090
00091 Args:
00092 test_output: the output of a Google Test program.
00093
00094 Returns:
00095 output with all file location info (in the form of
00096 'DIRECTORY/FILE_NAME:LINE_NUMBER: 'or
00097 'DIRECTORY\\FILE_NAME(LINE_NUMBER): ') replaced by
00098 'FILE_NAME:#: '.
00099 """
00100
00101 return re.sub(r'.*[/\\](.+)(\:\d+|\(\d+\))\: ', r'\1:#: ', test_output)
00102
00103
00104 def RemoveStackTraceDetails(output):
00105 """Removes all stack traces from a Google Test program's output."""
00106
00107
00108 return re.sub(r'Stack trace:(.|\n)*?\n\n',
00109 'Stack trace: (omitted)\n\n', output)
00110
00111
00112 def RemoveStackTraces(output):
00113 """Removes all traces of stack traces from a Google Test program's output."""
00114
00115
00116 return re.sub(r'Stack trace:(.|\n)*?\n\n', '', output)
00117
00118
00119 def RemoveTime(output):
00120 """Removes all time information from a Google Test program's output."""
00121
00122 return re.sub(r'\(\d+ ms', '(? ms', output)
00123
00124
00125 def RemoveTypeInfoDetails(test_output):
00126 """Removes compiler-specific type info from Google Test program's output.
00127
00128 Args:
00129 test_output: the output of a Google Test program.
00130
00131 Returns:
00132 output with type information normalized to canonical form.
00133 """
00134
00135
00136 return re.sub(r'unsigned int', 'unsigned', test_output)
00137
00138
00139 def NormalizeToCurrentPlatform(test_output):
00140 """Normalizes platform specific output details for easier comparison."""
00141
00142 if IS_WINDOWS:
00143
00144 test_output = re.sub('\x1b\\[(0;3\d)?m', '', test_output)
00145
00146 test_output = re.sub(r': Failure\n', r': error: ', test_output)
00147
00148 test_output = re.sub(r'((\w|\.)+)\((\d+)\):', r'\1:\3:', test_output)
00149
00150 return test_output
00151
00152
00153 def RemoveTestCounts(output):
00154 """Removes test counts from a Google Test program's output."""
00155
00156 output = re.sub(r'\d+ tests?, listed below',
00157 '? tests, listed below', output)
00158 output = re.sub(r'\d+ FAILED TESTS',
00159 '? FAILED TESTS', output)
00160 output = re.sub(r'\d+ tests? from \d+ test cases?',
00161 '? tests from ? test cases', output)
00162 output = re.sub(r'\d+ tests? from ([a-zA-Z_])',
00163 r'? tests from \1', output)
00164 return re.sub(r'\d+ tests?\.', '? tests.', output)
00165
00166
00167 def RemoveMatchingTests(test_output, pattern):
00168 """Removes output of specified tests from a Google Test program's output.
00169
00170 This function strips not only the beginning and the end of a test but also
00171 all output in between.
00172
00173 Args:
00174 test_output: A string containing the test output.
00175 pattern: A regex string that matches names of test cases or
00176 tests to remove.
00177
00178 Returns:
00179 Contents of test_output with tests whose names match pattern removed.
00180 """
00181
00182 test_output = re.sub(
00183 r'.*\[ RUN \] .*%s(.|\n)*?\[( FAILED | OK )\] .*%s.*\n' % (
00184 pattern, pattern),
00185 '',
00186 test_output)
00187 return re.sub(r'.*%s.*\n' % pattern, '', test_output)
00188
00189
00190 def NormalizeOutput(output):
00191 """Normalizes output (the output of gtest_output_test_.exe)."""
00192
00193 output = ToUnixLineEnding(output)
00194 output = RemoveLocations(output)
00195 output = RemoveStackTraceDetails(output)
00196 output = RemoveTime(output)
00197 return output
00198
00199
00200 def GetShellCommandOutput(env_cmd):
00201 """Runs a command in a sub-process, and returns its output in a string.
00202
00203 Args:
00204 env_cmd: The shell command. A 2-tuple where element 0 is a dict of extra
00205 environment variables to set, and element 1 is a string with
00206 the command and any flags.
00207
00208 Returns:
00209 A string with the command's combined standard and diagnostic output.
00210 """
00211
00212
00213
00214 environ = os.environ.copy()
00215 environ.update(env_cmd[0])
00216 p = gtest_test_utils.Subprocess(env_cmd[1], env=environ)
00217
00218 return p.output
00219
00220
00221 def GetCommandOutput(env_cmd):
00222 """Runs a command and returns its output with all file location
00223 info stripped off.
00224
00225 Args:
00226 env_cmd: The shell command. A 2-tuple where element 0 is a dict of extra
00227 environment variables to set, and element 1 is a string with
00228 the command and any flags.
00229 """
00230
00231
00232 environ, cmdline = env_cmd
00233 environ = dict(environ)
00234 environ[CATCH_EXCEPTIONS_ENV_VAR_NAME] = '1'
00235 return NormalizeOutput(GetShellCommandOutput((environ, cmdline)))
00236
00237
00238 def GetOutputOfAllCommands():
00239 """Returns concatenated output from several representative commands."""
00240
00241 return (GetCommandOutput(COMMAND_WITH_COLOR) +
00242 GetCommandOutput(COMMAND_WITH_TIME) +
00243 GetCommandOutput(COMMAND_WITH_DISABLED) +
00244 GetCommandOutput(COMMAND_WITH_SHARDING))
00245
00246
00247 test_list = GetShellCommandOutput(COMMAND_LIST_TESTS)
00248 SUPPORTS_DEATH_TESTS = 'DeathTest' in test_list
00249 SUPPORTS_TYPED_TESTS = 'TypedTest' in test_list
00250 SUPPORTS_THREADS = 'ExpectFailureWithThreadsTest' in test_list
00251 SUPPORTS_STACK_TRACES = False
00252
00253 CAN_GENERATE_GOLDEN_FILE = (SUPPORTS_DEATH_TESTS and
00254 SUPPORTS_TYPED_TESTS and
00255 SUPPORTS_THREADS and
00256 not IS_WINDOWS)
00257
00258 class GTestOutputTest(gtest_test_utils.TestCase):
00259 def RemoveUnsupportedTests(self, test_output):
00260 if not SUPPORTS_DEATH_TESTS:
00261 test_output = RemoveMatchingTests(test_output, 'DeathTest')
00262 if not SUPPORTS_TYPED_TESTS:
00263 test_output = RemoveMatchingTests(test_output, 'TypedTest')
00264 test_output = RemoveMatchingTests(test_output, 'TypedDeathTest')
00265 test_output = RemoveMatchingTests(test_output, 'TypeParamDeathTest')
00266 if not SUPPORTS_THREADS:
00267 test_output = RemoveMatchingTests(test_output,
00268 'ExpectFailureWithThreadsTest')
00269 test_output = RemoveMatchingTests(test_output,
00270 'ScopedFakeTestPartResultReporterTest')
00271 test_output = RemoveMatchingTests(test_output,
00272 'WorksConcurrently')
00273 if not SUPPORTS_STACK_TRACES:
00274 test_output = RemoveStackTraces(test_output)
00275
00276 return test_output
00277
00278 def testOutput(self):
00279 output = GetOutputOfAllCommands()
00280
00281 golden_file = open(GOLDEN_PATH, 'rb')
00282
00283
00284
00285
00286 golden = ToUnixLineEnding(golden_file.read())
00287 golden_file.close()
00288
00289
00290
00291
00292
00293 normalized_actual = RemoveTypeInfoDetails(output)
00294 normalized_golden = RemoveTypeInfoDetails(golden)
00295
00296 if CAN_GENERATE_GOLDEN_FILE:
00297 self.assertEqual(normalized_golden, normalized_actual)
00298 else:
00299 normalized_actual = NormalizeToCurrentPlatform(
00300 RemoveTestCounts(normalized_actual))
00301 normalized_golden = NormalizeToCurrentPlatform(
00302 RemoveTestCounts(self.RemoveUnsupportedTests(normalized_golden)))
00303
00304
00305 if os.getenv('DEBUG_GTEST_OUTPUT_TEST'):
00306 open(os.path.join(
00307 gtest_test_utils.GetSourceDir(),
00308 '_gtest_output_test_normalized_actual.txt'), 'wb').write(
00309 normalized_actual)
00310 open(os.path.join(
00311 gtest_test_utils.GetSourceDir(),
00312 '_gtest_output_test_normalized_golden.txt'), 'wb').write(
00313 normalized_golden)
00314
00315 self.assertEqual(normalized_golden, normalized_actual)
00316
00317
00318 if __name__ == '__main__':
00319 if sys.argv[1:] == [GENGOLDEN_FLAG]:
00320 if CAN_GENERATE_GOLDEN_FILE:
00321 output = GetOutputOfAllCommands()
00322 golden_file = open(GOLDEN_PATH, 'wb')
00323 golden_file.write(output)
00324 golden_file.close()
00325 else:
00326 message = (
00327 """Unable to write a golden file when compiled in an environment
00328 that does not support all the required features (death tests, typed tests,
00329 and multiple threads). Please generate the golden file using a binary built
00330 with those features enabled.""")
00331
00332 sys.stderr.write(message)
00333 sys.exit(1)
00334 else:
00335 gtest_test_utils.Main()