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