183b5493dSMartin Moene// Copyright 2013, 2014, 2015, 2016 by Martin Moene
26b44f317SMartin Moene//
36b44f317SMartin Moene// lest is based on ideas by Kevlin Henney, see video at
46b44f317SMartin Moene// http://skillsmatter.com/podcast/agile-testing/kevlin-henney-rethinking-unit-testing-in-c-plus-plus
56b44f317SMartin Moene//
66b44f317SMartin Moene// Distributed under the Boost Software License, Version 1.0. (See accompanying
76b44f317SMartin Moene// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
86b44f317SMartin Moene
983b5493dSMartin Moene#ifndef LEST_LEST_HPP_INCLUDED
1083b5493dSMartin Moene#define LEST_LEST_HPP_INCLUDED
116b44f317SMartin Moene
126b44f317SMartin Moene#include <algorithm>
136b44f317SMartin Moene#include <iomanip>
146b44f317SMartin Moene#include <iostream>
156b44f317SMartin Moene#include <iterator>
166b44f317SMartin Moene#include <limits>
176b44f317SMartin Moene#include <sstream>
186b44f317SMartin Moene#include <stdexcept>
196b44f317SMartin Moene#include <set>
206b44f317SMartin Moene#include <string>
216b44f317SMartin Moene#include <utility>
226b44f317SMartin Moene#include <vector>
236b44f317SMartin Moene
246b44f317SMartin Moene#include <cctype>
256b44f317SMartin Moene#include <cmath>
266b44f317SMartin Moene#include <cstddef>
276b44f317SMartin Moene#include <cstdlib>
286b44f317SMartin Moene#include <ctime>
296b44f317SMartin Moene
306b44f317SMartin Moene#ifdef __clang__
316b44f317SMartin Moene# pragma clang diagnostic ignored "-Woverloaded-shift-op-parentheses"
32883fa058SMartin Moene# pragma clang diagnostic ignored "-Wunused-comparison"
336b44f317SMartin Moene# pragma clang diagnostic ignored "-Wunused-value"
346b44f317SMartin Moene#elif defined __GNUC__
356b44f317SMartin Moene# pragma GCC   diagnostic ignored "-Wunused-value"
366b44f317SMartin Moene#endif
376b44f317SMartin Moene
386726a92dSMartin Moene#define  lest_VERSION "1.30.1"
396b44f317SMartin Moene
406b44f317SMartin Moene#ifndef  lest_FEATURE_COLOURISE
416b44f317SMartin Moene# define lest_FEATURE_COLOURISE 0
426b44f317SMartin Moene#endif
436b44f317SMartin Moene
446b44f317SMartin Moene#ifndef  lest_FEATURE_LITERAL_SUFFIX
456b44f317SMartin Moene# define lest_FEATURE_LITERAL_SUFFIX 0
466b44f317SMartin Moene#endif
476b44f317SMartin Moene
486b44f317SMartin Moene#ifndef  lest_FEATURE_REGEX_SEARCH
496b44f317SMartin Moene# define lest_FEATURE_REGEX_SEARCH 0
506b44f317SMartin Moene#endif
516b44f317SMartin Moene
526b44f317SMartin Moene#ifndef  lest_FEATURE_TIME
536b44f317SMartin Moene# define lest_FEATURE_TIME 1
546b44f317SMartin Moene#endif
556b44f317SMartin Moene
566b44f317SMartin Moene#ifndef lest_FEATURE_TIME_PRECISION
576b44f317SMartin Moene#define lest_FEATURE_TIME_PRECISION  0
586b44f317SMartin Moene#endif
596b44f317SMartin Moene
606b44f317SMartin Moene#ifdef _WIN32
616b44f317SMartin Moene# define lest_PLATFORM_IS_WINDOWS 1
626b44f317SMartin Moene#endif
636b44f317SMartin Moene
646b44f317SMartin Moene#if lest_FEATURE_REGEX_SEARCH
656b44f317SMartin Moene# include <regex>
666b44f317SMartin Moene#endif
676b44f317SMartin Moene
686b44f317SMartin Moene#if lest_FEATURE_TIME
696b44f317SMartin Moene# if lest_PLATFORM_IS_WINDOWS
706b44f317SMartin Moene#  include <iomanip>
716b44f317SMartin Moene#  include <windows.h>
726b44f317SMartin Moene# else
736b44f317SMartin Moene#  include <iomanip>
746b44f317SMartin Moene#  include <sys/time.h>
756b44f317SMartin Moene# endif
766b44f317SMartin Moene#endif
776b44f317SMartin Moene
786b44f317SMartin Moene#if defined(_MSC_VER)
7985b465b2SMartin Moene# define lest_COMPILER_MSVC_VERSION   (_MSC_VER / 100 - 5 - (_MSC_VER < 1900))
806b44f317SMartin Moene#else
816b44f317SMartin Moene# define lest_COMPILER_MSVC_VERSION   0
826b44f317SMartin Moene#endif
836b44f317SMartin Moene
846b44f317SMartin Moene#if lest_COMPILER_MSVC_VERSION == 6
85883fa058SMartin Moene# define lest_COMPILER_IS_MSVC6  1
866b44f317SMartin Moene#endif
876b44f317SMartin Moene
886726a92dSMartin Moene#define lest_CPP11_OR_GREATER  ((__cplusplus >= 201103L ) || lest_COMPILER_MSVC_VERSION >= 12)
896b44f317SMartin Moene
9083b5493dSMartin Moene#if lest_CPP11_OR_GREATER || lest_COMPILER_MSVC_VERSION >= 10
916b44f317SMartin Moene
926b44f317SMartin Moene# include <cstdint>
936b44f317SMartin Moene# include <random>
946b44f317SMartin Moene# include <tuple>
956b44f317SMartin Moene
966b44f317SMartin Moenenamespace lest
976b44f317SMartin Moene{
986b44f317SMartin Moene    using std::tie;
996b44f317SMartin Moene}
1006b44f317SMartin Moene
1016b44f317SMartin Moene#else
1026b44f317SMartin Moene
103883fa058SMartin Moene# if !defined(__clang__) && defined(__GNUC__)
104883fa058SMartin Moene#  pragma GCC diagnostic push
105883fa058SMartin Moene#  pragma GCC diagnostic ignored "-Weffc++"
106883fa058SMartin Moene# endif
107883fa058SMartin Moene
1086b44f317SMartin Moenenamespace lest
1096b44f317SMartin Moene{
1106b44f317SMartin Moene    template< typename T1, typename T2>
1116b44f317SMartin Moene    struct Tie
1126b44f317SMartin Moene    {
1136b44f317SMartin Moene        Tie( T1 & first, T2 & second )
1146b44f317SMartin Moene        : first( first ), second( second ) {}
1156b44f317SMartin Moene
1166b44f317SMartin Moene        std::pair<T1, T2> const &
1176b44f317SMartin Moene        operator=( std::pair<T1, T2> const & rhs )
1186b44f317SMartin Moene        {
1196b44f317SMartin Moene            first  = rhs.first;
1206b44f317SMartin Moene            second = rhs.second;
1216b44f317SMartin Moene            return rhs;
1226b44f317SMartin Moene        }
1236b44f317SMartin Moene
1246b44f317SMartin Moene    private:
1256b44f317SMartin Moene        void operator=( Tie const & );
1266b44f317SMartin Moene
1276b44f317SMartin Moene        T1 & first;
1286b44f317SMartin Moene        T2 & second;
1296b44f317SMartin Moene    };
1306b44f317SMartin Moene
1316b44f317SMartin Moene    template<typename T1, typename T2>
1326b44f317SMartin Moene    inline Tie<T1,T2> tie( T1 & first, T2 & second )
1336b44f317SMartin Moene    {
1346b44f317SMartin Moene      return Tie<T1, T2>( first, second );
1356b44f317SMartin Moene    }
136883fa058SMartin Moene
137883fa058SMartin Moene# if !defined(__clang__) && defined(__GNUC__)
138883fa058SMartin Moene#  pragma GCC diagnostic pop
139883fa058SMartin Moene# endif
140883fa058SMartin Moene
1416b44f317SMartin Moene}
1426b44f317SMartin Moene#endif // lest_CPP11_OR_GREATER
1436b44f317SMartin Moene
1446b44f317SMartin Moenenamespace lest
1456b44f317SMartin Moene{
1466b44f317SMartin Moene#ifdef lest_COMPILER_IS_MSVC6
1476b44f317SMartin Moene    using ::strtol;
1486b44f317SMartin Moene    using ::rand;
1496b44f317SMartin Moene    using ::srand;
1506b44f317SMartin Moene
1516b44f317SMartin Moene    inline double abs( double x ) { return ::fabs( x ); }
1526b44f317SMartin Moene
1536b44f317SMartin Moene    template< typename T >
15483b5493dSMartin Moene    T const & (min)(T const & a, T const & b) { return a <= b ? a : b; }
1556b44f317SMartin Moene#else
1566b44f317SMartin Moene    using std::abs;
15783b5493dSMartin Moene    using std::min;
1586b44f317SMartin Moene    using std::strtol;
1596b44f317SMartin Moene    using std::rand;
1606b44f317SMartin Moene    using std::srand;
1616b44f317SMartin Moene#endif
1626b44f317SMartin Moene}
1636b44f317SMartin Moene
1646b44f317SMartin Moene#if ! defined( lest_NO_SHORT_MACRO_NAMES ) && ! defined( lest_NO_SHORT_ASSERTION_NAMES )
1656b44f317SMartin Moene# define SETUP             lest_SETUP
1666b44f317SMartin Moene# define SECTION           lest_SECTION
1676b44f317SMartin Moene
1686b44f317SMartin Moene# define EXPECT            lest_EXPECT
1696b44f317SMartin Moene# define EXPECT_NOT        lest_EXPECT_NOT
1706b44f317SMartin Moene# define EXPECT_NO_THROW   lest_EXPECT_NO_THROW
1716b44f317SMartin Moene# define EXPECT_THROWS     lest_EXPECT_THROWS
1726b44f317SMartin Moene# define EXPECT_THROWS_AS  lest_EXPECT_THROWS_AS
1736b44f317SMartin Moene
1746b44f317SMartin Moene# define SCENARIO          lest_SCENARIO
1756b44f317SMartin Moene# define GIVEN             lest_GIVEN
1766b44f317SMartin Moene# define WHEN              lest_WHEN
1776b44f317SMartin Moene# define THEN              lest_THEN
1786b44f317SMartin Moene# define AND_WHEN          lest_AND_WHEN
1796b44f317SMartin Moene# define AND_THEN          lest_AND_THEN
1806b44f317SMartin Moene#endif
1816b44f317SMartin Moene
1826b44f317SMartin Moene#define lest_SCENARIO( sketch  )  lest_CASE(    lest::text("Scenario: ") + sketch  )
1836b44f317SMartin Moene#define lest_GIVEN(    context )  lest_SETUP(   lest::text(   "Given: ") + context )
1846b44f317SMartin Moene#define lest_WHEN(     story   )  lest_SECTION( lest::text(   " When: ") + story   )
1856b44f317SMartin Moene#define lest_THEN(     story   )  lest_SECTION( lest::text(   " Then: ") + story   )
1866b44f317SMartin Moene#define lest_AND_WHEN( story   )  lest_SECTION( lest::text(   "  And: ") + story   )
1876b44f317SMartin Moene#define lest_AND_THEN( story   )  lest_SECTION( lest::text(   "  And: ") + story   )
1886b44f317SMartin Moene
1896b44f317SMartin Moene#define lest_CASE( specification, proposition ) \
19083b5493dSMartin Moene    static void lest_FUNCTION( lest::env & ); \
1916b44f317SMartin Moene    namespace { lest::add_test lest_REGISTRAR( specification, lest::test( proposition, lest_FUNCTION ) ); } \
19283b5493dSMartin Moene    static void lest_FUNCTION( lest::env & lest_env )
1936b44f317SMartin Moene
1946b44f317SMartin Moene#define lest_ADD_TEST( specification, test ) \
1956b44f317SMartin Moene    specification.push_back( test )
1966b44f317SMartin Moene
1976b44f317SMartin Moene#define lest_SETUP( context ) \
19883b5493dSMartin Moene    for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ )
1996b44f317SMartin Moene
2006b44f317SMartin Moene#define lest_SECTION( proposition ) \
2016b44f317SMartin Moene    static int lest_UNIQUE( id ) = 0; \
20283b5493dSMartin Moene    if ( lest::guard( lest_UNIQUE( id ), lest__section, lest__count ) ) \
20383b5493dSMartin Moene        for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ )
2046b44f317SMartin Moene
2056b44f317SMartin Moene#define lest_EXPECT( expr ) \
2066b44f317SMartin Moene    do { \
2076b44f317SMartin Moene        try \
2086b44f317SMartin Moene        { \
2096b44f317SMartin Moene            if ( lest::result score = lest_DECOMPOSE( expr ) ) \
2106b44f317SMartin Moene                throw lest::failure( lest_LOCATION, #expr, score.decomposition ); \
21183b5493dSMartin Moene            else if ( lest_env.pass ) \
21283b5493dSMartin Moene                lest::report( lest_env.os, lest::passing( lest_LOCATION, #expr, score.decomposition ), lest_env.testing ); \
2136b44f317SMartin Moene        } \
2146b44f317SMartin Moene        catch(...) \
2156b44f317SMartin Moene        { \
2166b44f317SMartin Moene            lest::inform( lest_LOCATION, #expr ); \
2176b44f317SMartin Moene        } \
2186b44f317SMartin Moene    } while ( lest::is_false() )
2196b44f317SMartin Moene
2206b44f317SMartin Moene#define lest_EXPECT_NOT( expr ) \
2216b44f317SMartin Moene    do { \
2226b44f317SMartin Moene        try \
2236b44f317SMartin Moene        { \
2246b44f317SMartin Moene            if ( lest::result score = lest_DECOMPOSE( expr ) ) \
2256b44f317SMartin Moene            { \
22683b5493dSMartin Moene                if ( lest_env.pass ) \
22783b5493dSMartin Moene                    lest::report( lest_env.os, lest::passing( lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) ), lest_env.testing ); \
2286b44f317SMartin Moene            } \
2296b44f317SMartin Moene            else \
2306b44f317SMartin Moene                throw lest::failure( lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) ); \
2316b44f317SMartin Moene        } \
2326b44f317SMartin Moene        catch(...) \
2336b44f317SMartin Moene        { \
2346b44f317SMartin Moene            lest::inform( lest_LOCATION, lest::not_expr( #expr ) ); \
2356b44f317SMartin Moene        } \
2366b44f317SMartin Moene    } while ( lest::is_false() )
2376b44f317SMartin Moene
2386b44f317SMartin Moene#define lest_EXPECT_NO_THROW( expr ) \
2396b44f317SMartin Moene    do \
2406b44f317SMartin Moene    { \
2416b44f317SMartin Moene        try { expr; } \
2426b44f317SMartin Moene        catch (...) { lest::inform( lest_LOCATION, #expr ); } \
24383b5493dSMartin Moene        if ( lest_env.pass ) \
24483b5493dSMartin Moene            lest::report( lest_env.os, lest::got_none( lest_LOCATION, #expr ), lest_env.testing ); \
2456b44f317SMartin Moene    } while ( lest::is_false() )
2466b44f317SMartin Moene
2476b44f317SMartin Moene#define lest_EXPECT_THROWS( expr ) \
2486b44f317SMartin Moene    do \
2496b44f317SMartin Moene    { \
2506b44f317SMartin Moene        try \
2516b44f317SMartin Moene        { \
2526b44f317SMartin Moene            expr; \
2536b44f317SMartin Moene        } \
2546b44f317SMartin Moene        catch (...) \
2556b44f317SMartin Moene        { \
25683b5493dSMartin Moene            if ( lest_env.pass ) \
25783b5493dSMartin Moene                lest::report( lest_env.os, lest::got( lest_LOCATION, #expr ), lest_env.testing ); \
2586b44f317SMartin Moene            break; \
2596b44f317SMartin Moene        } \
2606b44f317SMartin Moene        throw lest::expected( lest_LOCATION, #expr ); \
2616b44f317SMartin Moene    } \
2626b44f317SMartin Moene    while ( lest::is_false() )
2636b44f317SMartin Moene
2646b44f317SMartin Moene#define lest_EXPECT_THROWS_AS( expr, excpt ) \
2656b44f317SMartin Moene    do \
2666b44f317SMartin Moene    { \
2676b44f317SMartin Moene        try \
2686b44f317SMartin Moene        { \
2696b44f317SMartin Moene            expr; \
2706b44f317SMartin Moene        }  \
2716b44f317SMartin Moene        catch ( excpt & ) \
2726b44f317SMartin Moene        { \
27383b5493dSMartin Moene            if ( lest_env.pass ) \
27483b5493dSMartin Moene                lest::report( lest_env.os, lest::got( lest_LOCATION, #expr, lest::of_type( #excpt ) ), lest_env.testing ); \
2756b44f317SMartin Moene            break; \
2766b44f317SMartin Moene        } \
2776b44f317SMartin Moene        catch (...) {} \
2786b44f317SMartin Moene        throw lest::expected( lest_LOCATION, #expr, lest::of_type( #excpt ) ); \
2796b44f317SMartin Moene    } \
2806b44f317SMartin Moene    while ( lest::is_false() )
2816b44f317SMartin Moene
2826b44f317SMartin Moene#define lest_DECOMPOSE( expr ) ( lest::expression_decomposer() << expr )
2836b44f317SMartin Moene
2846b44f317SMartin Moene#define lest_UNIQUE(  name       ) lest_UNIQUE2( name, __LINE__ )
2856b44f317SMartin Moene#define lest_UNIQUE2( name, line ) lest_UNIQUE3( name, line )
2866b44f317SMartin Moene#define lest_UNIQUE3( name, line ) name ## line
2876b44f317SMartin Moene
2886b44f317SMartin Moene#define lest_LOCATION  lest::location(__FILE__, __LINE__)
2896b44f317SMartin Moene
2906b44f317SMartin Moene#define lest_FUNCTION  lest_UNIQUE(__lest_function__  )
2916b44f317SMartin Moene#define lest_REGISTRAR lest_UNIQUE(__lest_registrar__ )
2926b44f317SMartin Moene
2936b44f317SMartin Moene#define lest_DIMENSION_OF( a ) ( sizeof(a) / sizeof(0[a]) )
2946b44f317SMartin Moene
2956b44f317SMartin Moenenamespace lest {
2966b44f317SMartin Moene
2976b44f317SMartin Moenetypedef std::string       text;
2986b44f317SMartin Moenetypedef std::vector<text> texts;
2996b44f317SMartin Moene
3006b44f317SMartin Moenestruct env;
3016b44f317SMartin Moene
3026b44f317SMartin Moenestruct test
3036b44f317SMartin Moene{
3046b44f317SMartin Moene    text name;
3056b44f317SMartin Moene    void (* behaviour)( env & );
3066b44f317SMartin Moene
3076b44f317SMartin Moene    test( text name, void (* behaviour)( env & ) )
3086b44f317SMartin Moene    : name( name ), behaviour( behaviour ) {}
3096b44f317SMartin Moene};
3106b44f317SMartin Moene
3116b44f317SMartin Moenetypedef std::vector<test> tests;
3126b44f317SMartin Moenetypedef tests test_specification;
3136b44f317SMartin Moene
3146b44f317SMartin Moenestruct add_test
3156b44f317SMartin Moene{
3166b44f317SMartin Moene    add_test( tests & specification, test const & test_case )
3176b44f317SMartin Moene    {
3186b44f317SMartin Moene        specification.push_back( test_case );
3196b44f317SMartin Moene    }
3206b44f317SMartin Moene};
3216b44f317SMartin Moene
3226b44f317SMartin Moenestruct result
3236b44f317SMartin Moene{
3246b44f317SMartin Moene    const bool passed;
3256b44f317SMartin Moene    const text decomposition;
3266b44f317SMartin Moene
3276b44f317SMartin Moene    result( bool passed, text decomposition )
3286b44f317SMartin Moene    : passed( passed ), decomposition( decomposition ) {}
3296b44f317SMartin Moene    operator bool() { return ! passed; }
3306b44f317SMartin Moene};
3316b44f317SMartin Moene
3326b44f317SMartin Moenestruct location
3336b44f317SMartin Moene{
3346b44f317SMartin Moene    const text file;
3356b44f317SMartin Moene    const int line;
3366b44f317SMartin Moene
3376b44f317SMartin Moene    location( text file, int line )
3386b44f317SMartin Moene    : file( file ), line( line ) {}
3396b44f317SMartin Moene};
3406b44f317SMartin Moene
3416b44f317SMartin Moenestruct comment
3426b44f317SMartin Moene{
3436b44f317SMartin Moene    const text info;
3446b44f317SMartin Moene
3456b44f317SMartin Moene    comment( text info ) : info( info ) {}
3466b44f317SMartin Moene    operator bool() { return ! info.empty(); }
3476b44f317SMartin Moene};
3486b44f317SMartin Moene
3496b44f317SMartin Moenestruct message : std::runtime_error
3506b44f317SMartin Moene{
3516b44f317SMartin Moene    const text kind;
3526b44f317SMartin Moene    const location where;
3536b44f317SMartin Moene    const comment note;
3546b44f317SMartin Moene
3556b44f317SMartin Moene    ~message() throw() {}
3566b44f317SMartin Moene
3576b44f317SMartin Moene    message( text kind, location where, text expr, text note = "" )
3586b44f317SMartin Moene    : std::runtime_error( expr ), kind( kind ), where( where ), note( note ) {}
3596b44f317SMartin Moene};
3606b44f317SMartin Moene
3616b44f317SMartin Moenestruct failure : message
3626b44f317SMartin Moene{
3636b44f317SMartin Moene    failure( location where, text expr, text decomposition )
3646b44f317SMartin Moene    : message( "failed", where, expr + " for " + decomposition ) {}
3656b44f317SMartin Moene};
3666b44f317SMartin Moene
3676b44f317SMartin Moenestruct success : message
3686b44f317SMartin Moene{
3696b44f317SMartin Moene    success( text kind, location where, text expr, text note = "" )
3706b44f317SMartin Moene    : message( kind, where, expr, note ) {}
3716b44f317SMartin Moene};
3726b44f317SMartin Moene
3736b44f317SMartin Moenestruct passing : success
3746b44f317SMartin Moene{
3756b44f317SMartin Moene    passing( location where, text expr, text decomposition )
3766b44f317SMartin Moene    : success( "passed", where, expr + " for " + decomposition ) {}
3776b44f317SMartin Moene};
3786b44f317SMartin Moene
3796b44f317SMartin Moenestruct got_none : success
3806b44f317SMartin Moene{
3816b44f317SMartin Moene    got_none( location where, text expr )
3826b44f317SMartin Moene    : success( "passed: got no exception", where, expr ) {}
3836b44f317SMartin Moene};
3846b44f317SMartin Moene
3856b44f317SMartin Moenestruct got : success
3866b44f317SMartin Moene{
3876b44f317SMartin Moene    got( location where, text expr )
3886b44f317SMartin Moene    : success( "passed: got exception", where, expr ) {}
3896b44f317SMartin Moene
3906b44f317SMartin Moene    got( location where, text expr, text excpt )
3916b44f317SMartin Moene    : success( "passed: got exception " + excpt, where, expr ) {}
3926b44f317SMartin Moene};
3936b44f317SMartin Moene
3946b44f317SMartin Moenestruct expected : message
3956b44f317SMartin Moene{
3966b44f317SMartin Moene    expected( location where, text expr, text excpt = "" )
3976b44f317SMartin Moene    : message( "failed: didn't get exception", where, expr, excpt ) {}
3986b44f317SMartin Moene};
3996b44f317SMartin Moene
4006b44f317SMartin Moenestruct unexpected : message
4016b44f317SMartin Moene{
4026b44f317SMartin Moene    unexpected( location where, text expr, text note = "" )
4036b44f317SMartin Moene    : message( "failed: got unexpected exception", where, expr, note ) {}
4046b44f317SMartin Moene};
4056b44f317SMartin Moene
4066b44f317SMartin Moenestruct guard
4076b44f317SMartin Moene{
4086b44f317SMartin Moene    int & id;
4096b44f317SMartin Moene    int const & section;
4106b44f317SMartin Moene
4116b44f317SMartin Moene    guard( int & id, int const & section, int & count )
4126b44f317SMartin Moene    : id( id ), section( section )
4136b44f317SMartin Moene    {
4146b44f317SMartin Moene        if ( section == 0 )
4156b44f317SMartin Moene            id = count++ - 1;
4166b44f317SMartin Moene    }
4176b44f317SMartin Moene    operator bool() { return id == section; }
4186b44f317SMartin Moene};
4196b44f317SMartin Moene
4206b44f317SMartin Moeneclass approx
4216b44f317SMartin Moene{
4226b44f317SMartin Moenepublic:
4236b44f317SMartin Moene    explicit approx ( double magnitude )
4246b44f317SMartin Moene    : epsilon_  ( std::numeric_limits<float>::epsilon() * 100 )
4256b44f317SMartin Moene    , scale_    ( 1.0 )
4266b44f317SMartin Moene    , magnitude_( magnitude ) {}
4276b44f317SMartin Moene
4286b44f317SMartin Moene    static approx custom() { return approx( 0 ); }
4296b44f317SMartin Moene
4306b44f317SMartin Moene    approx operator()( double magnitude )
4316b44f317SMartin Moene    {
4326b44f317SMartin Moene        approx approx ( magnitude );
4336b44f317SMartin Moene        approx.epsilon( epsilon_  );
4346b44f317SMartin Moene        approx.scale  ( scale_    );
4356b44f317SMartin Moene        return approx;
4366b44f317SMartin Moene    }
4376b44f317SMartin Moene
4386b44f317SMartin Moene    double magnitude() const { return magnitude_; }
4396b44f317SMartin Moene
4406b44f317SMartin Moene    approx & epsilon( double epsilon ) { epsilon_ = epsilon; return *this; }
4416b44f317SMartin Moene    approx & scale  ( double scale   ) { scale_   = scale;   return *this; }
4426b44f317SMartin Moene
4436b44f317SMartin Moene    friend bool operator == ( double lhs, approx const & rhs )
4446b44f317SMartin Moene    {
4456b44f317SMartin Moene        // Thanks to Richard Harris for his help refining this formula.
44683b5493dSMartin Moene        return lest::abs( lhs - rhs.magnitude_ ) < rhs.epsilon_ * ( rhs.scale_ + (lest::min)( lest::abs( lhs ), lest::abs( rhs.magnitude_ ) ) );
4476b44f317SMartin Moene    }
4486b44f317SMartin Moene
4496b44f317SMartin Moene    friend bool operator == ( approx const & lhs, double rhs ) { return  operator==( rhs, lhs ); }
4506b44f317SMartin Moene    friend bool operator != ( double lhs, approx const & rhs ) { return !operator==( lhs, rhs ); }
4516b44f317SMartin Moene    friend bool operator != ( approx const & lhs, double rhs ) { return !operator==( rhs, lhs ); }
4526b44f317SMartin Moene
453444de030SMartin Moene    friend bool operator <= ( double lhs, approx const & rhs ) { return lhs < rhs.magnitude_ || lhs == rhs; }
454444de030SMartin Moene    friend bool operator <= ( approx const & lhs, double rhs ) { return lhs.magnitude_ < rhs || lhs == rhs; }
455444de030SMartin Moene    friend bool operator >= ( double lhs, approx const & rhs ) { return lhs > rhs.magnitude_ || lhs == rhs; }
456444de030SMartin Moene    friend bool operator >= ( approx const & lhs, double rhs ) { return lhs.magnitude_ > rhs || lhs == rhs; }
457444de030SMartin Moene
4586b44f317SMartin Moeneprivate:
4596b44f317SMartin Moene    double epsilon_;
4606b44f317SMartin Moene    double scale_;
4616b44f317SMartin Moene    double magnitude_;
4626b44f317SMartin Moene};
4636b44f317SMartin Moene
4646b44f317SMartin Moeneinline bool is_false(           ) { return false; }
4656b44f317SMartin Moeneinline bool is_true ( bool flag ) { return  flag; }
4666b44f317SMartin Moene
4676b44f317SMartin Moeneinline text not_expr( text message )
4686b44f317SMartin Moene{
4696b44f317SMartin Moene    return "! ( " + message + " )";
4706b44f317SMartin Moene}
4716b44f317SMartin Moene
4726b44f317SMartin Moeneinline text with_message( text message )
4736b44f317SMartin Moene{
4746b44f317SMartin Moene    return "with message \"" + message + "\"";
4756b44f317SMartin Moene}
4766b44f317SMartin Moene
4776b44f317SMartin Moeneinline text of_type( text type )
4786b44f317SMartin Moene{
4796b44f317SMartin Moene    return "of type " + type;
4806b44f317SMartin Moene}
4816b44f317SMartin Moene
4826b44f317SMartin Moeneinline void inform( location where, text expr )
4836b44f317SMartin Moene{
4846b44f317SMartin Moene    try
4856b44f317SMartin Moene    {
4866b44f317SMartin Moene        throw;
4876b44f317SMartin Moene    }
4886b44f317SMartin Moene    catch( failure const & )
4896b44f317SMartin Moene    {
4906b44f317SMartin Moene        throw;
4916b44f317SMartin Moene    }
4926b44f317SMartin Moene    catch( std::exception const & e )
4936b44f317SMartin Moene    {
4946b44f317SMartin Moene        throw unexpected( where, expr, with_message( e.what() ) ); \
4956b44f317SMartin Moene    }
4966b44f317SMartin Moene    catch(...)
4976b44f317SMartin Moene    {
4986b44f317SMartin Moene        throw unexpected( where, expr, "of unknown type" ); \
4996b44f317SMartin Moene    }
5006b44f317SMartin Moene}
5016b44f317SMartin Moene
5026b44f317SMartin Moene// Expression decomposition:
5036b44f317SMartin Moene
504883fa058SMartin Moene#if lest_CPP11_OR_GREATER || lest_COMPILER_MSVC_VERSION >= 10
5056b44f317SMartin Moeneinline std::string to_string( std::nullptr_t const &      ) { return "nullptr"; }
5066b44f317SMartin Moene#endif
5076b44f317SMartin Moeneinline std::string to_string( std::string    const & text ) { return "\"" + text + "\"" ; }
5086b44f317SMartin Moeneinline std::string to_string( char const *   const & text ) { return "\"" + std::string( text ) + "\"" ; }
5096b44f317SMartin Moeneinline std::string to_string( char           const & text ) { return "\'" + std::string( 1, text ) + "\'" ; }
5106b44f317SMartin Moene
5116b44f317SMartin Moeneinline std::ostream & operator<<( std::ostream & os, approx const & appr )
5126b44f317SMartin Moene{
5136b44f317SMartin Moene    return os << appr.magnitude();
5146b44f317SMartin Moene}
5156b44f317SMartin Moene
5166b44f317SMartin Moenetemplate <typename T>
5176b44f317SMartin Moenestd::string to_string( T const & value, int=0 /* VC6 */ )
5186b44f317SMartin Moene{
5196b44f317SMartin Moene    std::ostringstream os; os << std::boolalpha << value; return os.str();
5206b44f317SMartin Moene}
5216b44f317SMartin Moene
5226b44f317SMartin Moenetemplate<typename T1, typename T2>
5236b44f317SMartin Moenestd::string to_string( std::pair<T1,T2> const & pair )
5246b44f317SMartin Moene{
5256b44f317SMartin Moene    std::ostringstream oss;
5266b44f317SMartin Moene    oss << "{ " << to_string( pair.first ) << ", " << to_string( pair.second ) << " }";
5276b44f317SMartin Moene    return oss.str();
5286b44f317SMartin Moene}
5296b44f317SMartin Moene
530883fa058SMartin Moene#if lest_CPP11_OR_GREATER
5316b44f317SMartin Moene
5326b44f317SMartin Moenetemplate<typename TU, std::size_t N>
5336b44f317SMartin Moenestruct make_tuple_string
5346b44f317SMartin Moene{
5356b44f317SMartin Moene    static std::string make( TU const & tuple )
5366b44f317SMartin Moene    {
53783b5493dSMartin Moene        std::ostringstream os;
5386b44f317SMartin Moene        os << to_string( std::get<N - 1>( tuple ) ) << ( N < std::tuple_size<TU>::value ? ", ": " ");
5396b44f317SMartin Moene        return make_tuple_string<TU, N - 1>::make( tuple ) + os.str();
5406b44f317SMartin Moene    }
5416b44f317SMartin Moene};
5426b44f317SMartin Moene
5436b44f317SMartin Moenetemplate<typename TU>
5446b44f317SMartin Moenestruct make_tuple_string<TU, 0>
5456b44f317SMartin Moene{
5466b44f317SMartin Moene    static std::string make( TU const & ) { return ""; }
5476b44f317SMartin Moene};
5486b44f317SMartin Moene
5496b44f317SMartin Moenetemplate<typename ...TS>
5506b44f317SMartin Moeneauto to_string( std::tuple<TS...> const & tuple ) -> std::string
5516b44f317SMartin Moene{
5526b44f317SMartin Moene    return "{ " + make_tuple_string<std::tuple<TS...>, sizeof...(TS)>::make( tuple ) + "}";
5536b44f317SMartin Moene}
5546b44f317SMartin Moene#endif
5556b44f317SMartin Moene
5566b44f317SMartin Moenetemplate <typename L, typename R>
5576b44f317SMartin Moenestd::string to_string( L const & lhs, std::string op, R const & rhs )
5586b44f317SMartin Moene{
5596b44f317SMartin Moene    std::ostringstream os; os << to_string( lhs ) << " " << op << " " << to_string( rhs ); return os.str();
5606b44f317SMartin Moene}
5616b44f317SMartin Moene
5626b44f317SMartin Moenetemplate <typename L>
5636b44f317SMartin Moenestruct expression_lhs
5646b44f317SMartin Moene{
5656b44f317SMartin Moene    L lhs;
5666b44f317SMartin Moene
5676b44f317SMartin Moene    expression_lhs( L lhs ) : lhs( lhs ) {}
5686b44f317SMartin Moene
56983b5493dSMartin Moene    operator result() { return result( !!lhs, to_string( lhs ) ); }
5706b44f317SMartin Moene
5716b44f317SMartin Moene    template <typename R> result operator==( R const & rhs ) { return result( lhs == rhs, to_string( lhs, "==", rhs ) ); }
5726b44f317SMartin Moene    template <typename R> result operator!=( R const & rhs ) { return result( lhs != rhs, to_string( lhs, "!=", rhs ) ); }
5736b44f317SMartin Moene    template <typename R> result operator< ( R const & rhs ) { return result( lhs <  rhs, to_string( lhs, "<" , rhs ) ); }
5746b44f317SMartin Moene    template <typename R> result operator<=( R const & rhs ) { return result( lhs <= rhs, to_string( lhs, "<=", rhs ) ); }
5756b44f317SMartin Moene    template <typename R> result operator> ( R const & rhs ) { return result( lhs >  rhs, to_string( lhs, ">" , rhs ) ); }
5766b44f317SMartin Moene    template <typename R> result operator>=( R const & rhs ) { return result( lhs >= rhs, to_string( lhs, ">=", rhs ) ); }
5776b44f317SMartin Moene};
5786b44f317SMartin Moene
5796b44f317SMartin Moenestruct expression_decomposer
5806b44f317SMartin Moene{
5816b44f317SMartin Moene    template <typename L>
5826b44f317SMartin Moene    expression_lhs<L const &> operator<< ( L const & operand )
5836b44f317SMartin Moene    {
5846b44f317SMartin Moene        return expression_lhs<L const &>( operand );
5856b44f317SMartin Moene    }
5866b44f317SMartin Moene};
5876b44f317SMartin Moene
5886b44f317SMartin Moene// Reporter:
5896b44f317SMartin Moene
5906b44f317SMartin Moene#if lest_FEATURE_COLOURISE
5916b44f317SMartin Moene
5926b44f317SMartin Moeneinline text red  ( text words ) { return "\033[1;31m" + words + "\033[0m"; }
5936b44f317SMartin Moeneinline text green( text words ) { return "\033[1;32m" + words + "\033[0m"; }
5946b44f317SMartin Moeneinline text gray ( text words ) { return "\033[1;30m" + words + "\033[0m"; }
5956b44f317SMartin Moene
5966b44f317SMartin Moeneinline bool starts_with( text words, text with )
5976b44f317SMartin Moene{
5986b44f317SMartin Moene    return 0 == words.find( with );
5996b44f317SMartin Moene}
6006b44f317SMartin Moene
6016b44f317SMartin Moeneinline text replace( text words, text from, text to )
6026b44f317SMartin Moene{
6036b44f317SMartin Moene    size_t pos = words.find( from );
6046b44f317SMartin Moene    return pos == std::string::npos ? words : words.replace( pos, from.length(), to  );
6056b44f317SMartin Moene}
6066b44f317SMartin Moene
6076b44f317SMartin Moeneinline text colour( text words )
6086b44f317SMartin Moene{
6096b44f317SMartin Moene    if      ( starts_with( words, "failed" ) ) return replace( words, "failed", red  ( "failed" ) );
6106b44f317SMartin Moene    else if ( starts_with( words, "passed" ) ) return replace( words, "passed", green( "passed" ) );
6116b44f317SMartin Moene
6126b44f317SMartin Moene    return replace( words, "for", gray( "for" ) );
6136b44f317SMartin Moene}
6146b44f317SMartin Moene
6156b44f317SMartin Moeneinline bool is_cout( std::ostream & os ) { return &os == &std::cout; }
6166b44f317SMartin Moene
6176b44f317SMartin Moenestruct colourise
6186b44f317SMartin Moene{
6196b44f317SMartin Moene    const text words;
6206b44f317SMartin Moene
6216b44f317SMartin Moene    colourise( text words )
6226b44f317SMartin Moene    : words( words ) {}
6236b44f317SMartin Moene
6246b44f317SMartin Moene    // only colourise for std::cout, not for a stringstream as used in tests:
6256b44f317SMartin Moene
6266b44f317SMartin Moene    std::ostream & operator()( std::ostream & os ) const
6276b44f317SMartin Moene    {
6286b44f317SMartin Moene        return is_cout( os ) ? os << colour( words ) : os << words;
6296b44f317SMartin Moene    }
6306b44f317SMartin Moene};
6316b44f317SMartin Moene
6326b44f317SMartin Moeneinline std::ostream & operator<<( std::ostream & os, colourise words ) { return words( os ); }
6336b44f317SMartin Moene#else
6346b44f317SMartin Moeneinline text colourise( text words ) { return words; }
6356b44f317SMartin Moene#endif
6366b44f317SMartin Moene
63783b5493dSMartin Moeneinline text pluralise( text word,int n )
6386b44f317SMartin Moene{
6396b44f317SMartin Moene    return n == 1 ? word : word + "s";
6406b44f317SMartin Moene}
6416b44f317SMartin Moene
6426b44f317SMartin Moeneinline std::ostream & operator<<( std::ostream & os, comment note )
6436b44f317SMartin Moene{
6446b44f317SMartin Moene    return os << (note ? " " + note.info : "" );
6456b44f317SMartin Moene}
6466b44f317SMartin Moene
6476b44f317SMartin Moeneinline std::ostream & operator<<( std::ostream & os, location where )
6486b44f317SMartin Moene{
6496b44f317SMartin Moene#ifdef __GNUG__
6506b44f317SMartin Moene    return os << where.file << ":" << where.line;
6516b44f317SMartin Moene#else
6526b44f317SMartin Moene    return os << where.file << "(" << where.line << ")";
6536b44f317SMartin Moene#endif
6546b44f317SMartin Moene}
6556b44f317SMartin Moene
6566b44f317SMartin Moeneinline void report( std::ostream & os, message const & e, text test )
6576b44f317SMartin Moene{
6586b44f317SMartin Moene    os << e.where << ": " << colourise( e.kind ) << e.note << ": " << test << ": " << colourise( e.what() ) << std::endl;
6596b44f317SMartin Moene}
6606b44f317SMartin Moene
6616b44f317SMartin Moene// Test runner:
6626b44f317SMartin Moene
6636b44f317SMartin Moene#if lest_FEATURE_REGEX_SEARCH
6646b44f317SMartin Moene    inline bool search( text re, text line )
6656b44f317SMartin Moene    {
6666b44f317SMartin Moene        return std::regex_search( line, std::regex( re ) );
6676b44f317SMartin Moene    }
6686b44f317SMartin Moene#else
6696b44f317SMartin Moene    inline bool case_insensitive_equal( char a, char b )
6706b44f317SMartin Moene    {
6716b44f317SMartin Moene        return tolower( a ) == tolower( b );
6726b44f317SMartin Moene    }
6736b44f317SMartin Moene
6746b44f317SMartin Moene    inline bool search( text part, text line )
6756b44f317SMartin Moene    {
6766b44f317SMartin Moene        return std::search(
6776b44f317SMartin Moene            line.begin(), line.end(),
6786b44f317SMartin Moene            part.begin(), part.end(), case_insensitive_equal ) != line.end();
6796b44f317SMartin Moene    }
6806b44f317SMartin Moene#endif
6816b44f317SMartin Moene
6826b44f317SMartin Moeneinline bool match( texts whats, text line )
6836b44f317SMartin Moene{
6846b44f317SMartin Moene    for ( texts::iterator what = whats.begin(); what != whats.end() ; ++what )
6856b44f317SMartin Moene    {
6866b44f317SMartin Moene        if ( search( *what, line ) )
6876b44f317SMartin Moene            return true;
6886b44f317SMartin Moene    }
6896b44f317SMartin Moene    return false;
6906b44f317SMartin Moene}
6916b44f317SMartin Moene
6926b44f317SMartin Moeneinline bool hidden( text name )
6936b44f317SMartin Moene{
6946b44f317SMartin Moene#if lest_FEATURE_REGEX_SEARCH
6956b44f317SMartin Moene    texts skipped; skipped.push_back( "\\[\\.\\]" ); skipped.push_back( "\\[hide\\]" );
6966b44f317SMartin Moene#else
6976b44f317SMartin Moene    texts skipped; skipped.push_back( "[." ); skipped.push_back( "[hide]" );
6986b44f317SMartin Moene#endif
6996b44f317SMartin Moene    return match( skipped, name );
7006b44f317SMartin Moene}
7016b44f317SMartin Moene
7026b44f317SMartin Moeneinline bool none( texts args )
7036b44f317SMartin Moene{
7046b44f317SMartin Moene    return args.size() == 0;
7056b44f317SMartin Moene}
7066b44f317SMartin Moene
7076b44f317SMartin Moeneinline bool select( text name, texts include )
7086b44f317SMartin Moene{
7096b44f317SMartin Moene    if ( none( include ) )
7106b44f317SMartin Moene    {
7116b44f317SMartin Moene        return ! hidden( name );
7126b44f317SMartin Moene    }
7136b44f317SMartin Moene
7146b44f317SMartin Moene    bool any = false;
7156b44f317SMartin Moene    for ( texts::reverse_iterator pos = include.rbegin(); pos != include.rend(); ++pos )
7166b44f317SMartin Moene    {
7176b44f317SMartin Moene        text & part = *pos;
7186b44f317SMartin Moene
7196b44f317SMartin Moene        if ( part == "@" || part == "*" )
7206b44f317SMartin Moene            return true;
7216b44f317SMartin Moene
7226b44f317SMartin Moene        if ( search( part, name ) )
7236b44f317SMartin Moene            return true;
7246b44f317SMartin Moene
7256b44f317SMartin Moene        if ( '!' == part[0] )
7266b44f317SMartin Moene        {
7276b44f317SMartin Moene            any = true;
7286b44f317SMartin Moene            if ( search( part.substr(1), name ) )
7296b44f317SMartin Moene                return false;
7306b44f317SMartin Moene        }
7316b44f317SMartin Moene        else
7326b44f317SMartin Moene        {
7336b44f317SMartin Moene            any = false;
7346b44f317SMartin Moene        }
7356b44f317SMartin Moene    }
7366b44f317SMartin Moene    return any && ! hidden( name );
7376b44f317SMartin Moene}
7386b44f317SMartin Moene
7396b44f317SMartin Moeneinline int indefinite( int repeat ) { return repeat == -1; }
7406b44f317SMartin Moene
7416b44f317SMartin Moenetypedef unsigned long seed_t;
7426b44f317SMartin Moene
7436b44f317SMartin Moenestruct options
7446b44f317SMartin Moene{
7456b44f317SMartin Moene    options()
7466b44f317SMartin Moene    : help(false), abort(false), count(false), list(false), tags(false), time(false)
7476b44f317SMartin Moene    , pass(false), lexical(false), random(false), version(false), repeat(1), seed(0) {}
7486b44f317SMartin Moene
7496b44f317SMartin Moene    bool help;
7506b44f317SMartin Moene    bool abort;
7516b44f317SMartin Moene    bool count;
7526b44f317SMartin Moene    bool list;
7536b44f317SMartin Moene    bool tags;
7546b44f317SMartin Moene    bool time;
7556b44f317SMartin Moene    bool pass;
7566b44f317SMartin Moene    bool lexical;
7576b44f317SMartin Moene    bool random;
7586b44f317SMartin Moene    bool version;
7596b44f317SMartin Moene    int  repeat;
7606b44f317SMartin Moene    seed_t seed;
7616b44f317SMartin Moene};
7626b44f317SMartin Moene
7636b44f317SMartin Moenestruct env
7646b44f317SMartin Moene{
7656b44f317SMartin Moene    std::ostream & os;
7666b44f317SMartin Moene    bool pass;
7676b44f317SMartin Moene    text testing;
7686b44f317SMartin Moene
7696b44f317SMartin Moene    env( std::ostream & os, bool pass )
770883fa058SMartin Moene    : os( os ), pass( pass ), testing() {}
7716b44f317SMartin Moene
7726b44f317SMartin Moene    env & operator()( text test )
7736b44f317SMartin Moene    {
7746b44f317SMartin Moene        testing = test; return *this;
7756b44f317SMartin Moene    }
7766b44f317SMartin Moene};
7776b44f317SMartin Moene
7786b44f317SMartin Moenestruct action
7796b44f317SMartin Moene{
7806b44f317SMartin Moene    std::ostream & os;
7816b44f317SMartin Moene
7826b44f317SMartin Moene    action( std::ostream & os ) : os( os ) {}
7836b44f317SMartin Moene
7846b44f317SMartin Moene    operator      int() { return 0; }
7856b44f317SMartin Moene    bool        abort() { return false; }
7866b44f317SMartin Moene    action & operator()( test ) { return *this; }
7876b44f317SMartin Moene
7886b44f317SMartin Moeneprivate:
7896b44f317SMartin Moene    action( action const & );
790015a591fSMartin Moene	void operator=(action const & );
7916b44f317SMartin Moene};
7926b44f317SMartin Moene
7936b44f317SMartin Moenestruct print : action
7946b44f317SMartin Moene{
7956b44f317SMartin Moene    print( std::ostream & os ) : action( os ) {}
7966b44f317SMartin Moene
7976b44f317SMartin Moene    print &  operator()( test testing )
7986b44f317SMartin Moene    {
7996b44f317SMartin Moene        os << testing.name << "\n"; return *this;
8006b44f317SMartin Moene    }
8016b44f317SMartin Moene};
8026b44f317SMartin Moene
8036b44f317SMartin Moeneinline texts tags( text name, texts result = texts() )
8046b44f317SMartin Moene{
8056b44f317SMartin Moene    size_t none = std::string::npos;
8066b44f317SMartin Moene    size_t lb   = name.find_first_of( "[" );
8076b44f317SMartin Moene    size_t rb   = name.find_first_of( "]" );
8086b44f317SMartin Moene
8096b44f317SMartin Moene    if ( lb == none || rb == none )
8106b44f317SMartin Moene        return result;
8116b44f317SMartin Moene
8126b44f317SMartin Moene    result.push_back( name.substr( lb, rb - lb + 1 ) );
8136b44f317SMartin Moene
8146b44f317SMartin Moene    return tags( name.substr( rb + 1 ), result );
8156b44f317SMartin Moene}
8166b44f317SMartin Moene
8176b44f317SMartin Moenestruct ptags : action
8186b44f317SMartin Moene{
8196b44f317SMartin Moene    std::set<text> result;
8206b44f317SMartin Moene
821883fa058SMartin Moene    ptags( std::ostream & os ) : action( os ), result() {}
8226b44f317SMartin Moene
8236b44f317SMartin Moene    ptags & operator()( test testing )
8246b44f317SMartin Moene    {
8256b44f317SMartin Moene        texts tags_( tags( testing.name ) );
8266b44f317SMartin Moene        for ( texts::iterator pos = tags_.begin(); pos != tags_.end() ; ++pos )
8276b44f317SMartin Moene            result.insert( *pos );
8286b44f317SMartin Moene
8296b44f317SMartin Moene        return *this;
8306b44f317SMartin Moene    }
8316b44f317SMartin Moene
8326b44f317SMartin Moene    ~ptags()
8336b44f317SMartin Moene    {
8346b44f317SMartin Moene        std::copy( result.begin(), result.end(), std::ostream_iterator<text>( os, "\n" ) );
8356b44f317SMartin Moene    }
8366b44f317SMartin Moene};
8376b44f317SMartin Moene
8386b44f317SMartin Moenestruct count : action
8396b44f317SMartin Moene{
8406b44f317SMartin Moene    int n;
8416b44f317SMartin Moene
8426b44f317SMartin Moene    count( std::ostream & os ) : action( os ), n( 0 ) {}
8436b44f317SMartin Moene
8446b44f317SMartin Moene    count & operator()( test ) { ++n; return *this; }
8456b44f317SMartin Moene
8466b44f317SMartin Moene    ~count()
8476b44f317SMartin Moene    {
84883b5493dSMartin Moene        os << n << " selected " << pluralise("test", n) << "\n";
8496b44f317SMartin Moene    }
8506b44f317SMartin Moene};
8516b44f317SMartin Moene
8526b44f317SMartin Moene#if lest_FEATURE_TIME
8536b44f317SMartin Moene
8546b44f317SMartin Moene#if lest_PLATFORM_IS_WINDOWS
8556a4fc092SMartin Moene# if ! lest_CPP11_OR_GREATER && ! lest_COMPILER_MSVC_VERSION
8566a4fc092SMartin Moene    typedef unsigned long uint64_t;
8576a4fc092SMartin Moene# elif lest_COMPILER_MSVC_VERSION >= 6 && lest_COMPILER_MSVC_VERSION < 10
8586b44f317SMartin Moene    typedef /*un*/signed __int64 uint64_t;
8596b44f317SMartin Moene# else
8606a4fc092SMartin Moene    using ::uint64_t;
8616b44f317SMartin Moene# endif
8626b44f317SMartin Moene#else
863883fa058SMartin Moene# if ! lest_CPP11_OR_GREATER
8646b44f317SMartin Moene    typedef unsigned long long uint64_t;
8656b44f317SMartin Moene# endif
8666b44f317SMartin Moene#endif
8676b44f317SMartin Moene
8686b44f317SMartin Moene#if lest_PLATFORM_IS_WINDOWS
8696b44f317SMartin Moene    inline uint64_t current_ticks()
8706b44f317SMartin Moene    {
8716a4fc092SMartin Moene        static LARGE_INTEGER hz = { 0,0 }, hzo = { 0,0 };
8726a4fc092SMartin Moene        if ( ! hz.QuadPart )
8736b44f317SMartin Moene        {
8746a4fc092SMartin Moene            QueryPerformanceFrequency( &hz  );
8756a4fc092SMartin Moene            QueryPerformanceCounter  ( &hzo );
8766b44f317SMartin Moene        }
8776a4fc092SMartin Moene        LARGE_INTEGER t = { 0,0 }; QueryPerformanceCounter( &t );
8786b44f317SMartin Moene
8796a4fc092SMartin Moene        return uint64_t( ( ( t.QuadPart - hzo.QuadPart ) * 1000000 ) / hz.QuadPart );
8806b44f317SMartin Moene    }
8816b44f317SMartin Moene#else
8826b44f317SMartin Moene    inline uint64_t current_ticks()
8836b44f317SMartin Moene    {
8846b44f317SMartin Moene        timeval t; gettimeofday( &t, NULL );
8856b44f317SMartin Moene        return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
8866b44f317SMartin Moene    }
8876b44f317SMartin Moene#endif
8886b44f317SMartin Moene
8896b44f317SMartin Moenestruct timer
8906b44f317SMartin Moene{
8916b44f317SMartin Moene    const uint64_t start_ticks;
8926b44f317SMartin Moene
8936b44f317SMartin Moene    timer() : start_ticks( current_ticks() ) {}
8946b44f317SMartin Moene
8956b44f317SMartin Moene    double elapsed_seconds() const
8966b44f317SMartin Moene    {
897c6087551SMartin Moene        return static_cast<double>( current_ticks() - start_ticks ) / 1e6;
8986b44f317SMartin Moene    }
8996b44f317SMartin Moene};
9006b44f317SMartin Moene
9016b44f317SMartin Moenestruct times : action
9026b44f317SMartin Moene{
9036b44f317SMartin Moene    env output;
9046b44f317SMartin Moene    options option;
9056b44f317SMartin Moene    int selected;
9066b44f317SMartin Moene    int failures;
9076b44f317SMartin Moene
9086b44f317SMartin Moene    timer total;
9096b44f317SMartin Moene
9106b44f317SMartin Moene    times( std::ostream & os, options option )
9116b44f317SMartin Moene    : action( os ), output( os, option.pass ), option( option ), selected( 0 ), failures( 0 ), total()
9126b44f317SMartin Moene    {
9136b44f317SMartin Moene        os << std::setfill(' ') << std::fixed << std::setprecision( lest_FEATURE_TIME_PRECISION );
9146b44f317SMartin Moene    }
9156b44f317SMartin Moene
9166b44f317SMartin Moene    operator int() { return failures; }
9176b44f317SMartin Moene
9186b44f317SMartin Moene    bool abort() { return option.abort && failures > 0; }
9196b44f317SMartin Moene
9206b44f317SMartin Moene    times & operator()( test testing )
9216b44f317SMartin Moene    {
9226b44f317SMartin Moene        timer t;
9236b44f317SMartin Moene
9246b44f317SMartin Moene        try
9256b44f317SMartin Moene        {
9266b44f317SMartin Moene            testing.behaviour( output( testing.name ) );
9276b44f317SMartin Moene        }
9286b44f317SMartin Moene        catch( message const & )
9296b44f317SMartin Moene        {
9306b44f317SMartin Moene            ++failures;
9316b44f317SMartin Moene        }
9326b44f317SMartin Moene
9336b44f317SMartin Moene        os << std::setw(5) << ( 1000 * t.elapsed_seconds() ) << " ms: " << testing.name  << "\n";
9346b44f317SMartin Moene
9356b44f317SMartin Moene        return *this;
9366b44f317SMartin Moene    }
9376b44f317SMartin Moene
9386b44f317SMartin Moene    ~times()
9396b44f317SMartin Moene    {
9406b44f317SMartin Moene        os << "Elapsed time: " << std::setprecision(1) << total.elapsed_seconds() << " s\n";
9416b44f317SMartin Moene    }
9426b44f317SMartin Moene};
9436b44f317SMartin Moene#else
9446b44f317SMartin Moenestruct times : action { times( std::ostream &, options ) : action( os ) {} };
9456b44f317SMartin Moene#endif
9466b44f317SMartin Moene
9476b44f317SMartin Moenestruct confirm : action
9486b44f317SMartin Moene{
9496b44f317SMartin Moene    env output;
9506b44f317SMartin Moene    options option;
9516b44f317SMartin Moene    int selected;
9526b44f317SMartin Moene    int failures;
9536b44f317SMartin Moene
9546b44f317SMartin Moene    confirm( std::ostream & os, options option )
9556b44f317SMartin Moene    : action( os ), output( os, option.pass ), option( option ), selected( 0 ), failures( 0 ) {}
9566b44f317SMartin Moene
9576b44f317SMartin Moene    operator int() { return failures; }
9586b44f317SMartin Moene
9596b44f317SMartin Moene    bool abort() { return option.abort && failures > 0; }
9606b44f317SMartin Moene
9616b44f317SMartin Moene    confirm & operator()( test testing )
9626b44f317SMartin Moene    {
9636b44f317SMartin Moene        try
9646b44f317SMartin Moene        {
9656b44f317SMartin Moene            ++selected; testing.behaviour( output( testing.name ) );
9666b44f317SMartin Moene        }
9676b44f317SMartin Moene        catch( message const & e )
9686b44f317SMartin Moene        {
9696b44f317SMartin Moene            ++failures; report( os, e, testing.name );
9706b44f317SMartin Moene        }
9716b44f317SMartin Moene        return *this;
9726b44f317SMartin Moene    }
9736b44f317SMartin Moene
9746b44f317SMartin Moene    ~confirm()
9756b44f317SMartin Moene    {
9766b44f317SMartin Moene        if ( failures > 0 )
9776b44f317SMartin Moene        {
97883b5493dSMartin Moene            os << failures << " out of " << selected << " selected " << pluralise("test", selected) << " " << colourise( "failed.\n" );
9796b44f317SMartin Moene        }
9806b44f317SMartin Moene        else if ( option.pass )
9816b44f317SMartin Moene        {
98283b5493dSMartin Moene            os << "All " << selected << " selected " << pluralise("test", selected) << " " << colourise( "passed.\n" );
9836b44f317SMartin Moene        }
9846b44f317SMartin Moene    }
9856b44f317SMartin Moene};
9866b44f317SMartin Moene
9876b44f317SMartin Moenetemplate<typename Action>
9886b44f317SMartin Moenebool abort( Action & perform )
9896b44f317SMartin Moene{
9906b44f317SMartin Moene    return perform.abort();
9916b44f317SMartin Moene}
9926b44f317SMartin Moene
9936b44f317SMartin Moenetemplate< typename Action >
9946b44f317SMartin MoeneAction & for_test( tests specification, texts in, Action & perform, int n = 1 )
9956b44f317SMartin Moene{
9966b44f317SMartin Moene    for ( int i = 0; indefinite( n ) || i < n; ++i )
9976b44f317SMartin Moene    {
9986b44f317SMartin Moene        for ( tests::iterator pos = specification.begin(); pos != specification.end() ; ++pos )
9996b44f317SMartin Moene        {
10006b44f317SMartin Moene            test & testing = *pos;
10016b44f317SMartin Moene
10026b44f317SMartin Moene            if ( select( testing.name, in ) )
10036b44f317SMartin Moene                if ( abort( perform( testing ) ) )
10046b44f317SMartin Moene                    return perform;
10056b44f317SMartin Moene        }
10066b44f317SMartin Moene    }
10076b44f317SMartin Moene    return perform;
10086b44f317SMartin Moene}
10096b44f317SMartin Moene
10106b44f317SMartin Moeneinline bool test_less( test const & a, test const & b ) { return a.name < b.name; }
10116b44f317SMartin Moene
10126b44f317SMartin Moeneinline void sort( tests & specification )
10136b44f317SMartin Moene{
10146b44f317SMartin Moene    std::sort( specification.begin(), specification.end(), test_less );
10156b44f317SMartin Moene}
10166b44f317SMartin Moene
10176b44f317SMartin Moene// Use struct to avoid VC6 error C2664 when using free function:
10186b44f317SMartin Moene
10196b44f317SMartin Moenestruct rng { int operator()( int n ) { return lest::rand() % n; } };
10206b44f317SMartin Moene
10216b44f317SMartin Moeneinline void shuffle( tests & specification, options option )
10226b44f317SMartin Moene{
1023883fa058SMartin Moene#if lest_CPP11_OR_GREATER
10246b44f317SMartin Moene    std::shuffle( specification.begin(), specification.end(), std::mt19937( option.seed ) );
10256b44f317SMartin Moene#else
10265319565fSMagnus Bergsten    lest::srand( static_cast<unsigned int>( option.seed ) );
10276b44f317SMartin Moene
10286b44f317SMartin Moene    rng generator;
10296b44f317SMartin Moene    std::random_shuffle( specification.begin(), specification.end(), generator );
10306b44f317SMartin Moene#endif
10316b44f317SMartin Moene}
10326b44f317SMartin Moene
10336b44f317SMartin Moeneinline int stoi( text num )
10346b44f317SMartin Moene{
10355319565fSMagnus Bergsten    return static_cast<int>( lest::strtol( num.c_str(), NULL, 10 ) );
10366b44f317SMartin Moene}
10376b44f317SMartin Moene
10386b44f317SMartin Moeneinline bool is_number( text arg )
10396b44f317SMartin Moene{
10406b44f317SMartin Moene    const text digits = "0123456789";
10416b44f317SMartin Moene    return text::npos != arg.find_first_of    ( digits )
10426b44f317SMartin Moene        && text::npos == arg.find_first_not_of( digits );
10436b44f317SMartin Moene}
10446b44f317SMartin Moene
10456b44f317SMartin Moeneinline seed_t seed( text opt, text arg )
10466b44f317SMartin Moene{
10476b44f317SMartin Moene    // std::time_t: implementation dependent
10486b44f317SMartin Moene
10496b44f317SMartin Moene    if ( arg == "time" )
10506b44f317SMartin Moene        return static_cast<seed_t>( time( NULL ) );
10516b44f317SMartin Moene
10526b44f317SMartin Moene    if ( is_number( arg ) )
1053c6087551SMartin Moene        return seed_t( lest::stoi( arg ) );
10546b44f317SMartin Moene
10556b44f317SMartin Moene    throw std::runtime_error( "expecting 'time' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" );
10566b44f317SMartin Moene}
10576b44f317SMartin Moene
10586b44f317SMartin Moeneinline int repeat( text opt, text arg )
10596b44f317SMartin Moene{
10606b44f317SMartin Moene    const int num = lest::stoi( arg );
10616b44f317SMartin Moene
10626b44f317SMartin Moene    if ( indefinite( num ) || num >= 0 )
10636b44f317SMartin Moene        return num;
10646b44f317SMartin Moene
10656b44f317SMartin Moene    throw std::runtime_error( "expecting '-1' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" );
10666b44f317SMartin Moene}
10676b44f317SMartin Moene
10686b44f317SMartin Moeneinline std::pair<text, text>
10696b44f317SMartin Moenesplit_option( text arg )
10706b44f317SMartin Moene{
10716b44f317SMartin Moene    text::size_type pos = arg.rfind( '=' );
10726b44f317SMartin Moene
10736b44f317SMartin Moene    return pos == text::npos
10746b44f317SMartin Moene                ? std::make_pair( arg, text() )
10756b44f317SMartin Moene                : std::make_pair( arg.substr( 0, pos ), arg.substr( pos + 1 ) );
10766b44f317SMartin Moene}
10776b44f317SMartin Moene
10786b44f317SMartin Moeneinline std::pair<options, texts>
10796b44f317SMartin Moenesplit_arguments( texts args )
10806b44f317SMartin Moene{
10816b44f317SMartin Moene    options option; texts in;
10826b44f317SMartin Moene
10836b44f317SMartin Moene    bool in_options = true;
10846b44f317SMartin Moene
10856b44f317SMartin Moene    for ( texts::iterator pos = args.begin(); pos != args.end() ; ++pos )
10866b44f317SMartin Moene    {
10876b44f317SMartin Moene        text opt, val, arg = *pos;
10886b44f317SMartin Moene        tie( opt, val ) = split_option( arg );
10896b44f317SMartin Moene
10906b44f317SMartin Moene        if ( in_options )
10916b44f317SMartin Moene        {
10926b44f317SMartin Moene            if      ( opt[0] != '-'                             ) { in_options     = false;           }
10936b44f317SMartin Moene            else if ( opt == "--"                               ) { in_options     = false; continue; }
10946b44f317SMartin Moene            else if ( opt == "-h"      || "--help"       == opt ) { option.help    =  true; continue; }
10956b44f317SMartin Moene            else if ( opt == "-a"      || "--abort"      == opt ) { option.abort   =  true; continue; }
10966b44f317SMartin Moene            else if ( opt == "-c"      || "--count"      == opt ) { option.count   =  true; continue; }
10976b44f317SMartin Moene            else if ( opt == "-g"      || "--list-tags"  == opt ) { option.tags    =  true; continue; }
10986b44f317SMartin Moene            else if ( opt == "-l"      || "--list-tests" == opt ) { option.list    =  true; continue; }
10996b44f317SMartin Moene            else if ( opt == "-t"      || "--time"       == opt ) { option.time    =  true; continue; }
11006b44f317SMartin Moene            else if ( opt == "-p"      || "--pass"       == opt ) { option.pass    =  true; continue; }
11016b44f317SMartin Moene            else if (                     "--version"    == opt ) { option.version =  true; continue; }
11026b44f317SMartin Moene            else if ( opt == "--order" && "declared"     == val ) { /* by definition */   ; continue; }
11036b44f317SMartin Moene            else if ( opt == "--order" && "lexical"      == val ) { option.lexical =  true; continue; }
11046b44f317SMartin Moene            else if ( opt == "--order" && "random"       == val ) { option.random  =  true; continue; }
11056b44f317SMartin Moene            else if ( opt == "--random-seed" ) { option.seed   = seed  ( "--random-seed", val ); continue; }
11066b44f317SMartin Moene            else if ( opt == "--repeat"      ) { option.repeat = repeat( "--repeat"     , val ); continue; }
11076b44f317SMartin Moene            else throw std::runtime_error( "unrecognised option '" + opt + "' (try option --help)" );
11086b44f317SMartin Moene        }
11096b44f317SMartin Moene        in.push_back( arg );
11106b44f317SMartin Moene    }
11116b44f317SMartin Moene    return std::make_pair( option, in );
11126b44f317SMartin Moene}
11136b44f317SMartin Moene
11146b44f317SMartin Moeneinline int usage( std::ostream & os )
11156b44f317SMartin Moene{
11166b44f317SMartin Moene    os <<
11176b44f317SMartin Moene        "\nUsage: test [options] [test-spec ...]\n"
11186b44f317SMartin Moene        "\n"
11196b44f317SMartin Moene        "Options:\n"
11206b44f317SMartin Moene        "  -h, --help         this help message\n"
11216b44f317SMartin Moene        "  -a, --abort        abort at first failure\n"
11226b44f317SMartin Moene        "  -c, --count        count selected tests\n"
11236b44f317SMartin Moene        "  -g, --list-tags    list tags of selected tests\n"
11246b44f317SMartin Moene        "  -l, --list-tests   list selected tests\n"
11256b44f317SMartin Moene        "  -p, --pass         also report passing tests\n"
11266b44f317SMartin Moene#if lest_FEATURE_TIME
11276b44f317SMartin Moene        "  -t, --time         list duration of selected tests\n"
11286b44f317SMartin Moene#endif
11296b44f317SMartin Moene        "  --order=declared   use source code test order (default)\n"
11306b44f317SMartin Moene        "  --order=lexical    use lexical sort test order\n"
11316b44f317SMartin Moene        "  --order=random     use random test order\n"
11326b44f317SMartin Moene        "  --random-seed=n    use n for random generator seed\n"
11336b44f317SMartin Moene        "  --random-seed=time use time for random generator seed\n"
11346b44f317SMartin Moene        "  --repeat=n         repeat selected tests n times (-1: indefinite)\n"
11356b44f317SMartin Moene        "  --version          report lest version and compiler used\n"
11366b44f317SMartin Moene        "  --                 end options\n"
11376b44f317SMartin Moene        "\n"
11386b44f317SMartin Moene        "Test specification:\n"
11396b44f317SMartin Moene        "  \"@\", \"*\" all tests, unless excluded\n"
11406b44f317SMartin Moene        "  empty    all tests, unless tagged [hide] or [.optional-name]\n"
11416b44f317SMartin Moene#if lest_FEATURE_REGEX_SEARCH
11426b44f317SMartin Moene        "  \"re\"     select tests that match regular expression\n"
11436b44f317SMartin Moene        "  \"!re\"    omit tests that match regular expression\n"
11446b44f317SMartin Moene#else
11456b44f317SMartin Moene        "  \"text\"   select tests that contain text (case insensitive)\n"
11466b44f317SMartin Moene        "  \"!text\"  omit tests that contain text (case insensitive)\n"
11476b44f317SMartin Moene#endif
11486b44f317SMartin Moene        ;
11496b44f317SMartin Moene    return 0;
11506b44f317SMartin Moene}
11516b44f317