Add ability to throw from ASSERT

while not losing benefits of EXPECT, and not killing the whole test,
as with --gtest_throw_on_failure.

183822976
This commit is contained in:
Alexey Sokolov 2018-02-03 23:36:19 +00:00
parent ea31cb15f0
commit 092d088533
5 changed files with 171 additions and 8 deletions

View File

@ -872,13 +872,33 @@ TEST(FooTest, Bar) {
} }
``` ```
Since we don't use exceptions, it is technically impossible to To alleviate this, gUnit provides three different solutions. You could use
implement the intended behavior here. To alleviate this, Google Test either exceptions, the `(ASSERT|EXPECT)_NO_FATAL_FAILURE` assertions or the
provides two solutions. You could use either the `HasFatalFailure()` function. They are described in the following two
`(ASSERT|EXPECT)_NO_FATAL_FAILURE` assertions or the
`HasFatalFailure()` function. They are described in the following two
subsections. subsections.
#### Asserting on Subroutines with an exception
The following code can turn ASSERT-failure into an exception:
```c++
class ThrowListener : public testing::EmptyTestEventListener {
void OnTestPartResult(const testing::TestPartResult& result) override {
if (result.type() == testing::TestPartResult::kFatalFailure) {
throw testing::AssertionException(result);
}
}
};
int main(int argc, char** argv) {
...
testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener);
return RUN_ALL_TESTS();
}
```
This listener should be added after other listeners if you have any, otherwise
they won't see failed `OnTestPartResult`.
### Asserting on Subroutines ### ### Asserting on Subroutines ###
As shown above, if your test calls a subroutine that has an `ASSERT_*` As shown above, if your test calls a subroutine that has an `ASSERT_*`

View File

@ -138,7 +138,7 @@ GTEST_DECLARE_int32_(stack_trace_depth);
// When this flag is specified, a failed assertion will throw an // When this flag is specified, a failed assertion will throw an
// exception if exceptions are enabled, or exit the program with a // exception if exceptions are enabled, or exit the program with a
// non-zero code otherwise. // non-zero code otherwise. For use with an external test framework.
GTEST_DECLARE_bool_(throw_on_failure); GTEST_DECLARE_bool_(throw_on_failure);
// When this flag is set with a "host:port" string, on supported // When this flag is set with a "host:port" string, on supported
@ -1004,6 +1004,18 @@ class Environment {
virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }
}; };
#if GTEST_HAS_EXCEPTIONS
// Exception which can be thrown from TestEventListener::OnTestPartResult.
class GTEST_API_ AssertionException
: public internal::GoogleTestFailureException {
public:
explicit AssertionException(const TestPartResult& result)
: GoogleTestFailureException(result) {}
};
#endif // GTEST_HAS_EXCEPTIONS
// The interface for tracing execution of tests. The methods are organized in // The interface for tracing execution of tests. The methods are organized in
// the order the corresponding events are fired. // the order the corresponding events are fired.
class TestEventListener { class TestEventListener {
@ -1032,6 +1044,8 @@ class TestEventListener {
virtual void OnTestStart(const TestInfo& test_info) = 0; virtual void OnTestStart(const TestInfo& test_info) = 0;
// Fired after a failed assertion or a SUCCEED() invocation. // Fired after a failed assertion or a SUCCEED() invocation.
// If you want to throw an exception from this function to skip to the next
// TEST, it must be AssertionException defined above, or inherited from it.
virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0;
// Fired after the test ends. // Fired after the test ends.

View File

@ -293,7 +293,7 @@ GTEST_DEFINE_bool_(
internal::BoolFromGTestEnv("throw_on_failure", false), internal::BoolFromGTestEnv("throw_on_failure", false),
"When this flag is specified, a failed assertion will throw an exception " "When this flag is specified, a failed assertion will throw an exception "
"if exceptions are enabled or exit the program with a non-zero code " "if exceptions are enabled or exit the program with a non-zero code "
"otherwise."); "otherwise. For use with an external test framework.");
#if GTEST_USE_OWN_FLAGFILE_FLAG_ #if GTEST_USE_OWN_FLAGFILE_FLAG_
GTEST_DEFINE_string_( GTEST_DEFINE_string_(
@ -2435,6 +2435,8 @@ Result HandleExceptionsInMethodIfSupported(
#if GTEST_HAS_EXCEPTIONS #if GTEST_HAS_EXCEPTIONS
try { try {
return HandleSehExceptionsInMethodIfSupported(object, method, location); return HandleSehExceptionsInMethodIfSupported(object, method, location);
} catch (const AssertionException&) { // NOLINT
// This failure was reported already.
} catch (const internal::GoogleTestFailureException&) { // NOLINT } catch (const internal::GoogleTestFailureException&) { // NOLINT
// This exception type can only be thrown by a failed Google // This exception type can only be thrown by a failed Google
// Test assertion with the intention of letting another testing // Test assertion with the intention of letting another testing
@ -5201,7 +5203,8 @@ static const char kColorEncodedHelpMessage[] =
" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" " @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n"
" Turn assertion failures into debugger break-points.\n" " Turn assertion failures into debugger break-points.\n"
" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" " @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n"
" Turn assertion failures into C++ exceptions.\n" " Turn assertion failures into C++ exceptions for use by an external\n"
" test framework.\n"
" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n" " @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n"
" Do not report exceptions as test failures. Instead, allow them\n" " Do not report exceptions as test failures. Instead, allow them\n"
" to crash the program or throw a pop-up (on Windows).\n" " to crash the program or throw a pop-up (on Windows).\n"

View File

@ -219,6 +219,13 @@ py_test(
deps = [":gtest_test_utils"], deps = [":gtest_test_utils"],
) )
cc_test(
name = "gtest_assert_by_exception_test",
size = "small",
srcs = ["gtest_assert_by_exception_test.cc"],
deps = ["//:gtest"],
)
cc_binary( cc_binary(
name = "gtest_throw_on_failure_test_", name = "gtest_throw_on_failure_test_",
testonly = 1, testonly = 1,

View File

@ -0,0 +1,119 @@
// Copyright 2009, 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: wan@google.com (Zhanyong Wan)
// Tests Google Test's assert-by-exception mode with exceptions enabled.
#include "gtest/gtest.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdexcept>
class ThrowListener : public testing::EmptyTestEventListener {
void OnTestPartResult(const testing::TestPartResult& result) override {
if (result.type() == testing::TestPartResult::kFatalFailure) {
throw testing::AssertionException(result);
}
}
};
// Prints the given failure message and exits the program with
// non-zero. We use this instead of a Google Test assertion to
// indicate a failure, as the latter is been tested and cannot be
// relied on.
void Fail(const char* msg) {
printf("FAILURE: %s\n", msg);
fflush(stdout);
exit(1);
}
static void AssertFalse() {
ASSERT_EQ(2, 3) << "Expected failure";
}
// Tests that an assertion failure throws a subclass of
// std::runtime_error.
TEST(Test, Test) {
// A successful assertion shouldn't throw.
try {
EXPECT_EQ(3, 3);
} catch(...) {
Fail("A successful assertion wrongfully threw.");
}
// A successful assertion shouldn't throw.
try {
EXPECT_EQ(3, 4);
} catch(...) {
Fail("A failed non-fatal assertion wrongfully threw.");
}
// A failed assertion should throw.
try {
AssertFalse();
} catch(const testing::AssertionException& e) {
if (strstr(e.what(), "Expected failure") != NULL)
throw;
printf("%s",
"A failed assertion did throw an exception of the right type, "
"but the message is incorrect. Instead of containing \"Expected "
"failure\", it is:\n");
Fail(e.what());
} catch(...) {
Fail("A failed assertion threw the wrong type of exception.");
}
Fail("A failed assertion should've thrown but didn't.");
}
int kTestForContinuingTest = 0;
TEST(Test, Test2) {
// FIXME(sokolov): how to force Test2 to be after Test?
kTestForContinuingTest = 1;
}
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener);
int result = RUN_ALL_TESTS();
if (result == 0) {
printf("RUN_ALL_TESTS returned %d\n", result);
Fail("Expected failure instead.");
}
if (kTestForContinuingTest == 0) {
Fail("Should have continued with other tests, but did not.");
}
return 0;
}