Our C codebase is using assert to check that preconditions/post conditions are being met.
#include <cstdlib>
#include <cassert>
void Aborting_Function(int n);
int main(){
Aborting_Function(1); //good
Aborting_Function(0); //calls std::abort()
//Is it possible to recover somehow?
//And continue on...
}
void Aborting_Function(int n){
assert(n > 0);
//impl...
}
In unit testing, I want to verify that functions are properly following their contracts
(aborting when they should).
Is it possible to recover from std::abort?
I realize it seems somewhat repetitive to have unit tests check exactly same thing that the assertions should be checking, but this would be helpful as we could automate the checking of particular use cases that should not work.
Short answer, "no".
Rather than subverting abort(), you may want to consider using the google test framework.
This has the DEATH_TEST (documentation here: https://github.com/google/googletest/blob/master/googletest/docs/V1_7_AdvancedGuide.md)
Essentially what this does is fork a child process and checks whether the statement causes it to exit (which it would do if it aborted).
For unit testing one can substitute abort() and longjmp() from there, or when testing via C++ throwing from there.
For example (expressed as C++):
#include <cassert>
#include <csetjmp>
#include <stdexcept>
#include <iostream>
#ifndef lest_ABORT_SIGNATURE
# if _MSC_VER
# define lest_NORETURN __declspec(noreturn)
# define lest_ABORT_SIGNATURE() _ACRTIMP lest_NORETURN void __cdecl abort(void)
# else
# define lest_NORETURN [[noreturn]]
# define lest_ABORT_SIGNATURE() lest_NORETURN void __cdecl abort()
# endif
#else
# ifndef lest_NORETURN
# define lest_NORETURN
# endif
#endif
#if USE_LONGJMP
jmp_buf env;
lest_ABORT_SIGNATURE()
{
std::longjmp( env, 1 );
}
#else
struct Abort{};
lest_NORETURN void my_abort()
{
throw Abort{};
}
lest_ABORT_SIGNATURE()
{
// throw indirectly and prevent warning in VC14:
my_abort();
}
#endif
int main()
{
#if USE_LONGJMP
if ( ! setjmp( env ) )
{
std::cout << "assert(false):\n";
assert( false );
}
else
{
std::cout << "Intercepted abort\n";
}
#else
try
{
std::cout << "assert(false):\n";
assert( false );
}
catch ( Abort const & )
{
std::cout << "Caught Abort\n";
}
catch ( std::exception const & e )
{
std::cout << "Exception: " << e.what() << "\n";
}
#endif
std::cout << "End\n";
}
#if 0
cl -EHsc -DUSE_LONGJMP=1 abort-own.cpp && abort-own.exe
g++ -Wall -DUSE_LONGJMP=1 -std=c++11 -o abort-own.exe abort-own.cpp && abort-own.exe
#endif
Compiling with VC14 (VS2015) and runnning with:
cl -EHsc -DUSE_LONGJMP=1 abort-own.cpp && abort-own.exe
yields the following output:
...
assert(false):
Assertion failed: false, file abort-own.cpp, line 45
Intercepted abort
End
Compiling with a pre-VC14 compiler produces a link error:
LIBCMT.lib(abort.obj) : error LNK2005: _abort already defined in {file}
{exe} : fatal error LNK1169: one or more multiply defined symbols found
This may be cured by:
/link /FORCE:MULTIPLE when compiling;abort() removed.Compiling via g++ using -std=c++03 or -std=c++11 does not lead to a multiply defined symbol.
More elaborate versions of the code above I'm developing for the lest test framework:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With