Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

write death test to verify std::set_terminate behavior

I'm using gtest to write some unit tests for exception handling code. As a simple example, let's say I want my program to abort on any unhandled exception, so I create the following test:

TEST_F(myFixture, myTest)
{
    std::set_terminate([](){std::abort(); });

    EXPECT_DEATH(throw std::runtime_error("terminate"), ".*");
}

I would expect this to succeed, since abort should be called on the program. However, my actual result is:

error: Death test: throw std::runtime_error("terminate")

Result: threw an exception.
Error msg:

[ DEATH ]
[ DEATH ] main.cpp(122):: Caught std::exception-derived exception escaping the death test statement. Exception message: terminate

[ DEATH ]

I think gtest's exception handlers are getting in the way. I've tried disabling them using the GTEST_CATCH_EXCEPTIONS environment variable, and the --gtest_catch_exceptions=0 command line flag as mentioned in the advanced guide, but I get the same result.

Am I doing something wrong, or is "death by exception" not something gtest can test for?

like image 921
Nicolas Holthaus Avatar asked Aug 30 '16 18:08

Nicolas Holthaus


1 Answers

Am I doing something wrong, or is "death by exception" not something gtest can test for?

What you're doing is wrong, because "death by exception" is not something gtest can test for.

EXPECT_DEATH(statement, regex);

verifies that statement calls abort or exit and causes output matching regex. The statement throw std::runtime_error("terminate") doesn't call abort or exit, as can be seen from the possibility of catching any exception in the program and letting it carry on. If an exception is not caught then the runtime library will call terminate, ending the program one way or another. But this outcome is not determined by any throw statement.

See the documentation of death-tests, in particular:

...any test that checks that a program terminates (except by throwing an exception) in an expected fashion is also a death test.

Note that if a piece of code throws an exception, we don't consider it "death" for the purpose of death tests, as the caller of the code could catch the exception and avoid the crash. If you want to verify exceptions thrown by your code, see Exception Assertions.

and:

Note that a death test only cares about three things:

  1. does statement abort or exit the process? ...

I appreciate you may be oversimplfying your real issue here but you hardly need to test that unhandled exceptions will result in terminate being called, or that terminate will call the operative terminate- handler, unless it is your daunting duty to test the implementation of your compiler and standard library. Maybe more of the real issue should be aired?

Later

my problem with this is my program doesn't (or shouldn't) carry on. It should abort via the termination handler.

It should, but per documentation the expectation that your program should not carry on is not tested by EXPECT_DEATH on any throw statement, because no throw statement terminates the program.

What I really want to test is that the behavior of my set_terminate handler is correct.

The behaviour of your set_terminate handler is correct if any only if, having set that handler, a call to std::terminate() has the outcome you expect. This question is independent of any throw statement and you may test it like:

#include <iostream>
#include <gtest/gtest.h>
#include <exception>


TEST(the_end, is_nigh)
{
    std::set_terminate([](){std::cout << "Goodbye cruel world\n", std::abort(); });

    EXPECT_DEATH(std::terminate(), ".*");
}
int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

which produces:

[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from the_end
[ RUN      ] the_end.is_nigh
Goodbye cruel world
[       OK ] the_end.is_nigh (172 ms)
[----------] 1 test from the_end (172 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (172 ms total)
[  PASSED  ] 1 test.
like image 57
Mike Kinghan Avatar answered Oct 22 '22 12:10

Mike Kinghan