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.2"
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_CPP11_OR_GREATER && ! lest_COMPILER_MSVC_VERSION
855    typedef unsigned long uint64_t;
856# elif lest_COMPILER_MSVC_VERSION >= 6 && lest_COMPILER_MSVC_VERSION < 10
857    typedef /*un*/signed __int64 uint64_t;
858# else
859    using ::uint64_t;
860# endif
861#else
862# if ! lest_CPP11_OR_GREATER
863    typedef unsigned long long uint64_t;
864# endif
865#endif
866
867#if lest_PLATFORM_IS_WINDOWS
868    inline uint64_t current_ticks()
869    {
870        static LARGE_INTEGER hz = { 0,0 }, hzo = { 0,0 };
871        if ( ! hz.QuadPart )
872        {
873            QueryPerformanceFrequency( &hz  );
874            QueryPerformanceCounter  ( &hzo );
875        }
876        LARGE_INTEGER t = { 0,0 }; QueryPerformanceCounter( &t );
877
878        return uint64_t( ( ( t.QuadPart - hzo.QuadPart ) * 1000000 ) / hz.QuadPart );
879    }
880#else
881    inline uint64_t current_ticks()
882    {
883        timeval t; gettimeofday( &t, NULL );
884        return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
885    }
886#endif
887
888struct timer
889{
890    const uint64_t start_ticks;
891
892    timer() : start_ticks( current_ticks() ) {}
893
894    double elapsed_seconds() const
895    {
896        return static_cast<double>( current_ticks() - start_ticks ) / 1e6;
897    }
898};
899
900struct times : action
901{
902    env output;
903    options option;
904    int selected;
905    int failures;
906
907    timer total;
908
909    times( std::ostream & os, options option )
910    : action( os ), output( os, option.pass ), option( option ), selected( 0 ), failures( 0 ), total()
911    {
912        os << std::setfill(' ') << std::fixed << std::setprecision( lest_FEATURE_TIME_PRECISION );
913    }
914
915    operator int() { return failures; }
916
917    bool abort() { return option.abort && failures > 0; }
918
919    times & operator()( test testing )
920    {
921        timer t;
922
923        try
924        {
925            testing.behaviour( output( testing.name ) );
926        }
927        catch( message const & )
928        {
929            ++failures;
930        }
931
932        os << std::setw(5) << ( 1000 * t.elapsed_seconds() ) << " ms: " << testing.name  << "\n";
933
934        return *this;
935    }
936
937    ~times()
938    {
939        os << "Elapsed time: " << std::setprecision(1) << total.elapsed_seconds() << " s\n";
940    }
941};
942#else
943struct times : action { times( std::ostream &, options ) : action( os ) {} };
944#endif
945
946struct confirm : action
947{
948    env output;
949    options option;
950    int selected;
951    int failures;
952
953    confirm( std::ostream & os, options option )
954    : action( os ), output( os, option.pass ), option( option ), selected( 0 ), failures( 0 ) {}
955
956    operator int() { return failures; }
957
958    bool abort() { return option.abort && failures > 0; }
959
960    confirm & operator()( test testing )
961    {
962        try
963        {
964            ++selected; testing.behaviour( output( testing.name ) );
965        }
966        catch( message const & e )
967        {
968            ++failures; report( os, e, testing.name );
969        }
970        return *this;
971    }
972
973    ~confirm()
974    {
975        if ( failures > 0 )
976        {
977            os << failures << " out of " << selected << " selected " << pluralise("test", selected) << " " << colourise( "failed.\n" );
978        }
979        else if ( option.pass )
980        {
981            os << "All " << selected << " selected " << pluralise("test", selected) << " " << colourise( "passed.\n" );
982        }
983    }
984};
985
986template<typename Action>
987bool abort( Action & perform )
988{
989    return perform.abort();
990}
991
992template< typename Action >
993Action & for_test( tests specification, texts in, Action & perform, int n = 1 )
994{
995    for ( int i = 0; indefinite( n ) || i < n; ++i )
996    {
997        for ( tests::iterator pos = specification.begin(); pos != specification.end() ; ++pos )
998        {
999            test & testing = *pos;
1000
1001            if ( select( testing.name, in ) )
1002                if ( abort( perform( testing ) ) )
1003                    return perform;
1004        }
1005    }
1006    return perform;
1007}
1008
1009inline bool test_less( test const & a, test const & b ) { return a.name < b.name; }
1010
1011inline void sort( tests & specification )
1012{
1013    std::sort( specification.begin(), specification.end(), test_less );
1014}
1015
1016// Use struct to avoid VC6 error C2664 when using free function:
1017
1018struct rng { int operator()( int n ) { return lest::rand() % n; } };
1019
1020inline void shuffle( tests & specification, options option )
1021{
1022#if lest_CPP11_OR_GREATER
1023    std::shuffle( specification.begin(), specification.end(), std::mt19937( option.seed ) );
1024#else
1025    lest::srand( option.seed );
1026
1027    rng generator;
1028    std::random_shuffle( specification.begin(), specification.end(), generator );
1029#endif
1030}
1031
1032inline int stoi( text num )
1033{
1034    return lest::strtol( num.c_str(), NULL, 10 );
1035}
1036
1037inline bool is_number( text arg )
1038{
1039    const text digits = "0123456789";
1040    return text::npos != arg.find_first_of    ( digits )
1041        && text::npos == arg.find_first_not_of( digits );
1042}
1043
1044inline seed_t seed( text opt, text arg )
1045{
1046    // std::time_t: implementation dependent
1047
1048    if ( arg == "time" )
1049        return static_cast<seed_t>( time( NULL ) );
1050
1051    if ( is_number( arg ) )
1052        return seed_t( lest::stoi( arg ) );
1053
1054    throw std::runtime_error( "expecting 'time' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" );
1055}
1056
1057inline int repeat( text opt, text arg )
1058{
1059    const int num = lest::stoi( arg );
1060
1061    if ( indefinite( num ) || num >= 0 )
1062        return num;
1063
1064    throw std::runtime_error( "expecting '-1' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" );
1065}
1066
1067inline std::pair<text, text>
1068split_option( text arg )
1069{
1070    text::size_type pos = arg.rfind( '=' );
1071
1072    return pos == text::npos
1073                ? std::make_pair( arg, text() )
1074                : std::make_pair( arg.substr( 0, pos ), arg.substr( pos + 1 ) );
1075}
1076
1077inline std::pair<options, texts>
1078split_arguments( texts args )
1079{
1080    options option; texts in;
1081
1082    bool in_options = true;
1083
1084    for ( texts::iterator pos = args.begin(); pos != args.end() ; ++pos )
1085    {
1086        text opt, val, arg = *pos;
1087        tie( opt, val ) = split_option( arg );
1088
1089        if ( in_options )
1090        {
1091            if      ( opt[0] != '-'                             ) { in_options     = false;           }
1092            else if ( opt == "--"                               ) { in_options     = false; continue; }
1093            else if ( opt == "-h"      || "--help"       == opt ) { option.help    =  true; continue; }
1094            else if ( opt == "-a"      || "--abort"      == opt ) { option.abort   =  true; continue; }
1095            else if ( opt == "-c"      || "--count"      == opt ) { option.count   =  true; continue; }
1096            else if ( opt == "-g"      || "--list-tags"  == opt ) { option.tags    =  true; continue; }
1097            else if ( opt == "-l"      || "--list-tests" == opt ) { option.list    =  true; continue; }
1098            else if ( opt == "-t"      || "--time"       == opt ) { option.time    =  true; continue; }
1099            else if ( opt == "-p"      || "--pass"       == opt ) { option.pass    =  true; continue; }
1100            else if (                     "--version"    == opt ) { option.version =  true; continue; }
1101            else if ( opt == "--order" && "declared"     == val ) { /* by definition */   ; continue; }
1102            else if ( opt == "--order" && "lexical"      == val ) { option.lexical =  true; continue; }
1103            else if ( opt == "--order" && "random"       == val ) { option.random  =  true; continue; }
1104            else if ( opt == "--random-seed" ) { option.seed   = seed  ( "--random-seed", val ); continue; }
1105            else if ( opt == "--repeat"      ) { option.repeat = repeat( "--repeat"     , val ); continue; }
1106            else throw std::runtime_error( "unrecognised option '" + opt + "' (try option --help)" );
1107        }
1108        in.push_back( arg );
1109    }
1110    return std::make_pair( option, in );
1111}
1112
1113inline int usage( std::ostream & os )
1114{
1115    os <<
1116        "\nUsage: test [options] [test-spec ...]\n"
1117        "\n"
1118        "Options:\n"
1119        "  -h, --help         this help message\n"
1120        "  -a, --abort        abort at first failure\n"
1121        "  -c, --count        count selected tests\n"
1122        "  -g, --list-tags    list tags of selected tests\n"
1123        "  -l, --list-tests   list selected tests\n"
1124        "  -p, --pass         also report passing tests\n"
1125#if lest_FEATURE_TIME
1126        "  -t, --time         list duration of selected tests\n"
1127#endif
1128        "  --order=declared   use source code test order (default)\n"
1129        "  --order=lexical    use lexical sort test order\n"
1130        "  --order=random     use random test order\n"
1131        "  --random-seed=n    use n for random generator seed\n"
1132        "  --random-seed=time use time for random generator seed\n"
1133        "  --repeat=n         repeat selected tests n times (-1: indefinite)\n"
1134        "  --version          report lest version and compiler used\n"
1135        "  --                 end options\n"
1136        "\n"
1137        "Test specification:\n"
1138        "  \"@\", \"*\" all tests, unless excluded\n"
1139        "  empty    all tests, unless tagged [hide] or [.optional-name]\n"
1140#if lest_FEATURE_REGEX_SEARCH
1141        "  \"re\"     select tests that match regular expression\n"
1142        "  \"!re\"    omit tests that match regular expression\n"
1143#else
1144        "  \"text\"   select tests that contain text (case insensitive)\n"
1145        "  \"!text\"  omit tests that contain text (case insensitive)\n"
1146#endif
1147        ;
1148    return 0;
1149}
1150
1151inline text compiler()
1152{
1153    std::ostringstream os;
1154#if   defined (__clang__ )
1155    os << "clang " << __clang_version__;
1156#elif defined (__GNUC__  )
1157    os << "gcc " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__;
1158#elif defined ( _MSC_VER )
1159    os << "MSVC " << lest_COMPILER_MSVC_VERSION << " (" << _MSC_VER << ")";
1160#else
1161    os << "[compiler]";
1162#endif
1163    return os.str();
1164}
1165
1166inline int version( std::ostream & os )
1167{
1168    os << "lest version "  << lest_VERSION << "\n"
1169       << "Compiled with " << compiler()   << " on " << __DATE__ << " at " << __TIME__ << ".\n"
1170       << "For more information, see https://github.com/martinmoene/lest.\n";
1171    return 0;
1172}
1173
1174inline int run( tests specification, texts arguments, std::ostream & os = std::cout )
1175{
1176    try
1177    {
1178        options option; texts in;
1179        tie( option, in ) = split_arguments( arguments );
1180
1181        if ( option.lexical ) {    sort( specification         ); }
1182        if ( option.random  ) { shuffle( specification, option ); }
1183
1184        if ( option.help    ) { return usage  ( os ); }
1185        if ( option.version ) { return version( os ); }
1186        if ( option.count   ) { count count_( os         ); return for_test( specification, in, count_ ); }
1187        if ( option.list    ) { print print_( os         ); return for_test( specification, in, print_ ); }
1188        if ( option.tags    ) { ptags ptags_( os         ); return for_test( specification, in, ptags_ ); }
1189        if ( option.time    ) { times times_( os, option ); return for_test( specification, in, times_ ); }
1190
1191        { confirm confirm_( os, option ); return for_test( specification, in, confirm_, option.repeat ); }
1192    }
1193    catch ( std::exception const & e )
1194    {
1195        os << "Error: " << e.what() << "\n";
1196        return 1;
1197    }
1198}
1199
1200// VC6: make<cont>(first,last) replaces cont(first,last)
1201
1202template<typename C, typename T>
1203C make( T const * first, T const * const last )
1204{
1205    C result;
1206    for ( ; first != last; ++first )
1207    {
1208       result.push_back( *first );
1209    }
1210    return result;
1211}
1212
1213inline tests make_tests( test const * first, test const * const last )
1214{
1215    return make<tests>( first, last );
1216}
1217
1218inline texts make_texts( char const * const * first, char const * const * last )
1219{
1220    return make<texts>( first, last );
1221}
1222
1223// Traversal of test[N] (test_specification[N]) set up to also work with MSVC6:
1224
1225template <typename C> test const *         test_begin( C const & c ) { return &*c; }
1226template <typename C> test const *           test_end( C const & c ) { return test_begin( c ) + lest_DIMENSION_OF( c ); }
1227
1228template <typename C> char const * const * text_begin( C const & c ) { return &*c; }
1229template <typename C> char const * const *   text_end( C const & c ) { return text_begin( c ) + lest_DIMENSION_OF( c ); }
1230
1231template <typename C> tests make_tests( C const & c ) { return make_tests( test_begin( c ), test_end( c ) ); }
1232template <typename C> texts make_texts( C const & c ) { return make_texts( text_begin( c ), text_end( c ) ); }
1233
1234inline int run( tests const & specification, int argc, char * argv[], std::ostream & os = std::cout )
1235{
1236    return run( specification, make_texts( argv + 1, argv + argc ), os  );
1237}
1238
1239inline int run( tests const & specification, std::ostream & os = std::cout )
1240{
1241    return run( specification, texts(), os );
1242}
1243
1244template <typename C>
1245int run(  C const & specification, texts args, std::ostream & os = std::cout )
1246{
1247    return run( make_tests( specification ), args, os  );
1248}
1249
1250template <typename C>
1251int run(  C const & specification, int argc, char * argv[], std::ostream & os = std::cout )
1252{
1253    return run( make_tests( specification ), argv, argc, os  );
1254}
1255
1256template <typename C>
1257int run(  C const & specification, std::ostream & os = std::cout )
1258{
1259    return run( make_tests( specification ), os  );
1260}
1261
1262} // namespace lest
1263
1264#endif // LEST_LEST_HPP_INCLUDED
1265