Last active
January 11, 2016 12:12
-
-
Save martinmoene/50a13f9cba9b041a82b2 to your computer and use it in GitHub Desktop.
EXPECT_ABORTS for lest test framework (C++11) - substitute ::abort(), longjmp()
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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