6

I have to test a library that provides its own abort_routine() function (that calls abort() internally, but the implementation may change).

One of the requirements for this abort_routine() is that it may not return.

I'm wondering if it's possible to test this requirement?

UPDATE: I'm not using gtest, only llvm's lit and stuff like these: return 0, return 1, assert(false).

embedc
  • 1,485
  • 1
  • 6
  • 20

3 Answers3

6

This is a nice use case for fork and I use it myself in my tests.

You can simply fork(), run the function in the child, _exit() the child, reap the result, and if it indicates the process was signaled with SIGABRT, the child aborted, otherwise it didn't.

Example code:

#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

int fork_and_reap(int *Ws, void Fn(void *), void *Arg)
{
    pid_t pid; if (0>(pid=fork())) return -1;
    if(0==pid) (void)Fn(Arg), _exit(0);
    else for(;;){
        if(0>waitpid(pid,Ws,WUNTRACED)){
            if(EINTR==errno) continue;
            else abort();
        }else if(!WIFEXITED(*Ws)&&!WIFSIGNALED(*Ws)){ //shouldn't have stopped
            if(0>kill(pid,SIGTERM) ||0>kill(pid,SIGCONT)) abort();
        }else break;
    }
    return 0;
}
void aborting(void *A){ (void)A; abort(); }
void not_aborting(void *A){ (void)A; }

int main()
{
    int ws;
    if(0<=fork_and_reap(&ws, aborting, 0) && WIFSIGNALED(ws) && WTERMSIG(SIGABRT)) puts("aborted"); else puts("didn't abort");
    if(0<=fork_and_reap(&ws, not_aborting, 0) && WIFSIGNALED(ws) && WTERMSIG(SIGABRT)) puts("aborted"); else puts("didn't abort");
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
1

As a general solution, you could test it by running it as a separate process, something like:

int main()
{
    abort_routine();
    printf("Didn't abort\n");
    return 0;
}

You will be able to see when you run it as a child process if it aborted (printed some abort output instead, non zero exit) or not (printed that output and exited with zero).

This is roughly how the "death tests" in gtest work, https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#how-it-works

Under the hood, ASSERT_EXIT() spawns a new process and executes the death test statement in that process. The details of how precisely that happens depend on the platform

Fire Lancer
  • 29,364
  • 31
  • 116
  • 182
  • Thank you, but... our test system interprets all the files in each folder as tests. So I'm not sure if we can add a special file that isn't actually a test, but a separate process for a test... – embedc Apr 19 '19 at 10:48
  • Give it a special command line, like "--die", so that by default just exits with 0? Or use a single binary with such a condition like gtest describes (I suspect using a command line parameter on windows, like "--death-test-x=this_test" and `fork` on POSIX). – Fire Lancer Apr 19 '19 at 10:51
1

See the _Noreturn keyword:

"The _Noreturn keyword appears in a function declaration and specifies that the function does not return by executing the return statement or by reaching the end of the function body (it may return by executing longjmp). If the function declared _Noreturn returns, the behavior is undefined. A compiler diagnostic is recommended if this can be detected."

If a function is declared as such, the compiler should give a diagnostics message. So you do not need to test it but can inspect the compiler messages and do a code review

https://en.cppreference.com/w/c/language/_Noreturn

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • And for C++11 onwards, the `[[noreturn]]` attribute. https://en.cppreference.com/w/cpp/language/attributes/noreturn – Eljay Apr 19 '19 at 13:40
  • Can we check (using C++ traits or something) that function is declared with attribute [[noreturn]]? – embedc Apr 22 '19 at 03:23