123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- // Lightweight unit-testing C++11 framework. zlib/libpng licensed.
- // - rlyeh ~~ listening to Led Zeppelin / No Quarter
- #ifndef DESSERT_VERSION
- #define DESSERT_VERSION "1.0.2" // Public API following
- #define dessert(...) ( !!(__VA_ARGS__) ? \
- ( dessert::suite(#__VA_ARGS__,__FILE__,__LINE__,1) < __VA_ARGS__ ) : \
- ( dessert::suite(#__VA_ARGS__,__FILE__,__LINE__,0) < __VA_ARGS__ ) )
- #define desserts(...) \
- static void dessert$line(dessert)(); \
- static const bool dessert$line(dsstSuite_) = dessert::suite::queue( [](){ \
- std::string title = "" __VA_ARGS__; if( title.empty() ) title = "Suite"; \
- fprintf( stderr, "------ %s\n", title.c_str() ); \
- dessert$line(dessert)(); \
- }, "" #__VA_ARGS__ ); \
- void dessert$line(dessert)()
- #define throws(...) ( [&](){ try { __VA_ARGS__; } catch( ... ) { return true; } return false; }() )
- /* API details following */
- #include <cassert>
- #include <cstdio>
- #include <cstdlib>
- #include <chrono>
- #include <deque>
- #include <functional>
- #include <string>
- #include <sstream>
- namespace dessert {
- using namespace std;
- using timer = chrono::high_resolution_clock;
- template<typename T> inline string to_str( const T &t ) { stringstream ss; return (ss << t) ? ss.str() : string("??"); }
- template< > inline string to_str( const timer::time_point &start ) {
- return to_str( double((timer::now() - start).count()) * timer::period::num / timer::period::den );
- }
- class suite {
- timer::time_point start = timer::now();
- deque< string > xpr;
- int ok = false, has_bp = false;
- enum { BREAKPOINT, BREAKPOINTS, PASSED, FAILED, TESTNO };
- static unsigned &get(int i) { static unsigned var[TESTNO+1] = {}; return var[i]; }
- public:
- static bool queue( const function<void()> &fn, const string &text ) {
- static auto start = timer::now();
- static struct install : public deque<function<void()>> {
- install() : deque<function<void()>>() {
- get(BREAKPOINT) = stoul( getenv("BREAKON") ? getenv("BREAKON") : "0" );
- }
- ~install() {
- for( auto &fn : *this ) fn();
- string ss, run = to_str( get(PASSED)+get(FAILED) ), res = get(FAILED) ? "[FAIL] " : "[ OK ] ";
- if( get(FAILED) ) ss += res + "Failure! " + to_str(get(FAILED)) + '/'+ run + " tests failed :(\n";
- else ss += res + "Success: " + run + " tests passed :)\n";
- ss += " Breakpoints: " + to_str( get(BREAKPOINTS) ) + " (*)\n";
- ss += " Total time: " + to_str(start) + " seconds.\n";
- fprintf( stderr, "\n%s", ss.c_str() );
- if( get(FAILED) ) std::exit( get(FAILED) );
- }
- } queue;
- return text.find("before main()") == string::npos ? ( queue.push_back( fn ), 0 ) : ( fn(), 1 );
- }
- suite( const char *const text, const char *const file, int line, bool result )
- : xpr( {string(file) + ':' + to_str(line), " - ", text, "" } ), ok(result) {
- xpr[0] = "Test " + to_str(++get(TESTNO)) + " at " + xpr[0];
- if( 0 != ( has_bp = ( get(TESTNO) == get(BREAKPOINT) )) ) {
- get(BREAKPOINTS)++;
- fprintf( stderr, "<dessert/dessert.hpp> says: breaking on test #%d\n\t", get(TESTNO) );
- assert(! "<dessert/dessert.hpp> says: breakpoint requested" );
- fprintf( stderr, "%s", "\n<dessert/dessert.hpp> says: breakpoint failed!\n" );
- }
- }
- ~suite() {
- if( xpr.empty() ) return;
- operator bool(), queue( [&](){ get(ok ? PASSED : FAILED)++; }, "before main()" );
- string res[] = { "[FAIL]", "[ OK ]", " ", " *", " ", "" }, *bp = &res[2], *tab = &res[4];
- xpr[0] = res[ok] + bp[has_bp] + xpr[0] + " (" + to_str(start) + " s)" + (xpr[1].size() > 3 && !ok ? xpr[1] : tab[1]);
- xpr.erase( xpr.begin() + 1 );
- if( ok ) xpr = { xpr[0] }; else {
- xpr[1].resize( (xpr[1] != (xpr[2] = xpr[2].substr( xpr[2][2] == ' ' ? 3 : 4 ))) * xpr[1].size() );
- xpr.push_back( "(unexpected)" );
- }
- for( auto end = xpr.size(), it = end - end; it < end; ++it ) {
- fprintf( stderr, &"\0%s%s\n"[ !xpr[it].empty() ], tab[ !it ].c_str(), xpr[ it ].c_str() );
- } }
- # define dessert$join(str, num) str##num
- # define dessert$glue(str, num) dessert$join(str, num)
- # define dessert$line(str) dessert$glue(str, __LINE__)
- # define dessert$(OP) \
- template<typename T> suite &operator OP( const T &rhs ) { return xpr[3] += " "#OP" " + to_str(rhs), *this; } \
- template<unsigned N> suite &operator OP( const char (&rhs)[N] ) { return xpr[3] += " "#OP" " + to_str(rhs), *this; }
- template<typename T> suite &operator <<( const T &t ) { return xpr[1] += to_str(t), *this; }
- template<unsigned N> suite &operator <<( const char (&str)[N] ) { return xpr[1] += to_str(str), *this; }
- operator bool() {
- return xpr.size() >= 3 && xpr[3].size() >= 6 && [&]() -> bool {
- char signR = xpr[3].at(2);
- bool equal = xpr[3].substr( 4 + xpr[3].size()/2 ) == xpr[3].substr( 3, xpr[3].size()/2 - 3 );
- return (ok = ( signR == '=' ? equal : ( signR == '!' ? !equal : ok ) ));
- }(), ok;
- }
- dessert$(<) dessert$(<=) dessert$(>) dessert$(>=) dessert$(!=) dessert$(==) dessert$(&&) dessert$(||)
- };
- }
- #endif
|