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