Modifies handling of C++ exceptions in death tests to treat exceptions escaping them as failures.
This commit is contained in:
parent
2c81010523
commit
50f4deb1cf
@ -147,6 +147,13 @@ if (gtest_build_tests)
|
|||||||
cxx_library(gtest_main_no_rtti "${cxx_no_rtti}"
|
cxx_library(gtest_main_no_rtti "${cxx_no_rtti}"
|
||||||
src/gtest-all.cc src/gtest_main.cc)
|
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}"
|
cxx_test_with_flags(gtest_no_rtti_unittest "${cxx_no_rtti}"
|
||||||
gtest_main_no_rtti test/gtest_unittest.cc)
|
gtest_main_no_rtti test/gtest_unittest.cc)
|
||||||
|
|
||||||
|
@ -39,6 +39,8 @@
|
|||||||
|
|
||||||
#include "gtest/internal/gtest-internal.h"
|
#include "gtest/internal/gtest-internal.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
namespace testing {
|
namespace testing {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
@ -96,8 +98,12 @@ class GTEST_API_ DeathTest {
|
|||||||
// test, then wait for it to complete.
|
// test, then wait for it to complete.
|
||||||
enum TestRole { OVERSEE_TEST, EXECUTE_TEST };
|
enum TestRole { OVERSEE_TEST, EXECUTE_TEST };
|
||||||
|
|
||||||
// An enumeration of the two reasons that a test might be aborted.
|
// An enumeration of the three reasons that a test might be aborted.
|
||||||
enum AbortReason { TEST_ENCOUNTERED_RETURN_STATEMENT, TEST_DID_NOT_DIE };
|
enum AbortReason {
|
||||||
|
TEST_ENCOUNTERED_RETURN_STATEMENT,
|
||||||
|
TEST_THREW_EXCEPTION,
|
||||||
|
TEST_DID_NOT_DIE
|
||||||
|
};
|
||||||
|
|
||||||
// Assumes one of the above roles.
|
// Assumes one of the above roles.
|
||||||
virtual TestRole AssumeRole() = 0;
|
virtual TestRole AssumeRole() = 0;
|
||||||
@ -149,6 +155,29 @@ class DefaultDeathTestFactory : public DeathTestFactory {
|
|||||||
// by a signal, or exited normally with a nonzero exit code.
|
// by a signal, or exited normally with a nonzero exit code.
|
||||||
GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
|
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*,
|
// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
|
||||||
// ASSERT_EXIT*, and EXPECT_EXIT*.
|
// ASSERT_EXIT*, and EXPECT_EXIT*.
|
||||||
#define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \
|
#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: { \
|
case ::testing::internal::DeathTest::EXECUTE_TEST: { \
|
||||||
::testing::internal::DeathTest::ReturnSentinel \
|
::testing::internal::DeathTest::ReturnSentinel \
|
||||||
gtest_sentinel(gtest_dt); \
|
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); \
|
gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
|
||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
|
@ -182,15 +182,19 @@ static String DeathTestThreadWarning(size_t thread_count) {
|
|||||||
// Flag characters for reporting a death test that did not die.
|
// Flag characters for reporting a death test that did not die.
|
||||||
static const char kDeathTestLived = 'L';
|
static const char kDeathTestLived = 'L';
|
||||||
static const char kDeathTestReturned = 'R';
|
static const char kDeathTestReturned = 'R';
|
||||||
|
static const char kDeathTestThrew = 'T';
|
||||||
static const char kDeathTestInternalError = 'I';
|
static const char kDeathTestInternalError = 'I';
|
||||||
|
|
||||||
// An enumeration describing all of the possible ways that a death test
|
// An enumeration describing all of the possible ways that a death test can
|
||||||
// can conclude. DIED means that the process died while executing the
|
// conclude. DIED means that the process died while executing the test
|
||||||
// test code; LIVED means that process lived beyond the end of the test
|
// code; LIVED means that process lived beyond the end of the test code;
|
||||||
// code; and RETURNED means that the test statement attempted a "return,"
|
// RETURNED means that the test statement attempted to execute a return
|
||||||
// which is not allowed. IN_PROGRESS means the test has not yet
|
// statement, which is not allowed; THREW means that the test statement
|
||||||
// concluded.
|
// returned control by throwing an exception. IN_PROGRESS means the test
|
||||||
enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED };
|
// 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
|
// Routine for aborting the program which is safe to call from an
|
||||||
// exec-style death test child process, in which case the error
|
// exec-style death test child process, in which case the error
|
||||||
@ -388,6 +392,9 @@ void DeathTestImpl::ReadAndInterpretStatusByte() {
|
|||||||
case kDeathTestReturned:
|
case kDeathTestReturned:
|
||||||
set_outcome(RETURNED);
|
set_outcome(RETURNED);
|
||||||
break;
|
break;
|
||||||
|
case kDeathTestThrew:
|
||||||
|
set_outcome(THREW);
|
||||||
|
break;
|
||||||
case kDeathTestLived:
|
case kDeathTestLived:
|
||||||
set_outcome(LIVED);
|
set_outcome(LIVED);
|
||||||
break;
|
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
|
// it finds any data in our pipe. So, here we write a single flag byte
|
||||||
// to the pipe, then exit.
|
// to the pipe, then exit.
|
||||||
const char status_ch =
|
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));
|
GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1));
|
||||||
// We are leaking the descriptor here because on some platforms (i.e.,
|
// We are leaking the descriptor here because on some platforms (i.e.,
|
||||||
// when built as Windows DLL), destructors of global objects will still
|
// when built as Windows DLL), destructors of global objects will still
|
||||||
@ -434,8 +443,8 @@ void DeathTestImpl::Abort(AbortReason reason) {
|
|||||||
//
|
//
|
||||||
// Private data members:
|
// Private data members:
|
||||||
// outcome: An enumeration describing how the death test
|
// outcome: An enumeration describing how the death test
|
||||||
// concluded: DIED, LIVED, or RETURNED. The death test fails
|
// concluded: DIED, LIVED, THREW, or RETURNED. The death test
|
||||||
// in the latter two cases.
|
// fails in the latter three cases.
|
||||||
// status: The exit status of the child process. On *nix, it is in the
|
// 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
|
// in the format specified by wait(2). On Windows, this is the
|
||||||
// value supplied to the ExitProcess() API or a numeric code
|
// 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"
|
buffer << " Result: failed to die.\n"
|
||||||
<< " Error msg: " << error_message;
|
<< " Error msg: " << error_message;
|
||||||
break;
|
break;
|
||||||
|
case THREW:
|
||||||
|
buffer << " Result: threw an exception.\n"
|
||||||
|
<< " Error msg: " << error_message;
|
||||||
|
break;
|
||||||
case RETURNED:
|
case RETURNED:
|
||||||
buffer << " Result: illegal return in test statement.\n"
|
buffer << " Result: illegal return in test statement.\n"
|
||||||
<< " Error msg: " << error_message;
|
<< " Error msg: " << error_message;
|
||||||
|
93
test/gtest-death-test_ex_test.cc
Normal file
93
test/gtest-death-test_ex_test.cc
Normal file
@ -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();
|
||||||
|
}
|
@ -538,15 +538,18 @@ TEST_F(TestForDeathTest, SingleEvaluation) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tests that run-away death tests are reported as failures.
|
// 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"),
|
EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(static_cast<void>(0), "Foo"),
|
||||||
"failed to die.");
|
"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"),
|
EXPECT_FATAL_FAILURE(ASSERT_DEATH(return, "Bar"),
|
||||||
"illegal return in test statement.");
|
"illegal return in test statement.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Tests that EXPECT_DEBUG_DEATH works as expected,
|
// Tests that EXPECT_DEBUG_DEATH works as expected,
|
||||||
// that is, in debug mode, it:
|
// that is, in debug mode, it:
|
||||||
// 1. Asserts on death.
|
// 1. Asserts on death.
|
||||||
|
Loading…
Reference in New Issue
Block a user