0

With google tests, let's assume the following code

#include <iostream>

using namespace std;
using MyFunc = void (*)(void);

void foo_robust(MyFunc f)   { if(f != nullptr) (*f)(); }
void foo_not_robust(MyFunc f)   { (*f)(); }
void print(void)     { cout << "hello world" << endl; }

int main()
{
    foo(&print); //works
    foo(nullptr); //runtime error ?
    return 0;
}

When using google test,

I can do:

TEST(TestAssertDeath, Death)
{
    EXPECT_DEATH(foo(&print)); //will return FAILED, because does not die.
    EXPECT_DEATH(foo(nullptr)); //will return FAILED if foo robust, OK otherwise (UB: it might even return FAILED)
}

I want to do:

TEST(TestAssertDeath, No_Death)
{
    EXPECT_NO_DEATH(foo(&print)); //will return OK, because does not die.
    EXPECT_NO_DEATH(foo(nullptr)); // will return OK, if foo is robust, FAILED or DEATH otherwise (UB: it might even return OK)
}

Is there any google macro that does "EXPECT_NO_DEATH" 's job? I tried EXPECT_NO_FATAL_FAILURE but it crashes as if I put nothing. I want to do:

TEST(TestAssertDeath, No_Death)
{
    EXPECT_NO_FATAL_FAILURE(foo(&print)); //will return OK, because does not die.
    EXPECT_NO_FATAL_FAILURE(foo(nullptr)); // will crash (UB: might even return OK)
}

But I don't want the test campaing to be stopped.

Google test gives me currently the following error.

[ RUN      ] MyClass.MyUnitTestA
[      OK  ]
[ RUN      ] MyClass.MyUnitTestB
[      OK  ]
[ RUN      ] MyClass.MyUnitTestC
mingw32-make[3]: *** [CMakeFiles/myproject-coverage] Error -1073741819
mingw32-make[2]: *** [CMakeFiles/myproject-coverage.dir/all] Error 2
mingw32-make[1]: *** [CMakeFiles/myproject-coverage.dir/rule] Error 2
mingw32-make: *** [myproject-coverage] Error 2

Because this stops the other tests from running, I would like something as the following if the code is not robust

[ RUN      ] MyClass.MyUnitTestA
[      OK  ]
[ RUN      ] MyClass.MyUnitTestB
[      OK  ]
[ RUN      ] MyClass.MyUnitTestC
[  DEATH   ] or [  FAILED  ]
[ RUN      ] MyClass.MyUnitTestD
[      OK  ]

and this if the code is robust:

[ RUN      ] MyClass.MyUnitTestA
[      OK  ]
[ RUN      ] MyClass.MyUnitTestB
[      OK  ]
[ RUN      ] MyClass.MyUnitTestC
[      OK  ]
[ RUN      ] MyClass.MyUnitTestD
[      OK  ]

Important note: I know that the line foo_not_robust(nullptr) is UB and will not ends up in a crash automatically, but if it does, I want this line to be skipped and marked as failed.

Guillaume D
  • 2,202
  • 2
  • 10
  • 37
  • 2
    why do you think you need that? When you have a runtime error your test will not pass, no need to do something extra for that. In a sense you always have an implicit `EXPECT_NO_DEATH` – 463035818_is_not_an_ai Aug 01 '19 at 15:42
  • 2
    Testing for UB like this is a bad idea. [Here is why](https://gcc.godbolt.org/z/d9eCOh) (from [here](https://www.reddit.com/r/cpp/comments/6xeqr3/compiler_undefined_behavior_calls_nevercalled/) because I can't seem to find the article). – Max Langhof Aug 01 '19 at 15:43
  • if the test do not pass i don't have the full campaign result which is what I want – Guillaume D Aug 01 '19 at 15:43
  • I want to be sure that the module I'm testing is robust against nullptr – Guillaume D Aug 01 '19 at 15:44
  • 1
    `EXPECT_DEATH` is only meant for just that... Expecting a crash or error. You should only be using it when you WANT that line to crash, or when you're actually EXPECTING it to crash. Using it this way assumes failure, when you actually want it to pass and be "robust" against your condition. – Spevacus Aug 01 '19 at 15:47
  • rephrasing: I want my code line, with strange inputs, NOT TO CRASH, but it will if the code is not robust enough, and I want the test campaign to carry on. – Guillaume D Aug 01 '19 at 15:50
  • Just execute the code normally, don't test for any specific result value and if it crashes, the test fails... am I missing something? – grek40 Aug 01 '19 at 16:01
  • Did you already try things like `EXPECT_NO_FATAL_FAILURE`? – grek40 Aug 02 '19 at 06:01
  • "robust against nullptr" -> don't write C++. You simply *can't* expect a particular result from undefined behaviour. *Anything* is allowed, including passing your tests *then* destroying your data – Caleth Aug 02 '19 at 07:14
  • @grek40 yes I tried EXPECT_NO_FATAL_FAILURE but it crashes – Guillaume D Aug 02 '19 at 09:55
  • I'm saying the rules of C++ are incompatible with "write whatever, then write tests". `EXPECT_DEATH(foo(nullptr));` is expecting something that *isn't promised* – Caleth Aug 02 '19 at 10:06
  • I don't want to do EXPECT_DEATH, i want EXPECT_NO_DEATH. I want to run the test, call a function which can crashes the test campaign, but not stop the test campaign and mark this test as failed if it should have crashed. – Guillaume D Aug 02 '19 at 10:07
  • `EXPECT_NO_DEATH(foo(nullptr));` is *also* expecting something that isn't promised. You simply cannot test for undefined behaviour. `foo(nullptr);` might very easily emit "hello world" – Caleth Aug 02 '19 at 10:10
  • if it emits "hello world" then the test passes. This is what I want. – Guillaume D Aug 02 '19 at 10:13
  • No, unexpected behavior doesn't mean that if on first run it prints "hello world" then it will do this always, it means that on next run it can crash and on third order pizza or something. – sklott Aug 02 '19 at 10:20
  • I think you are both guessing too much on how my real class is implemented. – Guillaume D Aug 02 '19 at 10:21
  • C++ **does not define** what happens with `foo_not_robust(nullptr)`. There is no behaviour that you can rely on or test for – Caleth Aug 02 '19 at 10:21
  • I don't want to test if it does the right thing while calling (eg "helloworld" or ordering a pizza), I just want to skip this line if it crashes. – Guillaume D Aug 02 '19 at 10:24
  • If you have `foo_not_robust(nullptr)` in your test suite, C++ does not define what *the entire test suite* does. You don't seem to understand the seriousness of undefined behaviour, which is why I recommend you *don't write C++* – Caleth Aug 02 '19 at 10:28
  • >.< I don't want to call foo_not_robust.... but it's a unit test, I'm calling foo, if foo is not robust it will (potentially) crash. – Guillaume D Aug 02 '19 at 11:31

1 Answers1

2

To make unit tests robust against crashing you could try to run each test in a child process, with a parent process that monitors the child process for success, failure or crash.

But there is an easy way, in fact you can do something as EXPECT_NO_DEATH

From google test doc:

TEST(MyDeathTest, NormalExit) { EXPECT_EXIT(NormalExit(), ::testing::ExitedWithCode(0), "Success"); }

You can hack it by using two statements (statement1,statement2) with statement1 = statement and statement2 = exit(0)

It gives the two following custom macros:

# define EXPECT_CRASH(statement) \
    EXPECT_EXIT((statement,exit(0)),::testing::KilledBySignal(SIGSEGV),".*")
# define EXPECT_NO_CRASH(statement) \
   EXPECT_EXIT((statement,exit(0)),::testing::ExitedWithCode(0),".*")

EXPECT_CRASH() is equivalent to EXPECT_DEATH()

EXPECT_NO_CRASH() is equivalent the requested EXPECT_NO_DEATH()

Note that ::testing::KilledBySignal(signal_number) is not available on Windows. What you could do as a workaround for Windows is just defining:

# define EXPECT_CRASH(statement) \
    EXPECT_DEATH(statement,".*")

gives the following message:

[ RUN      ] MyClass.MyUnitTestA
[      OK  ]
[ RUN      ] MyClass.MyUnitTestB
[      OK  ]
[ RUN      ] MyClass.MyUnitTestC
Death test: (foo(),exit(0))
    Result: died but not with expected exit code:
            Exited with exit status -1073741819
Actual msg:
[  DEATH   ]
[ RUN      ] MyClass.MyUnitTestD
[      OK  ]
Guillaume D
  • 2,202
  • 2
  • 10
  • 37