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.30.1"
39
40#ifndef  lest_FEATURE_COLOURISE
41# define lest_FEATURE_COLOURISE 0
42#endif
43
44#ifndef  lest_FEATURE_LITERAL_SUFFIX
45# define lest_FEATURE_LITERAL_SUFFIX 0
46#endif
47
48#ifndef  lest_FEATURE_REGEX_SEARCH
49# define lest_FEATURE_REGEX_SEARCH 0
50#endif
51
52#ifndef  lest_FEATURE_TIME
53# define lest_FEATURE_TIME 1
54#endif
55
56#ifndef lest_FEATURE_TIME_PRECISION
57#define lest_FEATURE_TIME_PRECISION  0
58#endif
59
60#ifdef _WIN32
61# define lest_PLATFORM_IS_WINDOWS 1
62#endif
63
64#if lest_FEATURE_REGEX_SEARCH
65# include <regex>
66#endif
67
68#if lest_FEATURE_TIME
69# if lest_PLATFORM_IS_WINDOWS
70#  include <iomanip>
71#  include <windows.h>
72# else
73#  include <iomanip>
74#  include <sys/time.h>
75# endif
76#endif
77
78#if defined(_MSC_VER)
79# define lest_COMPILER_MSVC_VERSION   (_MSC_VER / 100 - 5 - (_MSC_VER < 1900))
80#else
81# define lest_COMPILER_MSVC_VERSION   0
82#endif
83
84#if lest_COMPILER_MSVC_VERSION == 6
85# define lest_COMPILER_IS_MSVC6  1
86#endif
87
88#define lest_CPP11_OR_GREATER  ((__cplusplus >= 201103L ) || lest_COMPILER_MSVC_VERSION >= 12)
89
90#if lest_CPP11_OR_GREATER || lest_COMPILER_MSVC_VERSION >= 10
91
92# include <cstdint>
93# include <random>
94# include <tuple>
95
96namespace lest
97{
98    using std::tie;
99}
100
101#else
102
103# if !defined(__clang__) && defined(__GNUC__)
104#  pragma GCC diagnostic push
105#  pragma GCC diagnostic ignored "-Weffc++"
106# endif
107
108namespace lest
109{
110    template< typename T1, typename T2>
111    struct Tie
112    {
113        Tie( T1 & first, T2 & second )
114        : first( first ), second( second ) {}
115
116        std::pair<T1, T2> const &
117        operator=( std::pair<T1, T2> const & rhs )
118        {
119            first  = rhs.first;
120            second = rhs.second;
121            return rhs;
122        }
123
124    private:
125        void operator=( Tie const & );
126
127        T1 & first;
128        T2 & second;
129    };
130
131    template<typename T1, typename T2>
132    inline Tie<T1,T2> tie( T1 & first, T2 & second )
133    {
134      return Tie<T1, T2>( first, second );
135    }
136
137# if !defined(__clang__) && defined(__GNUC__)
138#  pragma GCC diagnostic pop
139# endif
140
141}
142#endif // lest_CPP11_OR_GREATER
143
144namespace lest
145{
146#ifdef lest_COMPILER_IS_MSVC6
147    using ::strtol;
148    using ::rand;
149    using ::srand;
150
151    inline double abs( double x ) { return ::fabs( x ); }
152
153    template< typename T >
154    T const & (min)(T const & a, T const & b) { return a <= b ? a : b; }
155#else
156    using std::abs;
157    using std::min;
158    using std::strtol;
159    using std::rand;
160    using std::srand;
161#endif
162}
163
164#if ! defined( lest_NO_SHORT_MACRO_NAMES ) && ! defined( lest_NO_SHORT_ASSERTION_NAMES )
165# define SETUP             lest_SETUP
166# define SECTION           lest_SECTION
167
168# define EXPECT            lest_EXPECT
169# define EXPECT_NOT        lest_EXPECT_NOT
170# define EXPECT_NO_THROW   lest_EXPECT_NO_THROW
171# define EXPECT_THROWS     lest_EXPECT_THROWS
172# define EXPECT_THROWS_AS  lest_EXPECT_THROWS_AS
173
174# define SCENARIO          lest_SCENARIO
175# define GIVEN             lest_GIVEN
176# define WHEN              lest_WHEN
177# define THEN              lest_THEN
178# define AND_WHEN          lest_AND_WHEN
179# define AND_THEN          lest_AND_THEN
180#endif
181
182#define lest_SCENARIO( sketch  )  lest_CASE(    lest::text("Scenario: ") + sketch  )
183#define lest_GIVEN(    context )  lest_SETUP(   lest::text(   "Given: ") + context )
184#define lest_WHEN(     story   )  lest_SECTION( lest::text(   " When: ") + story   )
185#define lest_THEN(     story   )  lest_SECTION( lest::text(   " Then: ") + story   )
186#define lest_AND_WHEN( story   )  lest_SECTION( lest::text(   "  And: ") + story   )
187#define lest_AND_THEN( story   )  lest_SECTION( lest::text(   "  And: ") + story   )
188
189#define lest_CASE( specification, proposition ) \
190    static void lest_FUNCTION( lest::env & ); \
191    namespace { lest::add_test lest_REGISTRAR( specification, lest::test( proposition, lest_FUNCTION ) ); } \
192    static void lest_FUNCTION( lest::env & lest_env )
193
194#define lest_ADD_TEST( specification, test ) \
195    specification.push_back( test )
196
197#define lest_SETUP( context ) \
198    for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ )
199
200#define lest_SECTION( proposition ) \
201    static int lest_UNIQUE( id ) = 0; \
202    if ( lest::guard( lest_UNIQUE( id ), lest__section, lest__count ) ) \
203        for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ )
204
205#define lest_EXPECT( expr ) \
206    do { \
207        try \
208        { \
209            if ( lest::result score = lest_DECOMPOSE( expr ) ) \
210                throw lest::failure( lest_LOCATION, #expr, score.decomposition ); \
211            else if ( lest_env.pass ) \
212                lest::report( lest_env.os, lest::passing( lest_LOCATION, #expr, score.decomposition ), lest_env.testing ); \
213        } \
214        catch(...) \
215        { \
216            lest::inform( lest_LOCATION, #expr ); \
217        } \
218    } while ( lest::is_false() )
219
220#define lest_EXPECT_NOT( expr ) \
221    do { \
222        try \
223        { \
224            if ( lest::result score = lest_DECOMPOSE( expr ) ) \
225            { \
226                if ( lest_env.pass ) \
227                    lest::report( lest_env.os, lest::passing( lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) ), lest_env.testing ); \
228            } \
229            else \
230                throw lest::failure( lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) ); \
231        } \
232        catch(...) \
233        { \
234            lest::inform( lest_LOCATION, lest::not_expr( #expr ) ); \
235        } \
236    } while ( lest::is_false() )
237
238#define lest_EXPECT_NO_THROW( expr ) \
239    do \
240    { \
241        try { expr; } \
242        catch (...) { lest::inform( lest_LOCATION, #expr ); } \
243        if ( lest_env.pass ) \
244            lest::report( lest_env.os, lest::got_none( lest_LOCATION, #expr ), lest_env.testing ); \
245    } while ( lest::is_false() )
246
247#define lest_EXPECT_THROWS( expr ) \
248    do \
249    { \
250        try \
251        { \
252            expr; \
253        } \
254        catch (...) \
255        { \
256            if ( lest_env.pass ) \
257                lest::report( lest_env.os, lest::got( lest_LOCATION, #expr ), lest_env.testing ); \
258            break; \
259        } \
260        throw lest::expected( lest_LOCATION, #expr ); \
261    } \
262    while ( lest::is_false() )
263
264#define lest_EXPECT_THROWS_AS( expr, excpt ) \
265    do \
266    { \
267        try \
268        { \
269            expr; \
270        }  \
271        catch ( excpt & ) \
272        { \
273            if ( lest_env.pass ) \
274                lest::report( lest_env.os, lest::got( lest_LOCATION, #expr, lest::of_type( #excpt ) ), lest_env.testing ); \
275            break; \
276        } \
277        catch (...) {} \
278        throw lest::expected( lest_LOCATION, #expr, lest::of_type( #excpt ) ); \
279    } \
280    while ( lest::is_false() )
281
282#define lest_DECOMPOSE( expr ) ( lest::expression_decomposer() << expr )
283
284#define lest_UNIQUE(  name       ) lest_UNIQUE2( name, __LINE__ )
285#define lest_UNIQUE2( name, line ) lest_UNIQUE3( name, line )
286#define lest_UNIQUE3( name, line ) name ## line
287
288#define lest_LOCATION  lest::location(__FILE__, __LINE__)
289
290#define lest_FUNCTION  lest_UNIQUE(__lest_function__  )
291#define lest_REGISTRAR lest_UNIQUE(__lest_registrar__ )
292
293#define lest_DIMENSION_OF( a ) ( sizeof(a) / sizeof(0[a]) )
294
295namespace lest {
296
297typedef std::string       text;
298typedef std::vector<text> texts;
299
300struct env;
301
302struct test
303{
304    text name;
305    void (* behaviour)( env & );
306
307    test( text name, void (* behaviour)( env & ) )
308    : name( name ), behaviour( behaviour ) {}
309};
310
311typedef std::vector<test> tests;
312typedef tests test_specification;
313
314struct add_test
315{
316    add_test( tests & specification, test const & test_case )
317    {
318        specification.push_back( test_case );
319    }
320};
321
322struct result
323{
324    const bool passed;
325    const text decomposition;
326
327    result( bool passed, text decomposition )
328    : passed( passed ), decomposition( decomposition ) {}
329    operator bool() { return ! passed; }
330};
331
332struct location
333{
334    const text file;
335    const int line;
336
337    location( text file, int line )
338    : file( file ), line( line ) {}
339};
340
341struct comment
342{
343    const text info;
344
345    comment( text info ) : info( info ) {}
346    operator bool() { return ! info.empty(); }
347};
348
349struct message : std::runtime_error
350{
351    const text kind;
352    const location where;
353    const comment note;
354
355    ~message() throw() {}
356
357    message( text kind, location where, text expr, text note = "" )
358    : std::runtime_error( expr ), kind( kind ), where( where ), note( note ) {}
359};
360
361struct failure : message
362{
363    failure( location where, text expr, text decomposition )
364    : message( "failed", where, expr + " for " + decomposition ) {}
365};
366
367struct success : message
368{
369    success( text kind, location where, text expr, text note = "" )
370    : message( kind, where, expr, note ) {}
371};
372
373struct passing : success
374{
375    passing( location where, text expr, text decomposition )
376    : success( "passed", where, expr + " for " + decomposition ) {}
377};
378
379struct got_none : success
380{
381    got_none( location where, text expr )
382    : success( "passed: got no exception", where, expr ) {}
383};
384
385struct got : success
386{
387    got( location where, text expr )
388    : success( "passed: got exception", where, expr ) {}
389
390    got( location where, text expr, text excpt )
391    : success( "passed: got exception " + excpt, where, expr ) {}
392};
393
394struct expected : message
395{
396    expected( location where, text expr, text excpt = "" )
397    : message( "failed: didn't get exception", where, expr, excpt ) {}
398};
399
400struct unexpected : message
401{
402    unexpected( location where, text expr, text note = "" )
403    : message( "failed: got unexpected exception", where, expr, note ) {}
404};
405
406struct guard
407{
408    int & id;
409    int const & section;
410
411    guard( int & id, int const & section, int & count )
412    : id( id ), section( section )
413    {
414        if ( section == 0 )
415            id = count++ - 1;
416    }
417    operator bool() { return id == section; }
418};
419
420class approx
421{
422public:
423    explicit approx ( double magnitude )
424    : epsilon_  ( std::numeric_limits<float>::epsilon() * 100 )
425    , scale_    ( 1.0 )
426    , magnitude_( magnitude ) {}
427
428    static approx custom() { return approx( 0 ); }
429
430    approx operator()( double magnitude )
431    {
432        approx approx ( magnitude );
433        approx.epsilon( epsilon_  );
434        approx.scale  ( scale_    );
435        return approx;
436    }
437
438    double magnitude() const { return magnitude_; }
439
440    approx & epsilon( double epsilon ) { epsilon_ = epsilon; return *this; }
441    approx & scale  ( double scale   ) { scale_   = scale;   return *this; }
442
443    friend bool operator == ( double lhs, approx const & rhs )
444    {
445        // Thanks to Richard Harris for his help refining this formula.
446        return lest::abs( lhs - rhs.magnitude_ ) < rhs.epsilon_ * ( rhs.scale_ + (lest::min)( lest::abs( lhs ), lest::abs( rhs.magnitude_ ) ) );
447    }
448
449    friend bool operator == ( approx const & lhs, double rhs ) { return  operator==( rhs, lhs ); }
450    friend bool operator != ( double lhs, approx const & rhs ) { return !operator==( lhs, rhs ); }
451    friend bool operator != ( approx const & lhs, double rhs ) { return !operator==( rhs, lhs ); }
452
453    friend bool operator <= ( double lhs, approx const & rhs ) { return lhs < rhs.magnitude_ || lhs == rhs; }
454    friend bool operator <= ( approx const & lhs, double rhs ) { return lhs.magnitude_ < rhs || lhs == rhs; }
455    friend bool operator >= ( double lhs, approx const & rhs ) { return lhs > rhs.magnitude_ || lhs == rhs; }
456    friend bool operator >= ( approx const & lhs, double rhs ) { return lhs.magnitude_ > rhs || lhs == rhs; }
457
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	void operator=(action const & );
791};
792
793struct print : action
794{
795    print( std::ostream & os ) : action( os ) {}
796
797    print &  operator()( test testing )
798    {
799        os << testing.name << "\n"; return *this;
800    }
801};
802
803inline texts tags( text name, texts result = texts() )
804{
805    size_t none = std::string::npos;
806    size_t lb   = name.find_first_of( "[" );
807    size_t rb   = name.find_first_of( "]" );
808
809    if ( lb == none || rb == none )
810        return result;
811
812    result.push_back( name.substr( lb, rb - lb + 1 ) );
813
814    return tags( name.substr( rb + 1 ), result );
815}
816
817struct ptags : action
818{
819    std::set<text> result;
820
821    ptags( std::ostream & os ) : action( os ), result() {}
822
823    ptags & operator()( test testing )
824    {
825        texts tags_( tags( testing.name ) );
826        for ( texts::iterator pos = tags_.begin(); pos != tags_.end() ; ++pos )
827            result.insert( *pos );
828
829        return *this;
830    }
831
832    ~ptags()
833    {
834        std::copy( result.begin(), result.end(), std::ostream_iterator<text>( os, "\n" ) );
835    }
836};
837
838struct count : action
839{
840    int n;
841
842    count( std::ostream & os ) : action( os ), n( 0 ) {}
843
844    count & operator()( test ) { ++n; return *this; }
845
846    ~count()
847    {
848        os << n << " selected " << pluralise("test", n) << "\n";
849    }
850};
851
852#if lest_FEATURE_TIME
853
854#if lest_PLATFORM_IS_WINDOWS
855# if ! lest_CPP11_OR_GREATER && ! lest_COMPILER_MSVC_VERSION
856    typedef unsigned long uint64_t;
857# elif lest_COMPILER_MSVC_VERSION >= 6 && lest_COMPILER_MSVC_VERSION < 10
858    typedef /*un*/signed __int64 uint64_t;
859# else
860    using ::uint64_t;
861# endif
862#else
863# if ! lest_CPP11_OR_GREATER
864    typedef unsigned long long uint64_t;
865# endif
866#endif
867
868#if lest_PLATFORM_IS_WINDOWS
869    inline uint64_t current_ticks()
870    {
871        static LARGE_INTEGER hz = { 0,0 }, hzo = { 0,0 };
872        if ( ! hz.QuadPart )
873        {
874            QueryPerformanceFrequency( &hz  );
875            QueryPerformanceCounter  ( &hzo );
876        }
877        LARGE_INTEGER t = { 0,0 }; QueryPerformanceCounter( &t );
878
879        return uint64_t( ( ( t.QuadPart - hzo.QuadPart ) * 1000000 ) / hz.QuadPart );
880    }
881#else
882    inline uint64_t current_ticks()
883    {
884        timeval t; gettimeofday( &t, NULL );
885        return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
886    }
887#endif
888
889struct timer
890{
891    const uint64_t start_ticks;
892
893    timer() : start_ticks( current_ticks() ) {}
894
895    double elapsed_seconds() const
896    {
897        return static_cast<double>( current_ticks() - start_ticks ) / 1e6;
898    }
899};
900
901struct times : action
902{
903    env output;
904    options option;
905    int selected;
906    int failures;
907
908    timer total;
909
910    times( std::ostream & os, options option )
911    : action( os ), output( os, option.pass ), option( option ), selected( 0 ), failures( 0 ), total()
912    {
913        os << std::setfill(' ') << std::fixed << std::setprecision( lest_FEATURE_TIME_PRECISION );
914    }
915
916    operator int() { return failures; }
917
918    bool abort() { return option.abort && failures > 0; }
919
920    times & operator()( test testing )
921    {
922        timer t;
923
924        try
925        {
926            testing.behaviour( output( testing.name ) );
927        }
928        catch( message const & )
929        {
930            ++failures;
931        }
932
933        os << std::setw(5) << ( 1000 * t.elapsed_seconds() ) << " ms: " << testing.name  << "\n";
934
935        return *this;
936    }
937
938    ~times()
939    {
940        os << "Elapsed time: " << std::setprecision(1) << total.elapsed_seconds() << " s\n";
941    }
942};
943#else
944struct times : action { times( std::ostream &, options ) : action( os ) {} };
945#endif
946
947struct confirm : action
948{
949    env output;
950    options option;
951    int selected;
952    int failures;
953
954    confirm( std::ostream & os, options option )
955    : action( os ), output( os, option.pass ), option( option ), selected( 0 ), failures( 0 ) {}
956
957    operator int() { return failures; }
958
959    bool abort() { return option.abort && failures > 0; }
960
961    confirm & operator()( test testing )
962    {
963        try
964        {
965            ++selected; testing.behaviour( output( testing.name ) );
966        }
967        catch( message const & e )
968        {
969            ++failures; report( os, e, testing.name );
970        }
971        return *this;
972    }
973
974    ~confirm()
975    {
976        if ( failures > 0 )
977        {
978            os << failures << " out of " << selected << " selected " << pluralise("test", selected) << " " << colourise( "failed.\n" );
979        }
980        else if ( option.pass )
981        {
982            os << "All " << selected << " selected " << pluralise("test", selected) << " " << colourise( "passed.\n" );
983        }
984    }
985};
986
987template<typename Action>
988bool abort( Action & perform )
989{
990    return perform.abort();
991}
992
993template< typename Action >
994Action & for_test( tests specification, texts in, Action & perform, int n = 1 )
995{
996    for ( int i = 0; indefinite( n ) || i < n; ++i )
997    {
998        for ( tests::iterator pos = specification.begin(); pos != specification.end() ; ++pos )
999        {
1000            test & testing = *pos;
1001
1002            if ( select( testing.name, in ) )
1003                if ( abort( perform( testing ) ) )
1004                    return perform;
1005        }
1006    }
1007    return perform;
1008}
1009
1010inline bool test_less( test const & a, test const & b ) { return a.name < b.name; }
1011
1012inline void sort( tests & specification )
1013{
1014    std::sort( specification.begin(), specification.end(), test_less );
1015}
1016
1017// Use struct to avoid VC6 error C2664 when using free function:
1018
1019struct rng { int operator()( int n ) { return lest::rand() % n; } };
1020
1021inline void shuffle( tests & specification, options option )
1022{
1023#if lest_CPP11_OR_GREATER
1024    std::shuffle( specification.begin(), specification.end(), std::mt19937( option.seed ) );
1025#else
1026    lest::srand( static_cast<unsigned int>( option.seed ) );
1027
1028    rng generator;
1029    std::random_shuffle( specification.begin(), specification.end(), generator );
1030#endif
1031}
1032
1033inline int stoi( text num )
1034{
1035    return static_cast<int>( lest::strtol( num.c_str(), NULL, 10 ) );
1036}
1037
1038inline bool is_number( text arg )
1039{
1040    const text digits = "0123456789";
1041    return text::npos != arg.find_first_of    ( digits )
1042        && text::npos == arg.find_first_not_of( digits );
1043}
1044
1045inline seed_t seed( text opt, text arg )
1046{
1047    // std::time_t: implementation dependent
1048
1049    if ( arg == "time" )
1050        return static_cast<seed_t>( time( NULL ) );
1051
1052    if ( is_number( arg ) )
1053        return seed_t( lest::stoi( arg ) );
1054
1055    throw std::runtime_error( "expecting 'time' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" );
1056}
1057
1058inline int repeat( text opt, text arg )
1059{
1060    const int num = lest::stoi( arg );
1061
1062    if ( indefinite( num ) || num >= 0 )
1063        return num;
1064
1065    throw std::runtime_error( "expecting '-1' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" );
1066}
1067
1068inline std::pair<text, text>
1069split_option( text arg )
1070{
1071    text::size_type pos = arg.rfind( '=' );
1072
1073    return pos == text::npos
1074                ? std::make_pair( arg, text() )
1075                : std::make_pair( arg.substr( 0, pos ), arg.substr( pos + 1 ) );
1076}
1077
1078inline std::pair<options, texts>
1079split_arguments( texts args )
1080{
1081    options option; texts in;
1082
1083    bool in_options = true;
1084
1085    for ( texts::iterator pos = args.begin(); pos != args.end() ; ++pos )
1086    {
1087        text opt, val, arg = *pos;
1088        tie( opt, val ) = split_option( arg );
1089
1090        if ( in_options )
1091        {
1092            if      ( opt[0] != '-'                             ) { in_options     = false;           }
1093            else if ( opt == "--"                               ) { in_options     = false; continue; }
1094            else if ( opt == "-h"      || "--help"       == opt ) { option.help    =  true; continue; }
1095            else if ( opt == "-a"      || "--abort"      == opt ) { option.abort   =  true; continue; }
1096            else if ( opt == "-c"      || "--count"      == opt ) { option.count   =  true; continue; }
1097            else if ( opt == "-g"      || "--list-tags"  == opt ) { option.tags    =  true; continue; }
1098            else if ( opt == "-l"      || "--list-tests" == opt ) { option.list    =  true; continue; }
1099            else if ( opt == "-t"      || "--time"       == opt ) { option.time    =  true; continue; }
1100            else if ( opt == "-p"      || "--pass"       == opt ) { option.pass    =  true; continue; }
1101            else if (                     "--version"    == opt ) { option.version =  true; continue; }
1102            else if ( opt == "--order" && "declared"     == val ) { /* by definition */   ; continue; }
1103            else if ( opt == "--order" && "lexical"      == val ) { option.lexical =  true; continue; }
1104            else if ( opt == "--order" && "random"       == val ) { option.random  =  true; continue; }
1105            else if ( opt == "--random-seed" ) { option.seed   = seed  ( "--random-seed", val ); continue; }
1106            else if ( opt == "--repeat"      ) { option.repeat = repeat( "--repeat"     , val ); continue; }
1107            else throw std::runtime_error( "unrecognised option '" + opt + "' (try option --help)" );
1108        }
1109        in.push_back( arg );
1110    }
1111    return std::make_pair( option, in );
1112}
1113
1114inline int usage( std::ostream & os )
1115{
1116    os <<
1117        "\nUsage: test [options] [test-spec ...]\n"
1118        "\n"
1119        "Options:\n"
1120        "  -h, --help         this help message\n"
1121        "  -a, --abort        abort at first failure\n"
1122        "  -c, --count        count selected tests\n"
1123        "  -g, --list-tags    list tags of selected tests\n"
1124        "  -l, --list-tests   list selected tests\n"
1125        "  -p, --pass         also report passing tests\n"
1126#if lest_FEATURE_TIME
1127        "  -t, --time         list duration of selected tests\n"
1128#endif
1129        "  --order=declared   use source code test order (default)\n"
1130        "  --order=lexical    use lexical sort test order\n"
1131        "  --order=random     use random test order\n"
1132        "  --random-seed=n    use n for random generator seed\n"
1133        "  --random-seed=time use time for random generator seed\n"
1134        "  --repeat=n         repeat selected tests n times (-1: indefinite)\n"
1135        "  --version          report lest version and compiler used\n"
1136        "  --                 end options\n"
1137        "\n"
1138        "Test specification:\n"
1139        "  \"@\", \"*\" all tests, unless excluded\n"
1140        "  empty    all tests, unless tagged [hide] or [.optional-name]\n"
1141#if lest_FEATURE_REGEX_SEARCH
1142        "  \"re\"     select tests that match regular expression\n"
1143        "  \"!re\"    omit tests that match regular expression\n"
1144#else
1145        "  \"text\"   select tests that contain text (case insensitive)\n"
1146        "  \"!text\"  omit tests that contain text (case insensitive)\n"
1147#endif
1148        ;
1149    return 0;
1150}
1151
1152inline text compiler()
1153{
1154    std::ostringstream os;
1155#if   defined (__clang__ )
1156    os << "clang " << __clang_version__;
1157#elif defined (__GNUC__  )
1158    os << "gcc " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__;
1159#elif defined ( _MSC_VER )
1160    os << "MSVC " << lest_COMPILER_MSVC_VERSION << " (" << _MSC_VER << ")";
1161#else
1162    os << "[compiler]";
1163#endif
1164    return os.str();
1165}
1166
1167inline int version( std::ostream & os )
1168{
1169    os << "lest version "  << lest_VERSION << "\n"
1170       << "Compiled with " << compiler()   << " on " << __DATE__ << " at " << __TIME__ << ".\n"
1171       << "For more information, see https://github.com/martinmoene/lest.\n";
1172    return 0;
1173}
1174
1175inline int run( tests specification, texts arguments, std::ostream & os = std::cout )
1176{
1177    try
1178    {
1179        options option; texts in;
1180        tie( option, in ) = split_arguments( arguments );
1181
1182        if ( option.lexical ) {    sort( specification         ); }
1183        if ( option.random  ) { shuffle( specification, option ); }
1184
1185        if ( option.help    ) { return usage  ( os ); }
1186        if ( option.version ) { return version( os ); }
1187        if ( option.count   ) { count count_( os         ); return for_test( specification, in, count_ ); }
1188        if ( option.list    ) { print print_( os         ); return for_test( specification, in, print_ ); }
1189        if ( option.tags    ) { ptags ptags_( os         ); return for_test( specification, in, ptags_ ); }
1190        if ( option.time    ) { times times_( os, option ); return for_test( specification, in, times_ ); }
1191
1192        { confirm confirm_( os, option ); return for_test( specification, in, confirm_, option.repeat ); }
1193    }
1194    catch ( std::exception const & e )
1195    {
1196        os << "Error: " << e.what() << "\n";
1197        return 1;
1198    }
1199}
1200
1201// VC6: make<cont>(first,last) replaces cont(first,last)
1202
1203template<typename C, typename T>
1204C make( T const * first, T const * const last )
1205{
1206    C result;
1207    for ( ; first != last; ++first )
1208    {
1209       result.push_back( *first );
1210    }
1211    return result;
1212}
1213
1214inline tests make_tests( test const * first, test const * const last )
1215{
1216    return make<tests>( first, last );
1217}
1218
1219inline texts make_texts( char const * const * first, char const * const * last )
1220{
1221    return make<texts>( first, last );
1222}
1223
1224// Traversal of test[N] (test_specification[N]) set up to also work with MSVC6:
1225
1226template <typename C> test const *         test_begin( C const & c ) { return &*c; }
1227template <typename C> test const *           test_end( C const & c ) { return test_begin( c ) + lest_DIMENSION_OF( c ); }
1228
1229template <typename C> char const * const * text_begin( C const & c ) { return &*c; }
1230template <typename C> char const * const *   text_end( C const & c ) { return text_begin( c ) + lest_DIMENSION_OF( c ); }
1231
1232template <typename C> tests make_tests( C const & c ) { return make_tests( test_begin( c ), test_end( c ) ); }
1233template <typename C> texts make_texts( C const & c ) { return make_texts( text_begin( c ), text_end( c ) ); }
1234
1235inline int run( tests const & specification, int argc, char * argv[], std::ostream & os = std::cout )
1236{
1237    return run( specification, make_texts( argv + 1, argv + argc ), os  );
1238}
1239
1240inline int run( tests const & specification, std::ostream & os = std::cout )
1241{
1242    return run( specification, texts(), os );
1243}
1244
1245template <typename C>
1246int run(  C const & specification, texts args, std::ostream & os = std::cout )
1247{
1248    return run( make_tests( specification ), args, os  );
1249}
1250
1251template <typename C>
1252int run(  C const & specification, int argc, char * argv[], std::ostream & os = std::cout )
1253{
1254    return run( make_tests( specification ), argv, argc, os  );
1255}
1256
1257template <typename C>
1258int run(  C const & specification, std::ostream & os = std::cout )
1259{
1260    return run( make_tests( specification ), os  );
1261}
1262
1263} // namespace lest
1264
1265#endif // LEST_LEST_HPP_INCLUDED
1266