Search code examples
c++unit-testinggoogletest

How to catch segmentation fault with Google Test?


How do I test that a function won't produce a segmentation fault?

Here what I know right now, I can do:

EXPECT_DEATH(foo(nullParameter))

In side the function, a segmentation fault is produce which is the behavior that I want to make fail. The snippet above will make the test pass because that is what is expected, the death of the process.

Now, how can I make it fail?


Solution

  • Here's a function that will segfault if passed a null pointer argument and otherwise not:

    int deref(int * pint)
    {
        return *pint;
    }
    

    And here is a googletest program that tests that behaviour:

    main.cpp

    #include <gtest/gtest.h>
    
    int deref(int * pint)
    {
        return *pint;
    }
    
    
    TEST(test_deref_1,will_segfault)
    {
        ASSERT_EXIT((deref(nullptr),exit(0)),::testing::KilledBySignal(SIGSEGV),".*");
    }
    
    
    TEST(test_dref_2,will_not_segfault)
    {
        int i = 42;
        ASSERT_EXIT((deref(&i),exit(0)),::testing::ExitedWithCode(0),".*");
    }
    
    
    int main(int argc, char **argv) {
        ::testing::InitGoogleTest(&argc, argv);
        return RUN_ALL_TESTS();
    }
    

    Compile and link:

    $ g++ -Wall -Wextra -pedantic -o tester main.cpp -pthread -lgtest
    

    Run:

    $ ./tester 
    [==========] Running 2 tests from 2 test cases.
    [----------] Global test environment set-up.
    [----------] 1 test from test_deref_1
    [ RUN      ] test_deref_1.will_segfault
    [       OK ] test_deref_1.will_segfault (168 ms)
    [----------] 1 test from test_deref_1 (168 ms total)
    
    [----------] 1 test from test_dref_2
    [ RUN      ] test_dref_2.will_not_segfault
    [       OK ] test_dref_2.will_not_segfault (1 ms)
    [----------] 1 test from test_dref_2 (1 ms total)
    
    [----------] Global test environment tear-down
    [==========] 2 tests from 2 test cases ran. (169 ms total)
    [  PASSED  ] 2 tests.
    

    As far as I can imagine, TEST(test_deref_1,will_segfault) is a pointless test, because I cannot think of any circumstances in which I would want to assure myself that a program will segfault as a result of making a certain call to a function I have written.

    TEST(test_dref_2,will_not_segfault) is possibly a useful kind of test. In effect, it is a test that the program:

    int main()
    {
        int i = 42;
        defref(&i);
        exit(0);
    }
    

    will terminate by exit(0) rather than in any premature abnormal way. A better name for this test would probably be TEST(test_dref,does_not_crash), or similar.

    It is a possibly useful kind of test because there could be a significant risk of it failing, if defref was some sufficiently complicated code, and the test suite could report that failure without crashing itself. We can force a failure by rewriting it:

    TEST(test_dref_2,will_not_segfault)
    {
        ASSERT_EXIT((deref(nullptr),exit(0)),::testing::ExitedWithCode(0),".*");
    }
    

    and then test test report is:

    $ ./tester
    [==========] Running 2 tests from 2 test cases.
    [----------] Global test environment set-up.
    [----------] 1 test from test_deref_1
    [ RUN      ] test_deref_1.will_segfault
    [       OK ] test_deref_1.will_segfault (147 ms)
    [----------] 1 test from test_deref_1 (147 ms total)
    
    [----------] 1 test from test_dref_2
    [ RUN      ] test_dref_2.will_not_segfault
    main.cpp:25: Failure
    Death test: (deref(nullptr),exit(0))
        Result: died but not with expected exit code:
                Terminated by signal 11 (core dumped)
    Actual msg:
    [  DEATH   ] 
    [  FAILED  ] test_dref_2.will_not_segfault (90 ms)
    [----------] 1 test from test_dref_2 (90 ms total)
    
    [----------] Global test environment tear-down
    [==========] 2 tests from 2 test cases ran. (237 ms total)
    [  PASSED  ] 1 test.
    [  FAILED  ] 1 test, listed below:
    [  FAILED  ] test_dref_2.will_not_segfault
    
     1 FAILED TEST
    

    See the documentation of {ASSERT|EXPECT}_EXIT to understand these macros.