gtest_output_test.py revision 1be2c9def7187e4e643c00a31dd9986395795d7d
1#!/usr/bin/env python
2#
3# Copyright 2008, Google Inc.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are
8# met:
9#
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
15# distribution.
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.
19#
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.
31
32"""Tests the text output of Google C++ Testing Framework.
33
34SYNOPSIS
35       gtest_output_test.py --gtest_build_dir=BUILD/DIR --gengolden
36         # where BUILD/DIR contains the built gtest_output_test_ file.
37       gtest_output_test.py --gengolden
38       gtest_output_test.py
39"""
40
41__author__ = 'wan@google.com (Zhanyong Wan)'
42
43import os
44import re
45import string
46import sys
47import unittest
48import gtest_test_utils
49
50
51# The flag for generating the golden file
52GENGOLDEN_FLAG = '--gengolden'
53
54IS_WINDOWS = os.name == 'nt'
55
56if IS_WINDOWS:
57  PROGRAM = r'..\build.dbg8\gtest_output_test_.exe'
58  GOLDEN_NAME = 'gtest_output_test_golden_win.txt'
59else:
60  PROGRAM = 'gtest_output_test_'
61  GOLDEN_NAME = 'gtest_output_test_golden_lin.txt'
62
63PROGRAM_PATH = os.path.join(gtest_test_utils.GetBuildDir(), PROGRAM)
64
65# At least one command we exercise must not have the
66# --gtest_internal_skip_environment_and_ad_hoc_tests flag.
67COMMAND_WITH_COLOR = ({}, PROGRAM_PATH + ' --gtest_color=yes')
68COMMAND_WITH_TIME = ({}, PROGRAM_PATH + ' --gtest_print_time '
69                     '--gtest_internal_skip_environment_and_ad_hoc_tests '
70                     '--gtest_filter="FatalFailureTest.*:LoggingTest.*"')
71COMMAND_WITH_DISABLED = ({}, PROGRAM_PATH + ' --gtest_also_run_disabled_tests '
72                         '--gtest_internal_skip_environment_and_ad_hoc_tests '
73                         '--gtest_filter="*DISABLED_*"')
74COMMAND_WITH_SHARDING = ({'GTEST_SHARD_INDEX': '1', 'GTEST_TOTAL_SHARDS': '2'},
75                         PROGRAM_PATH +
76                         ' --gtest_internal_skip_environment_and_ad_hoc_tests '
77                         ' --gtest_filter="PassingTest.*"')
78
79GOLDEN_PATH = os.path.join(gtest_test_utils.GetSourceDir(),
80                           GOLDEN_NAME)
81
82
83def ToUnixLineEnding(s):
84  """Changes all Windows/Mac line endings in s to UNIX line endings."""
85
86  return s.replace('\r\n', '\n').replace('\r', '\n')
87
88
89def RemoveLocations(output):
90  """Removes all file location info from a Google Test program's output.
91
92  Args:
93       output:  the output of a Google Test program.
94
95  Returns:
96       output with all file location info (in the form of
97       'DIRECTORY/FILE_NAME:LINE_NUMBER: 'or
98       'DIRECTORY\\FILE_NAME(LINE_NUMBER): ') replaced by
99       'FILE_NAME:#: '.
100  """
101
102  return re.sub(r'.*[/\\](.+)(\:\d+|\(\d+\))\: ', r'\1:#: ', output)
103
104
105def RemoveStackTraces(output):
106  """Removes all stack traces from a Google Test program's output."""
107
108  # *? means "find the shortest string that matches".
109  return re.sub(r'Stack trace:(.|\n)*?\n\n',
110                'Stack trace: (omitted)\n\n', output)
111
112
113def RemoveTime(output):
114  """Removes all time information from a Google Test program's output."""
115
116  return re.sub(r'\(\d+ ms', '(? ms', output)
117
118
119def RemoveTestCounts(output):
120  """Removes test counts from a Google Test program's output."""
121
122  output = re.sub(r'\d+ tests from \d+ test cases',
123                  '? tests from ? test cases', output)
124  return re.sub(r'\d+ tests\.', '? tests.', output)
125
126
127def RemoveDeathTests(output):
128  """Removes death test information from a Google Test program's output."""
129
130  return re.sub(r'\n.*DeathTest.*', '', output)
131
132
133def NormalizeOutput(output):
134  """Normalizes output (the output of gtest_output_test_.exe)."""
135
136  output = ToUnixLineEnding(output)
137  output = RemoveLocations(output)
138  output = RemoveStackTraces(output)
139  output = RemoveTime(output)
140  return output
141
142
143def IterShellCommandOutput(env_cmd, stdin_string=None):
144  """Runs a command in a sub-process, and iterates the lines in its STDOUT.
145
146  Args:
147
148    env_cmd:           The shell command. A 2-tuple where element 0 is a dict
149                       of extra environment variables to set, and element 1
150                       is a string with the command and any flags.
151    stdin_string:      The string to be fed to the STDIN of the sub-process;
152                       If None, the sub-process will inherit the STDIN
153                       from the parent process.
154  """
155
156  # Spawns cmd in a sub-process, and gets its standard I/O file objects.
157  # Set and save the environment properly.
158  old_env_vars = dict(os.environ)
159  os.environ.update(env_cmd[0])
160  stdin_file, stdout_file = os.popen2(env_cmd[1], 'b')
161  os.environ.clear()
162  os.environ.update(old_env_vars)
163
164  # If the caller didn't specify a string for STDIN, gets it from the
165  # parent process.
166  if stdin_string is None:
167    stdin_string = sys.stdin.read()
168
169  # Feeds the STDIN string to the sub-process.
170  stdin_file.write(stdin_string)
171  stdin_file.close()
172
173  while True:
174    line = stdout_file.readline()
175    if not line:  # EOF
176      stdout_file.close()
177      break
178
179    yield line
180
181
182def GetShellCommandOutput(env_cmd, stdin_string=None):
183  """Runs a command in a sub-process, and returns its STDOUT in a string.
184
185  Args:
186
187    env_cmd:           The shell command. A 2-tuple where element 0 is a dict
188                       of extra environment variables to set, and element 1
189                       is a string with the command and any flags.
190    stdin_string:      The string to be fed to the STDIN of the sub-process;
191                       If None, the sub-process will inherit the STDIN
192                       from the parent process.
193  """
194
195  lines = list(IterShellCommandOutput(env_cmd, stdin_string))
196  return string.join(lines, '')
197
198
199def GetCommandOutput(env_cmd):
200  """Runs a command and returns its output with all file location
201  info stripped off.
202
203  Args:
204    env_cmd:  The shell command. A 2-tuple where element 0 is a dict of extra
205              environment variables to set, and element 1 is a string with
206              the command and any flags.
207  """
208
209  # Disables exception pop-ups on Windows.
210  os.environ['GTEST_CATCH_EXCEPTIONS'] = '1'
211  return NormalizeOutput(GetShellCommandOutput(env_cmd, ''))
212
213
214def GetOutputOfAllCommands():
215  """Returns concatenated output from several representative commands."""
216
217  return (GetCommandOutput(COMMAND_WITH_COLOR) +
218          GetCommandOutput(COMMAND_WITH_TIME) +
219          GetCommandOutput(COMMAND_WITH_DISABLED) +
220          GetCommandOutput(COMMAND_WITH_SHARDING))
221
222
223class GTestOutputTest(unittest.TestCase):
224  def testOutput(self):
225    output = GetOutputOfAllCommands()
226    golden_file = open(GOLDEN_PATH, 'rb')
227    golden = golden_file.read()
228    golden_file.close()
229
230    # We want the test to pass regardless of death tests being
231    # supported or not.
232    self.assert_(output == golden or
233                 RemoveTestCounts(output) ==
234                 RemoveTestCounts(RemoveDeathTests(golden)))
235
236
237if __name__ == '__main__':
238  if sys.argv[1:] == [GENGOLDEN_FLAG]:
239    output = GetOutputOfAllCommands()
240    golden_file = open(GOLDEN_PATH, 'wb')
241    golden_file.write(output)
242    golden_file.close()
243  else:
244    gtest_test_utils.Main()
245