Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to test that an abort-routine doesn't return?

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).

like image 652
embedc Avatar asked Apr 19 '19 10:04

embedc


3 Answers

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");
}
like image 101
PSkocik Avatar answered Oct 26 '22 13:10

PSkocik


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

like image 33
Fire Lancer Avatar answered Oct 26 '22 12:10

Fire Lancer


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

like image 33
Paul Ogilvie Avatar answered Oct 26 '22 14:10

Paul Ogilvie