1// Copyright 2013, 2014, 2015 by Martin Moene
2//
3// lest is based on ideas by Kevlin Henney, see video at
4// http://skillsmatter.com/podcast/agile-testing/kevlin-henney-rethinking-unit-testing-in-c-plus-plus
5//
6// Distributed under the Boost Software License, Version 1.0. (See accompanying
7// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8
9#ifndef LEST_LEST_H_INCLUDED
10#define LEST_LEST_H_INCLUDED
11
12#include <algorithm>
13#include <iomanip>
14#include <iostream>
15#include <iterator>
16#include <limits>
17#include <sstream>
18#include <stdexcept>
19#include <set>
20#include <string>
21#include <utility>
22#include <vector>
23
24#include <cctype>
25#include <cmath>
26#include <cstddef>
27#include <cstdlib>
28#include <ctime>
29
30#ifdef __clang__
31# pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
32# pragma clang diagnostic ignored "-Woverloaded-shift-op-parentheses"
33# pragma clang diagnostic ignored "-Wunused-comparison"
34# pragma clang diagnostic ignored "-Wunused-value"
35#elif defined __GNUC__
36# pragma GCC   diagnostic ignored "-Wunused-value"
37#endif
38
39#define  lest_VERSION "1.24.3"
40
41#ifndef  lest_FEATURE_COLOURISE
42# define lest_FEATURE_COLOURISE 0
43#endif
44
45#ifndef  lest_FEATURE_LITERAL_SUFFIX
46# define lest_FEATURE_LITERAL_SUFFIX 0
47#endif
48
49#ifndef  lest_FEATURE_REGEX_SEARCH
50# define lest_FEATURE_REGEX_SEARCH 0
51#endif
52
53#ifndef  lest_FEATURE_TIME
54# define lest_FEATURE_TIME 1
55#endif
56
57#ifndef lest_FEATURE_TIME_PRECISION
58#define lest_FEATURE_TIME_PRECISION  0
59#endif
60
61#ifdef _WIN32
62# define lest_PLATFORM_IS_WINDOWS 1
63#endif
64
65#if lest_FEATURE_REGEX_SEARCH
66# include <regex>
67#endif
68
69#if lest_FEATURE_TIME
70# if lest_PLATFORM_IS_WINDOWS
71#  include <iomanip>
72#  include <windows.h>
73# else
74#  include <iomanip>
75#  include <sys/time.h>
76# endif
77#endif
78
79#if defined(_MSC_VER)
80# define lest_COMPILER_MSVC_VERSION   (_MSC_VER / 100 - 5 - (_MSC_VER < 1900))
81#else
82# define lest_COMPILER_MSVC_VERSION   0
83#endif
84
85#if lest_COMPILER_MSVC_VERSION == 6
86# define lest_COMPILER_IS_MSVC6  1
87#endif
88
89#if ( __cplusplus >= 201103L ) || lest_COMPILER_MSVC_VERSION >= 12
90# define lest_CPP11_OR_GREATER  1
91#endif
92
93#if lest_CPP11_OR_GREATER
94
95# include <cstdint>
96# include <random>
97# include <tuple>
98
99namespace lest
100{
101    using std::tie;
102}
103
104#else
105
106# if !defined(__clang__) && defined(__GNUC__)
107#  pragma GCC diagnostic push
108#  pragma GCC diagnostic ignored "-Weffc++"
109# endif
110
111namespace lest
112{
113    template< typename T1, typename T2>
114    struct Tie
115    {
116        Tie( T1 & first, T2 & second )
117        : first( first ), second( second ) {}
118
119        std::pair<T1, T2> const &
120        operator=( std::pair<T1, T2> const & rhs )
121        {
122            first  = rhs.first;
123            second = rhs.second;
124            return rhs;
125        }
126
127    private:
128        void operator=( Tie const & );
129
130        T1 & first;
131        T2 & second;
132    };
133
134    template<typename T1, typename T2>
135    inline Tie<T1,T2> tie( T1 & first, T2 & second )
136    {
137      return Tie<T1, T2>( first, second );
138    }
139
140# if !defined(__clang__) && defined(__GNUC__)
141#  pragma GCC diagnostic pop
142# endif
143
144}
145#endif // lest_CPP11_OR_GREATER
146
147namespace lest
148{
149#ifdef lest_COMPILER_IS_MSVC6
150    using ::strtol;
151    using ::rand;
152    using ::srand;
153
154    inline double abs( double x ) { return ::fabs( x ); }
155
156    template< typename T >
157    T const & (max)(T const & a, T const & b) { return a >= b ? a : b; }
158#else
159    using std::abs;
160    using std::max;
161    using std::strtol;
162    using std::rand;
163    using std::srand;
164#endif
165}
166
167#if ! defined( lest_NO_SHORT_MACRO_NAMES ) && ! defined( lest_NO_SHORT_ASSERTION_NAMES )
168# define SETUP             lest_SETUP
169# define SECTION           lest_SECTION
170
171# define EXPECT            lest_EXPECT
172# define EXPECT_NOT        lest_EXPECT_NOT
173# define EXPECT_NO_THROW   lest_EXPECT_NO_THROW
174# define EXPECT_THROWS     lest_EXPECT_THROWS
175# define EXPECT_THROWS_AS  lest_EXPECT_THROWS_AS
176
177# define SCENARIO          lest_SCENARIO
178# define GIVEN             lest_GIVEN
179# define WHEN              lest_WHEN
180# define THEN              lest_THEN
181# define AND_WHEN          lest_AND_WHEN
182# define AND_THEN          lest_AND_THEN
183#endif
184
185#define lest_SCENARIO( sketch  )  lest_CASE(    lest::text("Scenario: ") + sketch  )
186#define lest_GIVEN(    context )  lest_SETUP(   lest::text(   "Given: ") + context )
187#define lest_WHEN(     story   )  lest_SECTION( lest::text(   " When: ") + story   )
188#define lest_THEN(     story   )  lest_SECTION( lest::text(   " Then: ") + story   )
189#define lest_AND_WHEN( story   )  lest_SECTION( lest::text(   "  And: ") + story   )
190#define lest_AND_THEN( story   )  lest_SECTION( lest::text(   "  And: ") + story   )
191
192#define lest_TEST \
193    lest_CASE
194
195#define lest_CASE( specification, proposition ) \
196    void lest_FUNCTION( lest::env & ); \
197    namespace { lest::add_test lest_REGISTRAR( specification, lest::test( proposition, lest_FUNCTION ) ); } \
198    void lest_FUNCTION( lest::env & $ )
199
200#define lest_ADD_TEST( specification, test ) \
201    specification.push_back( test )
202
203#define lest_SETUP( context ) \
204    for ( int $section = 0, $count = 1; $section < $count; $count -= 0==$section++ )
205
206#define lest_SECTION( proposition ) \
207    static int lest_UNIQUE( id ) = 0; \
208    if ( lest::guard( lest_UNIQUE( id ), $section, $count ) ) \
209        for ( int $section = 0, $count = 1; $section < $count; $count -= 0==$section++ )
210
211#define lest_EXPECT( expr ) \
212    do { \
213        try \
214        { \
215            if ( lest::result score = lest_DECOMPOSE( expr ) ) \
216                throw lest::failure( lest_LOCATION, #expr, score.decomposition ); \
217            else if ( $.pass ) \
218                lest::report( $.os, lest::passing( lest_LOCATION, #expr, score.decomposition ), $.testing ); \
219        } \
220        catch(...) \
221        { \
222            lest::inform( lest_LOCATION, #expr ); \
223        } \
224    } while ( lest::is_false() )
225
226#define lest_EXPECT_NOT( expr ) \
227    do { \
228        try \
229        { \
230            if ( lest::result score = lest_DECOMPOSE( expr ) ) \
231            { \
232                if ( $.pass ) \
233                    lest::report( $.os, lest::passing( lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) ), $.testing ); \
234            } \
235            else \
236                throw lest::failure( lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) ); \
237        } \
238        catch(...) \
239        { \
240            lest::inform( lest_LOCATION, lest::not_expr( #expr ) ); \
241        } \
242    } while ( lest::is_false() )
243
244#define lest_EXPECT_NO_THROW( expr ) \
245    do \
246    { \
247        try { expr; } \
248        catch (...) { lest::inform( lest_LOCATION, #expr ); } \
249        if ( $.pass ) \
250            lest::report( $.os, lest::got_none( lest_LOCATION, #expr ), $.testing ); \
251    } while ( lest::is_false() )
252
253#define lest_EXPECT_THROWS( expr ) \
254    do \
255    { \
256        try \
257        { \
258            expr; \
259        } \
260        catch (...) \
261        { \
262            if ( $.pass ) \
263                lest::report( $.os, lest::got( lest_LOCATION, #expr ), $.testing ); \
264            break; \
265        } \
266        throw lest::expected( lest_LOCATION, #expr ); \
267    } \
268    while ( lest::is_false() )
269
270#define lest_EXPECT_THROWS_AS( expr, excpt ) \
271    do \
272    { \
273        try \
274        { \
275            expr; \
276        }  \
277        catch ( excpt & ) \
278        { \
279            if ( $.pass ) \
280                lest::report( $.os, lest::got( lest_LOCATION, #expr, lest::of_type( #excpt ) ), $.testing ); \
281            break; \
282        } \
283        catch (...) {} \
284        throw lest::expected( lest_LOCATION, #expr, lest::of_type( #excpt ) ); \
285    } \
286    while ( lest::is_false() )
287
288#define lest_DECOMPOSE( expr ) ( lest::expression_decomposer() << expr )
289
290#define lest_UNIQUE(  name       ) lest_UNIQUE2( name, __LINE__ )
291#define lest_UNIQUE2( name, line ) lest_UNIQUE3( name, line )
292#define lest_UNIQUE3( name, line ) name ## line
293
294#define lest_LOCATION  lest::location(__FILE__, __LINE__)
295
296#define lest_FUNCTION  lest_UNIQUE(__lest_function__  )
297#define lest_REGISTRAR lest_UNIQUE(__lest_registrar__ )
298
299#define lest_DIMENSION_OF( a ) ( sizeof(a) / sizeof(0[a]) )
300
301namespace lest {
302
303typedef std::string       text;
304typedef std::vector<text> texts;
305
306struct env;
307
308struct test
309{
310    text name;
311    void (* behaviour)( env & );
312
313    test( text name, void (* behaviour)( env & ) )
314    : name( name ), behaviour( behaviour ) {}
315};
316
317typedef std::vector<test> tests;
318typedef tests test_specification;
319
320struct add_test
321{
322    add_test( tests & specification, test const & test_case )
323    {
324        specification.push_back( test_case );
325    }
326};
327
328struct result
329{
330    const bool passed;
331    const text decomposition;
332
333    result( bool passed, text decomposition )
334    : passed( passed ), decomposition( decomposition ) {}
335    operator bool() { return ! passed; }
336};
337
338struct location
339{
340    const text file;
341    const int line;
342
343    location( text file, int line )
344    : file( file ), line( line ) {}
345};
346
347struct comment
348{
349    const text info;
350
351    comment( text info ) : info( info ) {}
352    operator bool() { return ! info.empty(); }
353};
354
355struct message : std::runtime_error
356{
357    const text kind;
358    const location where;
359    const comment note;
360
361    ~message() throw() {}
362
363    message( text kind, location where, text expr, text note = "" )
364    : std::runtime_error( expr ), kind( kind ), where( where ), note( note ) {}
365};
366
367struct failure : message
368{
369    failure( location where, text expr, text decomposition )
370    : message( "failed", where, expr + " for " + decomposition ) {}
371};
372
373struct success : message
374{
375    success( text kind, location where, text expr, text note = "" )
376    : message( kind, where, expr, note ) {}
377};
378
379struct passing : success
380{
381    passing( location where, text expr, text decomposition )
382    : success( "passed", where, expr + " for " + decomposition ) {}
383};
384
385struct got_none : success
386{
387    got_none( location where, text expr )
388    : success( "passed: got no exception", where, expr ) {}
389};
390
391struct got : success
392{
393    got( location where, text expr )
394    : success( "passed: got exception", where, expr ) {}
395
396    got( location where, text expr, text excpt )
397    : success( "passed: got exception " + excpt, where, expr ) {}
398};
399
400struct expected : message
401{
402    expected( location where, text expr, text excpt = "" )
403    : message( "failed: didn't get exception", where, expr, excpt ) {}
404};
405
406struct unexpected : message
407{
408    unexpected( location where, text expr, text note = "" )
409    : message( "failed: got unexpected exception", where, expr, note ) {}
410};
411
412struct guard
413{
414    int & id;
415    int const & section;
416
417    guard( int & id, int const & section, int & count )
418    : id( id ), section( section )
419    {
420        if ( section == 0 )
421            id = count++ - 1;
422    }
423    operator bool() { return id == section; }
424};
425
426class approx
427{
428public:
429    explicit approx ( double magnitude )
430    : epsilon_  ( std::numeric_limits<float>::epsilon() * 100 )
431    , scale_    ( 1.0 )
432    , magnitude_( magnitude ) {}
433
434    static approx custom() { return approx( 0 ); }
435
436    approx operator()( double magnitude )
437    {
438        approx approx ( magnitude );
439        approx.epsilon( epsilon_  );
440        approx.scale  ( scale_    );
441        return approx;
442    }
443
444    double magnitude() const { return magnitude_; }
445
446    approx & epsilon( double epsilon ) { epsilon_ = epsilon; return *this; }
447    approx & scale  ( double scale   ) { scale_   = scale;   return *this; }
448
449    friend bool operator == ( double lhs, approx const & rhs )
450    {
451        // Thanks to Richard Harris for his help refining this formula.
452        return lest::abs( lhs - rhs.magnitude_ ) < rhs.epsilon_ * ( rhs.scale_ + (lest::max)( lest::abs( lhs ), lest::abs( rhs.magnitude_ ) ) );
453    }
454
455    friend bool operator == ( approx const & lhs, double rhs ) { return  operator==( rhs, lhs ); }
456    friend bool operator != ( double lhs, approx const & rhs ) { return !operator==( lhs, rhs ); }
457    friend bool operator != ( approx const & lhs, double rhs ) { return !operator==( rhs, lhs ); }
458
459private:
460    double epsilon_;
461    double scale_;
462    double magnitude_;
463};
464
465inline bool is_false(           ) { return false; }
466inline bool is_true ( bool flag ) { return  flag; }
467
468inline text not_expr( text message )
469{
470    return "! ( " + message + " )";
471}
472
473inline text with_message( text message )
474{
475    return "with message \"" + message + "\"";
476}
477
478inline text of_type( text type )
479{
480    return "of type " + type;
481}
482
483inline void inform( location where, text expr )
484{
485    try
486    {
487        throw;
488    }
489    catch( failure const & )
490    {
491        throw;
492    }
493    catch( std::exception const & e )
494    {
495        throw unexpected( where, expr, with_message( e.what() ) ); \
496    }
497    catch(...)
498    {
499        throw unexpected( where, expr, "of unknown type" ); \
500    }
501}
502
503// Expression decomposition:
504
505#if lest_CPP11_OR_GREATER || lest_COMPILER_MSVC_VERSION >= 10
506inline std::string to_string( std::nullptr_t const &      ) { return "nullptr"; }
507#endif
508inline std::string to_string( std::string    const & text ) { return "\"" + text + "\"" ; }
509inline std::string to_string( char const *   const & text ) { return "\"" + std::string( text ) + "\"" ; }
510inline std::string to_string( char           const & text ) { return "\'" + std::string( 1, text ) + "\'" ; }
511
512inline std::ostream & operator<<( std::ostream & os, approx const & appr )
513{
514    return os << appr.magnitude();
515}
516
517template <typename T>
518std::string to_string( T const & value, int=0 /* VC6 */ )
519{
520    std::ostringstream os; os << std::boolalpha << value; return os.str();
521}
522
523template<typename T1, typename T2>
524std::string to_string( std::pair<T1,T2> const & pair )
525{
526    std::ostringstream oss;
527    oss << "{ " << to_string( pair.first ) << ", " << to_string( pair.second ) << " }";
528    return oss.str();
529}
530
531#if lest_CPP11_OR_GREATER
532
533template<typename TU, std::size_t N>
534struct make_tuple_string
535{
536    static std::string make( TU const & tuple )
537    {
538        std::ostringstream os;
539        os << to_string( std::get<N - 1>( tuple ) ) << ( N < std::tuple_size<TU>::value ? ", ": " ");
540        return make_tuple_string<TU, N - 1>::make( tuple ) + os.str();
541    }
542};
543
544template<typename TU>
545struct make_tuple_string<TU, 0>
546{
547    static std::string make( TU const & ) { return ""; }
548};
549
550template<typename ...TS>
551auto to_string( std::tuple<TS...> const & tuple ) -> std::string
552{
553    return "{ " + make_tuple_string<std::tuple<TS...>, sizeof...(TS)>::make( tuple ) + "}";
554}
555#endif
556
557template <typename L, typename R>
558std::string to_string( L const & lhs, std::string op, R const & rhs )
559{
560    std::ostringstream os; os << to_string( lhs ) << " " << op << " " << to_string( rhs ); return os.str();
561}
562
563template <typename L>
564struct expression_lhs
565{
566    L lhs;
567
568    expression_lhs( L lhs ) : lhs( lhs ) {}
569
570    operator result() { return result( lhs, to_string( lhs ) ); }
571
572    template <typename R> result operator==( R const & rhs ) { return result( lhs == rhs, to_string( lhs, "==", rhs ) ); }
573    template <typename R> result operator!=( R const & rhs ) { return result( lhs != rhs, to_string( lhs, "!=", rhs ) ); }
574    template <typename R> result operator< ( R const & rhs ) { return result( lhs <  rhs, to_string( lhs, "<" , rhs ) ); }
575    template <typename R> result operator<=( R const & rhs ) { return result( lhs <= rhs, to_string( lhs, "<=", rhs ) ); }
576    template <typename R> result operator> ( R const & rhs ) { return result( lhs >  rhs, to_string( lhs, ">" , rhs ) ); }
577    template <typename R> result operator>=( R const & rhs ) { return result( lhs >= rhs, to_string( lhs, ">=", rhs ) ); }
578};
579
580struct expression_decomposer
581{
582    template <typename L>
583    expression_lhs<L const &> operator<< ( L const & operand )
584    {
585        return expression_lhs<L const &>( operand );
586    }
587};
588
589// Reporter:
590
591#if lest_FEATURE_COLOURISE
592
593inline text red  ( text words ) { return "\033[1;31m" + words + "\033[0m"; }
594inline text green( text words ) { return "\033[1;32m" + words + "\033[0m"; }
595inline text gray ( text words ) { return "\033[1;30m" + words + "\033[0m"; }
596
597inline bool starts_with( text words, text with )
598{
599    return 0 == words.find( with );
600}
601
602inline text replace( text words, text from, text to )
603{
604    size_t pos = words.find( from );
605    return pos == std::string::npos ? words : words.replace( pos, from.length(), to  );
606}
607
608inline text colour( text words )
609{
610    if      ( starts_with( words, "failed" ) ) return replace( words, "failed", red  ( "failed" ) );
611    else if ( starts_with( words, "passed" ) ) return replace( words, "passed", green( "passed" ) );
612
613    return replace( words, "for", gray( "for" ) );
614}
615
616inline bool is_cout( std::ostream & os ) { return &os == &std::cout; }
617
618struct colourise
619{
620    const text words;
621
622    colourise( text words )
623    : words( words ) {}
624
625    // only colourise for std::cout, not for a stringstream as used in tests:
626
627    std::ostream & operator()( std::ostream & os ) const
628    {
629        return is_cout( os ) ? os << colour( words ) : os << words;
630    }
631};
632
633inline std::ostream & operator<<( std::ostream & os, colourise words ) { return words( os ); }
634#else
635inline text colourise( text words ) { return words; }
636#endif
637
638inline text pluralise( int n, text word )
639{
640    return n == 1 ? word : word + "s";
641}
642
643inline std::ostream & operator<<( std::ostream & os, comment note )
644{
645    return os << (note ? " " + note.info : "" );
646}
647
648inline std::ostream & operator<<( std::ostream & os, location where )
649{
650#ifdef __GNUG__
651    return os << where.file << ":" << where.line;
652#else
653    return os << where.file << "(" << where.line << ")";
654#endif
655}
656
657inline void report( std::ostream & os, message const & e, text test )
658{
659    os << e.where << ": " << colourise( e.kind ) << e.note << ": " << test << ": " << colourise( e.what() ) << std::endl;
660}
661
662// Test runner:
663
664#if lest_FEATURE_REGEX_SEARCH
665    inline bool search( text re, text line )
666    {
667        return std::regex_search( line, std::regex( re ) );
668    }
669#else
670    inline bool case_insensitive_equal( char a, char b )
671    {
672        return tolower( a ) == tolower( b );
673    }
674
675    inline bool search( text part, text line )
676    {
677        return std::search(
678            line.begin(), line.end(),
679            part.begin(), part.end(), case_insensitive_equal ) != line.end();
680    }
681#endif
682
683inline bool match( texts whats, text line )
684{
685    for ( texts::iterator what = whats.begin(); what != whats.end() ; ++what )
686    {
687        if ( search( *what, line ) )
688            return true;
689    }
690    return false;
691}
692
693inline bool hidden( text name )
694{
695#if lest_FEATURE_REGEX_SEARCH
696    texts skipped; skipped.push_back( "\\[\\.\\]" ); skipped.push_back( "\\[hide\\]" );
697#else
698    texts skipped; skipped.push_back( "[." ); skipped.push_back( "[hide]" );
699#endif
700    return match( skipped, name );
701}
702
703inline bool none( texts args )
704{
705    return args.size() == 0;
706}
707
708inline bool select( text name, texts include )
709{
710    if ( none( include ) )
711    {
712        return ! hidden( name );
713    }
714
715    bool any = false;
716    for ( texts::reverse_iterator pos = include.rbegin(); pos != include.rend(); ++pos )
717    {
718        text & part = *pos;
719
720        if ( part == "@" || part == "*" )
721            return true;
722
723        if ( search( part, name ) )
724            return true;
725
726        if ( '!' == part[0] )
727        {
728            any = true;
729            if ( search( part.substr(1), name ) )
730                return false;
731        }
732        else
733        {
734            any = false;
735        }
736    }
737    return any && ! hidden( name );
738}
739
740inline int indefinite( int repeat ) { return repeat == -1; }
741
742typedef unsigned long seed_t;
743
744struct options
745{
746    options()
747    : help(false), abort(false), count(false), list(false), tags(false), time(false)
748    , pass(false), lexical(false), random(false), version(false), repeat(1), seed(0) {}
749
750    bool help;
751    bool abort;
752    bool count;
753    bool list;
754    bool tags;
755    bool time;
756    bool pass;
757    bool lexical;
758    bool random;
759    bool version;
760    int  repeat;
761    seed_t seed;
762};
763
764struct env
765{
766    std::ostream & os;
767    bool pass;
768    text testing;
769
770    env( std::ostream & os, bool pass )
771    : os( os ), pass( pass ), testing() {}
772
773    env & operator()( text test )
774    {
775        testing = test; return *this;
776    }
777};
778
779struct action
780{
781    std::ostream & os;
782
783    action( std::ostream & os ) : os( os ) {}
784
785    operator      int() { return 0; }
786    bool        abort() { return false; }
787    action & operator()( test ) { return *this; }
788
789private:
790    action( action const & );
791};
792
793struct print : action
794{
795    print( std::ostream & os ) : action( os ) {}
796
797    print &  operator()( test testing )
798    {
799        os << testing.name << "\n"; return *this;
800    }
801};
802
803inline texts tags( text name, texts result = texts() )
804{
805    size_t none = std::string::npos;
806    size_t lb   = name.find_first_of( "[" );
807    size_t rb   = name.find_first_of( "]" );
808
809    if ( lb == none || rb == none )
810        return result;
811
812    result.push_back( name.substr( lb, rb - lb + 1 ) );
813
814    return tags( name.substr( rb + 1 ), result );
815}
816
817struct ptags : action
818{
819    std::set<text> result;
820
821    ptags( std::ostream & os ) : action( os ), result() {}
822
823    ptags & operator()( test testing )
824    {
825        texts tags_( tags( testing.name ) );
826        for ( texts::iterator pos = tags_.begin(); pos != tags_.end() ; ++pos )
827            result.insert( *pos );
828
829        return *this;
830    }
831
832    ~ptags()
833    {
834        std::copy( result.begin(), result.end(), std::ostream_iterator<text>( os, "\n" ) );
835    }
836};
837
838struct count : action
839{
840    int n;
841
842    count( std::ostream & os ) : action( os ), n( 0 ) {}
843
844    count & operator()( test ) { ++n; return *this; }
845
846    ~count()
847    {
848        os << n << " selected " << pluralise(n, "test") << "\n";
849    }
850};
851
852#if lest_FEATURE_TIME
853
854#if lest_PLATFORM_IS_WINDOWS
855# if lest_COMPILER_IS_MSVC6
856    typedef /*un*/signed __int64 uint64_t;
857# else
858    typedef unsigned long long uint64_t;
859# endif
860#else
861# if ! lest_CPP11_OR_GREATER
862    typedef unsigned long long uint64_t;
863# endif
864#endif
865
866#if lest_PLATFORM_IS_WINDOWS
867    inline uint64_t current_ticks()
868    {
869        static uint64_t hz = 0, hzo = 0;
870        if ( ! hz )
871        {
872            QueryPerformanceFrequency( (LARGE_INTEGER *) &hz  );
873            QueryPerformanceCounter  ( (LARGE_INTEGER *) &hzo );
874        }
875        uint64_t t; QueryPerformanceCounter( (LARGE_INTEGER *) &t );
876
877        return ( ( t - hzo ) * 1000000 ) / hz;
878    }
879#else
880    inline uint64_t current_ticks()
881    {
882        timeval t; gettimeofday( &t, NULL );
883        return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
884    }
885#endif
886
887struct timer
888{
889    const uint64_t start_ticks;
890
891    timer() : start_ticks( current_ticks() ) {}
892
893    double elapsed_seconds() const
894    {
895        return ( current_ticks() - start_ticks ) / 1e6;
896    }
897};
898
899struct times : action
900{
901    env output;
902    options option;
903    int selected;
904    int failures;
905
906    timer total;
907
908    times( std::ostream & os, options option )
909    : action( os ), output( os, option.pass ), option( option ), selected( 0 ), failures( 0 ), total()
910    {
911        os << std::setfill(' ') << std::fixed << std::setprecision( lest_FEATURE_TIME_PRECISION );
912    }
913
914    operator int() { return failures; }
915
916    bool abort() { return option.abort && failures > 0; }
917
918    times & operator()( test testing )
919    {
920        timer t;
921
922        try
923        {
924            testing.behaviour( output( testing.name ) );
925        }
926        catch( message const & )
927        {
928            ++failures;
929        }
930
931        os << std::setw(5) << ( 1000 * t.elapsed_seconds() ) << " ms: " << testing.name  << "\n";
932
933        return *this;
934    }
935
936    ~times()
937    {
938        os << "Elapsed time: " << std::setprecision(1) << total.elapsed_seconds() << " s\n";
939    }
940};
941#else
942struct times : action { times( std::ostream &, options ) : action( os ) {} };
943#endif
944
945struct confirm : action
946{
947    env output;
948    options option;
949    int selected;
950    int failures;
951
952    confirm( std::ostream & os, options option )
953    : action( os ), output( os, option.pass ), option( option ), selected( 0 ), failures( 0 ) {}
954
955    operator int() { return failures; }
956
957    bool abort() { return option.abort && failures > 0; }
958
959    confirm & operator()( test testing )
960    {
961        try
962        {
963            ++selected; testing.behaviour( output( testing.name ) );
964        }
965        catch( message const & e )
966        {
967            ++failures; report( os, e, testing.name );
968        }
969        return *this;
970    }
971
972    ~confirm()
973    {
974        if ( failures > 0 )
975        {
976            os << failures << " out of " << selected << " selected " << pluralise(selected, "test") << " " << colourise( "failed.\n" );
977        }
978        else if ( option.pass )
979        {
980            os << "All " << selected << " selected " << pluralise(selected, "test") << " " << colourise( "passed.\n" );
981        }
982    }
983};
984
985template<typename Action>
986bool abort( Action & perform )
987{
988    return perform.abort();
989}
990
991template< typename Action >
992Action & for_test( tests specification, texts in, Action & perform, int n = 1 )
993{
994    for ( int i = 0; indefinite( n ) || i < n; ++i )
995    {
996        for ( tests::iterator pos = specification.begin(); pos != specification.end() ; ++pos )
997        {
998            test & testing = *pos;
999
1000            if ( select( testing.name, in ) )
1001                if ( abort( perform( testing ) ) )
1002                    return perform;
1003        }
1004    }
1005    return perform;
1006}
1007
1008inline bool test_less( test const & a, test const & b ) { return a.name < b.name; }
1009
1010inline void sort( tests & specification )
1011{
1012    std::sort( specification.begin(), specification.end(), test_less );
1013}
1014
1015// Use struct to avoid VC6 error C2664 when using free function:
1016
1017struct rng { int operator()( int n ) { return lest::rand() % n; } };
1018
1019inline void shuffle( tests & specification, options option )
1020{
1021#if lest_CPP11_OR_GREATER
1022    std::shuffle( specification.begin(), specification.end(), std::mt19937( option.seed ) );
1023#else
1024    lest::srand( option.seed );
1025
1026    rng generator;
1027    std::random_shuffle( specification.begin(), specification.end(), generator );
1028#endif
1029}
1030
1031inline int stoi( text num )
1032{
1033    return lest::strtol( num.c_str(), NULL, 10 );
1034}
1035
1036inline bool is_number( text arg )
1037{
1038    const text digits = "0123456789";
1039    return text::npos != arg.find_first_of    ( digits )
1040        && text::npos == arg.find_first_not_of( digits );
1041}
1042
1043inline seed_t seed( text opt, text arg )
1044{
1045    // std::time_t: implementation dependent
1046
1047    if ( arg == "time" )
1048        return static_cast<seed_t>( time( NULL ) );
1049
1050    if ( is_number( arg ) )
1051        return lest::stoi( arg );
1052
1053    throw std::runtime_error( "expecting 'time' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" );
1054}
1055
1056inline int repeat( text opt, text arg )
1057{
1058    const int num = lest::stoi( arg );
1059
1060    if ( indefinite( num ) || num >= 0 )
1061        return num;
1062
1063    throw std::runtime_error( "expecting '-1' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" );
1064}
1065
1066inline std::pair<text, text>
1067split_option( text arg )
1068{
1069    text::size_type pos = arg.rfind( '=' );
1070
1071    return pos == text::npos
1072                ? std::make_pair( arg, text() )
1073                : std::make_pair( arg.substr( 0, pos ), arg.substr( pos + 1 ) );
1074}
1075
1076inline std::pair<options, texts>
1077split_arguments( texts args )
1078{
1079    options option; texts in;
1080
1081    bool in_options = true;
1082
1083    for ( texts::iterator pos = args.begin(); pos != args.end() ; ++pos )
1084    {
1085        text opt, val, arg = *pos;
1086        tie( opt, val ) = split_option( arg );
1087
1088        if ( in_options )
1089        {
1090            if      ( opt[0] != '-'                             ) { in_options     = false;           }
1091            else if ( opt == "--"                               ) { in_options     = false; continue; }
1092            else if ( opt == "-h"      || "--help"       == opt ) { option.help    =  true; continue; }
1093            else if ( opt == "-a"      || "--abort"      == opt ) { option.abort   =  true; continue; }
1094            else if ( opt == "-c"      || "--count"      == opt ) { option.count   =  true; continue; }
1095            else if ( opt == "-g"      || "--list-tags"  == opt ) { option.tags    =  true; continue; }
1096            else if ( opt == "-l"      || "--list-tests" == opt ) { option.list    =  true; continue; }
1097            else if ( opt == "-t"      || "--time"       == opt ) { option.time    =  true; continue; }
1098            else if ( opt == "-p"      || "--pass"       == opt ) { option.pass    =  true; continue; }
1099            else if (                     "--version"    == opt ) { option.version =  true; continue; }
1100            else if ( opt == "--order" && "declared"     == val ) { /* by definition */   ; continue; }
1101            else if ( opt == "--order" && "lexical"      == val ) { option.lexical =  true; continue; }
1102            else if ( opt == "--order" && "random"       == val ) { option.random  =  true; continue; }
1103            else if ( opt == "--random-seed" ) { option.seed   = seed  ( "--random-seed", val ); continue; }
1104            else if ( opt == "--repeat"      ) { option.repeat = repeat( "--repeat"     , val ); continue; }
1105            else throw std::runtime_error( "unrecognised option '" + opt + "' (try option --help)" );
1106        }
1107        in.push_back( arg );
1108    }
1109    return std::make_pair( option, in );
1110}
1111
1112inline int usage( std::ostream & os )
1113{
1114    os <<
1115        "\nUsage: test [options] [test-spec ...]\n"
1116        "\n"
1117        "Options:\n"
1118        "  -h, --help         this help message\n"
1119        "  -a, --abort        abort at first failure\n"
1120        "  -c, --count        count selected tests\n"
1121        "  -g, --list-tags    list tags of selected tests\n"
1122        "  -l, --list-tests   list selected tests\n"
1123        "  -p, --pass         also report passing tests\n"
1124#if lest_FEATURE_TIME
1125        "  -t, --time         list duration of selected tests\n"
1126#endif
1127        "  --order=declared   use source code test order (default)\n"
1128        "  --order=lexical    use lexical sort test order\n"
1129        "  --order=random     use random test order\n"
1130        "  --random-seed=n    use n for random generator seed\n"
1131        "  --random-seed=time use time for random generator seed\n"
1132        "  --repeat=n         repeat selected tests n times (-1: indefinite)\n"
1133        "  --version          report lest version and compiler used\n"
1134        "  --                 end options\n"
1135        "\n"
1136        "Test specification:\n"
1137        "  \"@\", \"*\" all tests, unless excluded\n"
1138        "  empty    all tests, unless tagged [hide] or [.optional-name]\n"
1139#if lest_FEATURE_REGEX_SEARCH
1140        "  \"re\"     select tests that match regular expression\n"
1141        "  \"!re\"    omit tests that match regular expression\n"
1142#else
1143        "  \"text\"   select tests that contain text (case insensitive)\n"
1144        "  \"!text\"  omit tests that contain text (case insensitive)\n"
1145#endif
1146        ;
1147    return 0;
1148}
1149
1150inline text compiler()
1151{
1152    std::ostringstream os;
1153#if   defined (__clang__ )
1154    os << "clang " << __clang_version__;
1155#elif defined (__GNUC__  )
1156    os << "gcc " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__;
1157#elif defined ( _MSC_VER )
1158    os << "MSVC " << lest_COMPILER_MSVC_VERSION << " (" << _MSC_VER << ")";
1159#else
1160    os << "[compiler]";
1161#endif
1162    return os.str();
1163}
1164
1165inline int version( std::ostream & os )
1166{
1167    os << "lest version "  << lest_VERSION << "\n"
1168       << "Compiled with " << compiler()   << " on " << __DATE__ << " at " << __TIME__ << ".\n"
1169       << "For more information, see https://github.com/martinmoene/lest.\n";
1170    return 0;
1171}
1172
1173inline int run( tests specification, texts arguments, std::ostream & os = std::cout )
1174{
1175    try
1176    {
1177        options option; texts in;
1178        tie( option, in ) = split_arguments( arguments );
1179
1180        if ( option.lexical ) {    sort( specification         ); }
1181        if ( option.random  ) { shuffle( specification, option ); }
1182
1183        if ( option.help    ) { return usage  ( os ); }
1184        if ( option.version ) { return version( os ); }
1185        if ( option.count   ) { count count_( os         ); return for_test( specification, in, count_ ); }
1186        if ( option.list    ) { print print_( os         ); return for_test( specification, in, print_ ); }
1187        if ( option.tags    ) { ptags ptags_( os         ); return for_test( specification, in, ptags_ ); }
1188        if ( option.time    ) { times times_( os, option ); return for_test( specification, in, times_ ); }
1189
1190        { confirm confirm_( os, option ); return for_test( specification, in, confirm_, option.repeat ); }
1191    }
1192    catch ( std::exception const & e )
1193    {
1194        os << "Error: " << e.what() << "\n";
1195        return 1;
1196    }
1197}
1198
1199// VC6: make<cont>(first,last) replaces cont(first,last)
1200
1201template<typename C, typename T>
1202C make( T const * first, T const * const last )
1203{
1204    C result;
1205    for ( ; first != last; ++first )
1206    {
1207       result.push_back( *first );
1208    }
1209    return result;
1210}
1211
1212inline tests make_tests( test const * first, test const * const last )
1213{
1214    return make<tests>( first, last );
1215}
1216
1217inline texts make_texts( char const * const * first, char const * const * last )
1218{
1219    return make<texts>( first, last );
1220}
1221
1222// Traversal of test[N] (test_specification[N]) set up to also work with MSVC6:
1223
1224template <typename C> test const *         test_begin( C const & c ) { return &*c; }
1225template <typename C> test const *           test_end( C const & c ) { return test_begin( c ) + lest_DIMENSION_OF( c ); }
1226
1227template <typename C> char const * const * text_begin( C const & c ) { return &*c; }
1228template <typename C> char const * const *   text_end( C const & c ) { return text_begin( c ) + lest_DIMENSION_OF( c ); }
1229
1230template <typename C> tests make_tests( C const & c ) { return make_tests( test_begin( c ), test_end( c ) ); }
1231template <typename C> texts make_texts( C const & c ) { return make_texts( text_begin( c ), text_end( c ) ); }
1232
1233inline int run( tests const & specification, int argc, char * argv[], std::ostream & os = std::cout )
1234{
1235    return run( specification, make_texts( argv + 1, argv + argc ), os  );
1236}
1237
1238inline int run( tests const & specification, std::ostream & os = std::cout )
1239{
1240    return run( specification, texts(), os );
1241}
1242
1243template <typename C>
1244int run(  C const & specification, texts args, std::ostream & os = std::cout )
1245{
1246    return run( make_tests( specification ), args, os  );
1247}
1248
1249template <typename C>
1250int run(  C const & specification, int argc, char * argv[], std::ostream & os = std::cout )
1251{
1252    return run( make_tests( specification ), argv, argc, os  );
1253}
1254
1255template <typename C>
1256int run(  C const & specification, std::ostream & os = std::cout )
1257{
1258    return run( make_tests( specification ), os  );
1259}
1260
1261} // namespace lest
1262
1263#endif // LEST_LEST_H_INCLUDED
1264