Skip to content

Instantly share code, notes, and snippets.

@martinmoene
Last active January 11, 2016 12:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save martinmoene/50a13f9cba9b041a82b2 to your computer and use it in GitHub Desktop.
Save martinmoene/50a13f9cba9b041a82b2 to your computer and use it in GitHub Desktop.
EXPECT_ABORTS for lest test framework (C++11) - substitute ::abort(), longjmp()
// Death test for abort/assert - substitute ::abort()
//
// Expect_aborts succeeds for ::abort() [pass]
// Expect_aborts succeeds for std::abort() [pass]
// Expect_aborts succeeds for assert(false) [pass]
// Expect_aborts reports assert(true) [fail]
// Expect_aborts succeeds for assert(false) in user noexcept function [pass]
// Expect_aborts reports an unexpected standard exception [fail]
// Expect_aborts reports an unexpected non-standard exception [fail]
// Expect_no_abort succeeds for assert(true) [pass]
// Expect_no_abort reports ::abort() [fail]
// Expect_no_abort reports std::abort() [fail]
// Expect_no_abort reports assert(false) [fail]
// Expect_no_abort reports an unexpected standard exception [fail]
// Expect_no_abort reports an unexpected non-standard exception [fail]
#include <lest.hpp>
// -----------------------------------------------------------------------
// Additions to lest.hpp:
#include <csetjmp>
#include <io.h>
#include <fcntl.h>
#if ! defined( lest_NO_SHORT_MACRO_NAMES )
# define EXPECT_NO_ABORT lest_EXPECT_NO_ABORT
# define EXPECT_ABORTS lest_EXPECT_ABORTS
#endif
#define lest_EXPECT_NO_ABORT( expr ) \
do \
{ \
try \
{ \
lest::scoped_abort_substitute lest_UNIQUE( id ); \
if ( ! setjmp( lest_UNIQUE( id ).env() ) ) \
{ \
expr; \
} \
else \
{ \
throw lest::aborted{ "failed", lest_LOCATION, #expr }; \
} \
} \
catch (...) \
{ \
lest::inform( lest_LOCATION, #expr ); \
} \
if ( lest_env.pass ) \
lest::report( lest_env.os, lest::not_aborted{ "passed", lest_LOCATION, #expr }, lest_env.testing ); \
} while ( lest::is_false() )
#define lest_EXPECT_ABORTS( expr ) \
do \
{ \
try \
{ \
lest::scoped_abort_substitute lest_UNIQUE( id ); \
if ( ! setjmp( lest_UNIQUE( id ).env() ) ) \
{ \
expr; \
} \
else \
{ \
if ( lest_env.pass ) \
lest::report( lest_env.os, lest::aborted{ "passed", lest_LOCATION, #expr }, lest_env.testing ); \
break; \
} \
} \
catch (...) \
{ \
lest::inform( lest_LOCATION, #expr ); \
} \
throw lest::not_aborted{ "failed", lest_LOCATION, #expr }; \
} \
while ( lest::is_false() )
#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 _WIN32
# define lest_DEV_NULL "nul"
# else
# define lest_DEV_NULL "/dev/null"
#endif
namespace lest {
struct aborted : message
{
aborted( text kind, location where, text expr )
: message{ kind + ": aborted", where, expr } {}
};
struct not_aborted : message
{
not_aborted( text kind, location where, text expr )
: message{ kind + ": didn't abort", where, expr } {}
};
// substitute for ::abort(),
// inhibit/restore output to stderr to suppress output of assert().
// non-thread-safe
class scoped_abort_substitute
{
public:
scoped_abort_substitute()
{
inhibit_stderr();
}
~scoped_abort_substitute()
{
restore_stderr();
}
lest_NORETURN static void abort()
{
std::longjmp( env(), 1 );
}
static jmp_buf & env()
{
static jmp_buf buf;
return buf;
}
private:
void inhibit_stderr()
{
fflush( stderr );
stderr_org = dup( 2 );
stderr_new = open( lest_DEV_NULL, O_WRONLY );
dup2 ( stderr_new, 2 );
close( stderr_new );
}
void restore_stderr()
{
fflush( stderr );
dup2 ( stderr_org, 2 );
close ( stderr_org );
}
int stderr_org = -1;
int stderr_new = -1;
};
} // namespace lest
// substitute ::abort():
lest_ABORT_SIGNATURE()
{
lest::scoped_abort_substitute::abort();
}
// -----------------------------------------------------------------------
// Usage:
#include <cassert>
#define CASE( name ) lest_CASE( specification, name )
static lest::tests specification;
struct user_type{};
void user_noexcept() noexcept
{
assert( false );
}
// report value of __cplusplus:
CASE( "__cplusplus" )
{
EXPECT( __cplusplus == 0 );
}
// test for abort:
CASE( "Expect_aborts succeeds for ::abort() " "[pass]" )
{
EXPECT_ABORTS( ::abort() );
}
CASE( "Expect_aborts succeeds for std::abort() " "[pass]" )
{
EXPECT_ABORTS( std::abort() );
}
CASE( "Expect_aborts succeeds for assert(false) " "[pass]" )
{
EXPECT_ABORTS( assert( false ) );
}
CASE( "Expect_aborts reports assert(true) " "[fail]" )
{
EXPECT_ABORTS( assert( true ) );
}
CASE( "Expect_aborts succeeds for assert(false) in user noexcept function " "[pass]" )
{
EXPECT_ABORTS( user_noexcept() );
}
CASE( "Expect_aborts reports an unexpected standard exception " "[fail]" )
{
EXPECT_ABORTS( throw std::runtime_error("augh") );
}
CASE( "Expect_aborts reports an unexpected non-standard exception " "[fail]" )
{
EXPECT_ABORTS( throw user_type{} );
}
// test for no abort:
CASE( "Expect_no_abort succeeds for assert(true) " "[pass]" )
{
EXPECT_NO_ABORT( assert( true ) );
}
CASE( "Expect_no_abort reports ::abort() " "[fail]" )
{
EXPECT_NO_ABORT( ::abort() );
}
CASE( "Expect_no_abort reports std::abort() " "[fail]" )
{
EXPECT_NO_ABORT( std::abort() );
}
CASE( "Expect_no_abort reports assert(false) " "[fail]" )
{
EXPECT_NO_ABORT( assert( false ) );
}
CASE( "Expect_no_abort reports an unexpected standard exception " "[fail]" )
{
EXPECT_NO_ABORT( throw std::runtime_error("augh") );
}
CASE( "Expect_no_abort reports an unexpected non-standard exception " "[fail]" )
{
EXPECT_NO_ABORT( throw user_type{} );
}
// run tests:
int main( int argc, char * argv[] )
{
return lest::run( specification, argc, argv /*, std::cout */ );
}
// -----------------------------------------------------------------------
// Compilation:
#if 0
cl -EHsc -Dlest_FEATURE_AUTO_REGISTER=1 -I../../../lest/ main-own-abort.cpp && main-own-abort.exe
g++ -Wall -std=c++11 -Dlest_FEATURE_AUTO_REGISTER=1 -I../../../lest/ -o main-own-abort.exe main-own-abort.cpp && main-own-abort.exe --pass
// -Dlest_ABORT_SIGNATURE()="void __cdecl abort()"
#endif
// Output:
// main-own-abort.cpp:191: failed: __cplusplus: __cplusplus == 0 for 201103 == 0
// main-own-abort.cpp:198: passed: aborted: Expect_aborts succeeds for ::abort() [pass]: ::abort()
// main-own-abort.cpp:203: passed: aborted: Expect_aborts succeeds for std::abort() [pass]: std::abort()
// main-own-abort.cpp:208: passed: aborted: Expect_aborts succeeds for assert(false) [pass]: assert( false )
// main-own-abort.cpp:213: failed: didn't abort: Expect_aborts reports assert(true) [fail]: assert( true )
// main-own-abort.cpp:218: passed: aborted: Expect_aborts succeeds for assert(false) in user noexcept function [pass]: user_noexcept()
// main-own-abort.cpp:223: failed: got unexpected exception with message "augh": Expect_aborts reports an unexpected standard exception [fail]: throw std::runtime_error("augh")
// main-own-abort.cpp:228: failed: got unexpected exception of unknown type: Expect_aborts reports an unexpected non-standard exception [fail]: throw user_type{}
// main-own-abort.cpp:235: passed: didn't abort: Expect_no_abort succeeds for assert(true) [pass]: assert( true )
// main-own-abort.cpp:240: failed: aborted: Expect_no_abort reports ::abort() [fail]: ::abort()
// main-own-abort.cpp:245: failed: aborted: Expect_no_abort reports std::abort() [fail]: std::abort()
// main-own-abort.cpp:250: failed: aborted: Expect_no_abort reports assert(false) [fail]: assert( false )
// main-own-abort.cpp:255: failed: got unexpected exception with message "augh": Expect_no_abort reports an unexpected standard exception [fail]: throw std::runtime_error("augh")
// main-own-abort.cpp:260: failed: got unexpected exception of unknown type: Expect_no_abort reports an unexpected non-standard exception [fail]: throw user_type{}
// 9 out of 14 selected tests failed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment