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