summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvladlosev <vladlosev@861a406c-534a-0410-8894-cb66d6ee9925>2010-10-18 22:09:55 +0000
committervladlosev <vladlosev@861a406c-534a-0410-8894-cb66d6ee9925>2010-10-18 22:09:55 +0000
commit636f903d84ec28714cb3d84a346dfb58f5bc99d3 (patch)
tree3d20f998d4faf253f25acc6ea747ad39c545a3be
parent035dbfb1b79edac47512377dae33e2d8ba458f33 (diff)
downloadgoogletest-636f903d84ec28714cb3d84a346dfb58f5bc99d3.tar.gz
Modifies handling of C++ exceptions in death tests to treat exceptions escaping them as failures.
git-svn-id: http://googletest.googlecode.com/svn/trunk@500 861a406c-534a-0410-8894-cb66d6ee9925
-rw-r--r--CMakeLists.txt7
-rw-r--r--include/gtest/internal/gtest-death-test-internal.h35
-rw-r--r--src/gtest-death-test.cc33
-rw-r--r--test/gtest-death-test_ex_test.cc93
-rw-r--r--test/gtest-death-test_test.cc7
5 files changed, 160 insertions, 15 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 130719f..0fe2654 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -147,6 +147,13 @@ if (gtest_build_tests)
cxx_library(gtest_main_no_rtti "${cxx_no_rtti}"
src/gtest-all.cc src/gtest_main.cc)
+ cxx_test_with_flags(gtest-death-test_ex_nocatch_test
+ "${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=0"
+ gtest test/gtest-death-test_ex_test.cc)
+ cxx_test_with_flags(gtest-death-test_ex_catch_test
+ "${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=1"
+ gtest test/gtest-death-test_ex_test.cc)
+
cxx_test_with_flags(gtest_no_rtti_unittest "${cxx_no_rtti}"
gtest_main_no_rtti test/gtest_unittest.cc)
diff --git a/include/gtest/internal/gtest-death-test-internal.h b/include/gtest/internal/gtest-death-test-internal.h
index 9242bd3..2b2c98d 100644
--- a/include/gtest/internal/gtest-death-test-internal.h
+++ b/include/gtest/internal/gtest-death-test-internal.h
@@ -39,6 +39,8 @@
#include "gtest/internal/gtest-internal.h"
+#include <stdio.h>
+
namespace testing {
namespace internal {
@@ -96,8 +98,12 @@ class GTEST_API_ DeathTest {
// test, then wait for it to complete.
enum TestRole { OVERSEE_TEST, EXECUTE_TEST };
- // An enumeration of the two reasons that a test might be aborted.
- enum AbortReason { TEST_ENCOUNTERED_RETURN_STATEMENT, TEST_DID_NOT_DIE };
+ // An enumeration of the three reasons that a test might be aborted.
+ enum AbortReason {
+ TEST_ENCOUNTERED_RETURN_STATEMENT,
+ TEST_THREW_EXCEPTION,
+ TEST_DID_NOT_DIE
+ };
// Assumes one of the above roles.
virtual TestRole AssumeRole() = 0;
@@ -149,6 +155,29 @@ class DefaultDeathTestFactory : public DeathTestFactory {
// by a signal, or exited normally with a nonzero exit code.
GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
+// Traps C++ exceptions escaping statement and reports them as test
+// failures. Note that trapping SEH exceptions is not implemented here.
+#if GTEST_HAS_EXCEPTIONS
+#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
+ try { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } catch (const ::std::exception& gtest_exception) { \
+ fprintf(\
+ stderr, \
+ "\n%s: Caught std::exception-derived exception escaping the " \
+ "death test statement. Exception message: %s\n", \
+ ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \
+ gtest_exception.what()); \
+ fflush(stderr); \
+ death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
+ } catch (...) { \
+ death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
+ }
+#else
+#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
+#endif
+
// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
// ASSERT_EXIT*, and EXPECT_EXIT*.
#define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \
@@ -172,7 +201,7 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
case ::testing::internal::DeathTest::EXECUTE_TEST: { \
::testing::internal::DeathTest::ReturnSentinel \
gtest_sentinel(gtest_dt); \
- GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \
gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
break; \
} \
diff --git a/src/gtest-death-test.cc b/src/gtest-death-test.cc
index e11f504..3ab9cf9 100644
--- a/src/gtest-death-test.cc
+++ b/src/gtest-death-test.cc
@@ -182,15 +182,19 @@ static String DeathTestThreadWarning(size_t thread_count) {
// Flag characters for reporting a death test that did not die.
static const char kDeathTestLived = 'L';
static const char kDeathTestReturned = 'R';
+static const char kDeathTestThrew = 'T';
static const char kDeathTestInternalError = 'I';
-// An enumeration describing all of the possible ways that a death test
-// can conclude. DIED means that the process died while executing the
-// test code; LIVED means that process lived beyond the end of the test
-// code; and RETURNED means that the test statement attempted a "return,"
-// which is not allowed. IN_PROGRESS means the test has not yet
-// concluded.
-enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED };
+// An enumeration describing all of the possible ways that a death test can
+// conclude. DIED means that the process died while executing the test
+// code; LIVED means that process lived beyond the end of the test code;
+// RETURNED means that the test statement attempted to execute a return
+// statement, which is not allowed; THREW means that the test statement
+// returned control by throwing an exception. IN_PROGRESS means the test
+// has not yet concluded.
+// TODO(vladl@google.com): Unify names and possibly values for
+// AbortReason, DeathTestOutcome, and flag characters above.
+enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW };
// Routine for aborting the program which is safe to call from an
// exec-style death test child process, in which case the error
@@ -388,6 +392,9 @@ void DeathTestImpl::ReadAndInterpretStatusByte() {
case kDeathTestReturned:
set_outcome(RETURNED);
break;
+ case kDeathTestThrew:
+ set_outcome(THREW);
+ break;
case kDeathTestLived:
set_outcome(LIVED);
break;
@@ -416,7 +423,9 @@ void DeathTestImpl::Abort(AbortReason reason) {
// it finds any data in our pipe. So, here we write a single flag byte
// to the pipe, then exit.
const char status_ch =
- reason == TEST_DID_NOT_DIE ? kDeathTestLived : kDeathTestReturned;
+ reason == TEST_DID_NOT_DIE ? kDeathTestLived :
+ reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned;
+
GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1));
// We are leaking the descriptor here because on some platforms (i.e.,
// when built as Windows DLL), destructors of global objects will still
@@ -434,8 +443,8 @@ void DeathTestImpl::Abort(AbortReason reason) {
//
// Private data members:
// outcome: An enumeration describing how the death test
-// concluded: DIED, LIVED, or RETURNED. The death test fails
-// in the latter two cases.
+// concluded: DIED, LIVED, THREW, or RETURNED. The death test
+// fails in the latter three cases.
// status: The exit status of the child process. On *nix, it is in the
// in the format specified by wait(2). On Windows, this is the
// value supplied to the ExitProcess() API or a numeric code
@@ -466,6 +475,10 @@ bool DeathTestImpl::Passed(bool status_ok) {
buffer << " Result: failed to die.\n"
<< " Error msg: " << error_message;
break;
+ case THREW:
+ buffer << " Result: threw an exception.\n"
+ << " Error msg: " << error_message;
+ break;
case RETURNED:
buffer << " Result: illegal return in test statement.\n"
<< " Error msg: " << error_message;
diff --git a/test/gtest-death-test_ex_test.cc b/test/gtest-death-test_ex_test.cc
new file mode 100644
index 0000000..71cc7a3
--- /dev/null
+++ b/test/gtest-death-test_ex_test.cc
@@ -0,0 +1,93 @@
+// Copyright 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vladl@google.com (Vlad Losev)
+//
+// Tests that verify interaction of exceptions and death tests.
+
+#include "gtest/gtest-death-test.h"
+#include "gtest/gtest.h"
+
+#if GTEST_HAS_DEATH_TEST
+
+#if GTEST_HAS_SEH
+#include <windows.h> // For RaiseException().
+#endif
+
+#include "gtest/gtest-spi.h"
+
+#if GTEST_HAS_EXCEPTIONS
+
+#include <exception> // For std::exception.
+
+// Tests that death tests report thrown exceptions as failures and that the
+// exceptions do not escape death test macros.
+TEST(CxxExceptionDeathTest, ExceptionIsFailure) {
+ try {
+ EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(throw 1, ""), "threw an exception");
+ } catch (...) { // NOLINT
+ FAIL() << "An exception escaped a death test macro invocation "
+ << "with catch_exceptions "
+ << (testing::GTEST_FLAG(catch_exceptions) ? "enabled" : "disabled");
+ }
+}
+
+class TestException : public std::exception {
+ public:
+ virtual const char* what() const throw() { return "exceptional message"; }
+};
+
+TEST(CxxExceptionDeathTest, PrintsMessageForStdExceptions) {
+ // Verifies that the exception message is quoted in the failure text.
+ EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(throw TestException(), ""),
+ "exceptional message");
+ // Verifies that the location is mentioned in the failure text.
+ EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(throw TestException(), ""),
+ "gtest-death-test_ex_test.cc");
+}
+#endif // GTEST_HAS_EXCEPTIONS
+
+#if GTEST_HAS_SEH
+// Tests that enabling interception of SEH exceptions with the
+// catch_exceptions flag does not interfere with SEH exceptions being
+// treated as death by death tests.
+TEST(SehExceptionDeasTest, CatchExceptionsDoesNotInterfere) {
+ EXPECT_DEATH(RaiseException(42, 0x0, 0, NULL), "")
+ << "with catch_exceptions "
+ << (testing::GTEST_FLAG(catch_exceptions) ? "enabled" : "disabled");
+}
+#endif
+
+#endif // GTEST_HAS_DEATH_TEST
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ testing::GTEST_FLAG(catch_exceptions) = GTEST_ENABLE_CATCH_EXCEPTIONS_ != 0;
+ return RUN_ALL_TESTS();
+}
diff --git a/test/gtest-death-test_test.cc b/test/gtest-death-test_test.cc
index 2f1d385..b83b0db 100644
--- a/test/gtest-death-test_test.cc
+++ b/test/gtest-death-test_test.cc
@@ -538,15 +538,18 @@ TEST_F(TestForDeathTest, SingleEvaluation) {
}
// Tests that run-away death tests are reported as failures.
-TEST_F(TestForDeathTest, Runaway) {
+TEST_F(TestForDeathTest, RunawayIsFailure) {
EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(static_cast<void>(0), "Foo"),
"failed to die.");
+}
+// Tests that death tests report executing 'return' in the statement as
+// failure.
+TEST_F(TestForDeathTest, ReturnIsFailure) {
EXPECT_FATAL_FAILURE(ASSERT_DEATH(return, "Bar"),
"illegal return in test statement.");
}
-
// Tests that EXPECT_DEBUG_DEATH works as expected,
// that is, in debug mode, it:
// 1. Asserts on death.