1#!/usr/bin/env python
2#
3# Copyright 2010 Google Inc.  All Rights Reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9#     * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#     * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15#     * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31"""Tests Google Test's exception catching behavior.
32
33This script invokes googletest-catch-exceptions-test_ and
34googletest-catch-exceptions-ex-test_ (programs written with
35Google Test) and verifies their output.
36"""
37
38from googletest.test import gtest_test_utils
39
40# Constants.
41FLAG_PREFIX = '--gtest_'
42LIST_TESTS_FLAG = FLAG_PREFIX + 'list_tests'
43NO_CATCH_EXCEPTIONS_FLAG = FLAG_PREFIX + 'catch_exceptions=0'
44FILTER_FLAG = FLAG_PREFIX + 'filter'
45
46# Path to the googletest-catch-exceptions-ex-test_ binary, compiled with
47# exceptions enabled.
48EX_EXE_PATH = gtest_test_utils.GetTestExecutablePath(
49    'googletest-catch-exceptions-ex-test_'
50)
51
52# Path to the googletest-catch-exceptions-test_ binary, compiled with
53# exceptions disabled.
54EXE_PATH = gtest_test_utils.GetTestExecutablePath(
55    'googletest-catch-exceptions-no-ex-test_'
56)
57
58environ = gtest_test_utils.environ
59SetEnvVar = gtest_test_utils.SetEnvVar
60
61# Tests in this file run a Google-Test-based test program and expect it
62# to terminate prematurely.  Therefore they are incompatible with
63# the premature-exit-file protocol by design.  Unset the
64# premature-exit filepath to prevent Google Test from creating
65# the file.
66SetEnvVar(gtest_test_utils.PREMATURE_EXIT_FILE_ENV_VAR, None)
67
68TEST_LIST = gtest_test_utils.Subprocess(
69    [EXE_PATH, LIST_TESTS_FLAG], env=environ
70).output
71
72SUPPORTS_SEH_EXCEPTIONS = 'ThrowsSehException' in TEST_LIST
73
74if SUPPORTS_SEH_EXCEPTIONS:
75  BINARY_OUTPUT = gtest_test_utils.Subprocess([EXE_PATH], env=environ).output
76
77EX_BINARY_OUTPUT = gtest_test_utils.Subprocess(
78    [EX_EXE_PATH], env=environ
79).output
80
81
82# The tests.
83if SUPPORTS_SEH_EXCEPTIONS:
84
85  class CatchSehExceptionsTest(gtest_test_utils.TestCase):
86    """Tests exception-catching behavior."""
87
88    def TestSehExceptions(self, test_output):
89      self.assertIn(
90          (
91              'SEH exception with code 0x2a thrown '
92              "in the test fixture's constructor"
93          ),
94          test_output,
95      )
96      self.assertIn(
97          (
98              'SEH exception with code 0x2a thrown '
99              "in the test fixture's destructor"
100          ),
101          test_output,
102      )
103      self.assertIn(
104          'SEH exception with code 0x2a thrown in SetUpTestSuite()', test_output
105      )
106      self.assertIn(
107          'SEH exception with code 0x2a thrown in TearDownTestSuite()',
108          test_output,
109      )
110      self.assertIn(
111          'SEH exception with code 0x2a thrown in SetUp()', test_output
112      )
113      self.assertIn(
114          'SEH exception with code 0x2a thrown in TearDown()', test_output
115      )
116      self.assertIn(
117          'SEH exception with code 0x2a thrown in the test body', test_output
118      )
119
120    def testCatchesSehExceptionsWithCxxExceptionsEnabled(self):
121      self.TestSehExceptions(EX_BINARY_OUTPUT)
122
123    def testCatchesSehExceptionsWithCxxExceptionsDisabled(self):
124      self.TestSehExceptions(BINARY_OUTPUT)
125
126
127class CatchCxxExceptionsTest(gtest_test_utils.TestCase):
128  """Tests C++ exception-catching behavior.
129
130  Tests in this test case verify that:
131  * C++ exceptions are caught and logged as C++ (not SEH) exceptions
132  * Exception thrown affect the remainder of the test work flow in the
133    expected manner.
134  """
135
136  def testCatchesCxxExceptionsInFixtureConstructor(self):
137    self.assertTrue(
138        'C++ exception with description '
139        '"Standard C++ exception" thrown '
140        "in the test fixture's constructor"
141        in EX_BINARY_OUTPUT,
142        EX_BINARY_OUTPUT,
143    )
144    self.assertTrue(
145        'unexpected' not in EX_BINARY_OUTPUT,
146        (
147            'This failure belongs in this test only if '
148            '"CxxExceptionInConstructorTest" (no quotes) '
149            'appears on the same line as words "called unexpectedly"'
150        ),
151    )
152
153  if (
154      'CxxExceptionInDestructorTest.ThrowsExceptionInDestructor'
155      in EX_BINARY_OUTPUT
156  ):
157
158    def testCatchesCxxExceptionsInFixtureDestructor(self):
159      self.assertTrue(
160          'C++ exception with description '
161          '"Standard C++ exception" thrown '
162          "in the test fixture's destructor"
163          in EX_BINARY_OUTPUT,
164          EX_BINARY_OUTPUT,
165      )
166      self.assertTrue(
167          'CxxExceptionInDestructorTest::TearDownTestSuite() '
168          'called as expected.'
169          in EX_BINARY_OUTPUT,
170          EX_BINARY_OUTPUT,
171      )
172
173  def testCatchesCxxExceptionsInSetUpTestCase(self):
174    self.assertTrue(
175        'C++ exception with description "Standard C++ exception"'
176        ' thrown in SetUpTestSuite()'
177        in EX_BINARY_OUTPUT,
178        EX_BINARY_OUTPUT,
179    )
180    self.assertTrue(
181        'CxxExceptionInConstructorTest::TearDownTestSuite() called as expected.'
182        in EX_BINARY_OUTPUT,
183        EX_BINARY_OUTPUT,
184    )
185    self.assertFalse(
186        'CxxExceptionInSetUpTestSuiteTest constructor called as expected.'
187        in EX_BINARY_OUTPUT,
188        EX_BINARY_OUTPUT,
189    )
190    self.assertFalse(
191        'CxxExceptionInSetUpTestSuiteTest destructor called as expected.'
192        in EX_BINARY_OUTPUT,
193        EX_BINARY_OUTPUT,
194    )
195    self.assertFalse(
196        'CxxExceptionInSetUpTestSuiteTest::SetUp() called as expected.'
197        in EX_BINARY_OUTPUT,
198        EX_BINARY_OUTPUT,
199    )
200    self.assertFalse(
201        'CxxExceptionInSetUpTestSuiteTest::TearDown() called as expected.'
202        in EX_BINARY_OUTPUT,
203        EX_BINARY_OUTPUT,
204    )
205    self.assertFalse(
206        'CxxExceptionInSetUpTestSuiteTest test body called as expected.'
207        in EX_BINARY_OUTPUT,
208        EX_BINARY_OUTPUT,
209    )
210
211  def testCatchesCxxExceptionsInTearDownTestCase(self):
212    self.assertTrue(
213        'C++ exception with description "Standard C++ exception"'
214        ' thrown in TearDownTestSuite()'
215        in EX_BINARY_OUTPUT,
216        EX_BINARY_OUTPUT,
217    )
218
219  def testCatchesCxxExceptionsInSetUp(self):
220    self.assertTrue(
221        'C++ exception with description "Standard C++ exception"'
222        ' thrown in SetUp()'
223        in EX_BINARY_OUTPUT,
224        EX_BINARY_OUTPUT,
225    )
226    self.assertTrue(
227        'CxxExceptionInSetUpTest::TearDownTestSuite() called as expected.'
228        in EX_BINARY_OUTPUT,
229        EX_BINARY_OUTPUT,
230    )
231    self.assertTrue(
232        'CxxExceptionInSetUpTest destructor called as expected.'
233        in EX_BINARY_OUTPUT,
234        EX_BINARY_OUTPUT,
235    )
236    self.assertTrue(
237        'CxxExceptionInSetUpTest::TearDown() called as expected.'
238        in EX_BINARY_OUTPUT,
239        EX_BINARY_OUTPUT,
240    )
241    self.assertTrue(
242        'unexpected' not in EX_BINARY_OUTPUT,
243        (
244            'This failure belongs in this test only if '
245            '"CxxExceptionInSetUpTest" (no quotes) '
246            'appears on the same line as words "called unexpectedly"'
247        ),
248    )
249
250  def testCatchesCxxExceptionsInTearDown(self):
251    self.assertTrue(
252        'C++ exception with description "Standard C++ exception"'
253        ' thrown in TearDown()'
254        in EX_BINARY_OUTPUT,
255        EX_BINARY_OUTPUT,
256    )
257    self.assertTrue(
258        'CxxExceptionInTearDownTest::TearDownTestSuite() called as expected.'
259        in EX_BINARY_OUTPUT,
260        EX_BINARY_OUTPUT,
261    )
262    self.assertTrue(
263        'CxxExceptionInTearDownTest destructor called as expected.'
264        in EX_BINARY_OUTPUT,
265        EX_BINARY_OUTPUT,
266    )
267
268  def testCatchesCxxExceptionsInTestBody(self):
269    self.assertTrue(
270        'C++ exception with description "Standard C++ exception"'
271        ' thrown in the test body'
272        in EX_BINARY_OUTPUT,
273        EX_BINARY_OUTPUT,
274    )
275    self.assertTrue(
276        'CxxExceptionInTestBodyTest::TearDownTestSuite() called as expected.'
277        in EX_BINARY_OUTPUT,
278        EX_BINARY_OUTPUT,
279    )
280    self.assertTrue(
281        'CxxExceptionInTestBodyTest destructor called as expected.'
282        in EX_BINARY_OUTPUT,
283        EX_BINARY_OUTPUT,
284    )
285    self.assertTrue(
286        'CxxExceptionInTestBodyTest::TearDown() called as expected.'
287        in EX_BINARY_OUTPUT,
288        EX_BINARY_OUTPUT,
289    )
290
291  def testCatchesNonStdCxxExceptions(self):
292    self.assertTrue(
293        'Unknown C++ exception thrown in the test body' in EX_BINARY_OUTPUT,
294        EX_BINARY_OUTPUT,
295    )
296
297  def testUnhandledCxxExceptionsAbortTheProgram(self):
298    # Filters out SEH exception tests on Windows. Unhandled SEH exceptions
299    # cause tests to show pop-up windows there.
300    filter_out_seh_tests_flag = FILTER_FLAG + '=-*Seh*'
301    # By default, Google Test doesn't catch the exceptions.
302    uncaught_exceptions_ex_binary_output = gtest_test_utils.Subprocess(
303        [EX_EXE_PATH, NO_CATCH_EXCEPTIONS_FLAG, filter_out_seh_tests_flag],
304        env=environ,
305    ).output
306
307    self.assertIn(
308        'Unhandled C++ exception terminating the program',
309        uncaught_exceptions_ex_binary_output,
310    )
311    self.assertNotIn('unexpected', uncaught_exceptions_ex_binary_output)
312
313
314if __name__ == '__main__':
315  gtest_test_utils.Main()
316