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 
96 namespace 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 
108 namespace 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 
144 namespace lest
145 {
146 #ifdef lest_COMPILER_IS_MSVC6
147     using ::strtol;
148     using ::rand;
149     using ::srand;
150 
abs( double x )151     inline double abs( double x ) { return ::fabs( x ); }
152 
153     template< typename T >
min(T const & a, T const & b)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 
295 namespace lest {
296 
297 typedef std::string       text;
298 typedef std::vector<text> texts;
299 
300 struct env;
301 
302 struct test
303 {
304     text name;
305     void (* behaviour)( env & );
306 
testlest::test307     test( text name, void (* behaviour)( env & ) )
308     : name( name ), behaviour( behaviour ) {}
309 };
310 
311 typedef std::vector<test> tests;
312 typedef tests test_specification;
313 
314 struct add_test
315 {
add_testlest::add_test316     add_test( tests & specification, test const & test_case )
317     {
318         specification.push_back( test_case );
319     }
320 };
321 
322 struct result
323 {
324     const bool passed;
325     const text decomposition;
326 
resultlest::result327     result( bool passed, text decomposition )
328     : passed( passed ), decomposition( decomposition ) {}
operator boollest::result329     operator bool() { return ! passed; }
330 };
331 
332 struct location
333 {
334     const text file;
335     const int line;
336 
locationlest::location337     location( text file, int line )
338     : file( file ), line( line ) {}
339 };
340 
341 struct comment
342 {
343     const text info;
344 
commentlest::comment345     comment( text info ) : info( info ) {}
operator boollest::comment346     operator bool() { return ! info.empty(); }
347 };
348 
349 struct message : std::runtime_error
350 {
351     const text kind;
352     const location where;
353     const comment note;
354 
~messagelest::message355     ~message() throw() {}
356 
messagelest::message357     message( text kind, location where, text expr, text note = "" )
358     : std::runtime_error( expr ), kind( kind ), where( where ), note( note ) {}
359 };
360 
361 struct failure : message
362 {
failurelest::failure363     failure( location where, text expr, text decomposition )
364     : message( "failed", where, expr + " for " + decomposition ) {}
365 };
366 
367 struct success : message
368 {
successlest::success369     success( text kind, location where, text expr, text note = "" )
370     : message( kind, where, expr, note ) {}
371 };
372 
373 struct passing : success
374 {
passinglest::passing375     passing( location where, text expr, text decomposition )
376     : success( "passed", where, expr + " for " + decomposition ) {}
377 };
378 
379 struct got_none : success
380 {
got_nonelest::got_none381     got_none( location where, text expr )
382     : success( "passed: got no exception", where, expr ) {}
383 };
384 
385 struct got : success
386 {
gotlest::got387     got( location where, text expr )
388     : success( "passed: got exception", where, expr ) {}
389 
gotlest::got390     got( location where, text expr, text excpt )
391     : success( "passed: got exception " + excpt, where, expr ) {}
392 };
393 
394 struct expected : message
395 {
expectedlest::expected396     expected( location where, text expr, text excpt = "" )
397     : message( "failed: didn't get exception", where, expr, excpt ) {}
398 };
399 
400 struct unexpected : message
401 {
unexpectedlest::unexpected402     unexpected( location where, text expr, text note = "" )
403     : message( "failed: got unexpected exception", where, expr, note ) {}
404 };
405 
406 struct guard
407 {
408     int & id;
409     int const & section;
410 
guardlest::guard411     guard( int & id, int const & section, int & count )
412     : id( id ), section( section )
413     {
414         if ( section == 0 )
415             id = count++ - 1;
416     }
operator boollest::guard417     operator bool() { return id == section; }
418 };
419 
420 class approx
421 {
422 public:
approx( double magnitude )423     explicit approx ( double magnitude )
424     : epsilon_  ( std::numeric_limits<float>::epsilon() * 100 )
425     , scale_    ( 1.0 )
426     , magnitude_( magnitude ) {}
427 
custom()428     static approx custom() { return approx( 0 ); }
429 
operator ()( double magnitude )430     approx operator()( double magnitude )
431     {
432         approx approx ( magnitude );
433         approx.epsilon( epsilon_  );
434         approx.scale  ( scale_    );
435         return approx;
436     }
437 
magnitude() const438     double magnitude() const { return magnitude_; }
439 
epsilon( double epsilon )440     approx & epsilon( double epsilon ) { epsilon_ = epsilon; return *this; }
scale( double scale )441     approx & scale  ( double scale   ) { scale_   = scale;   return *this; }
442 
operator ==( double lhs, approx const & rhs )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 
operator ==( approx const & lhs, double rhs )449     friend bool operator == ( approx const & lhs, double rhs ) { return  operator==( rhs, lhs ); }
operator !=( double lhs, approx const & rhs )450     friend bool operator != ( double lhs, approx const & rhs ) { return !operator==( lhs, rhs ); }
operator !=( approx const & lhs, double rhs )451     friend bool operator != ( approx const & lhs, double rhs ) { return !operator==( rhs, lhs ); }
452 
operator <=( double lhs, approx const & rhs )453     friend bool operator <= ( double lhs, approx const & rhs ) { return lhs < rhs.magnitude_ || lhs == rhs; }
operator <=( approx const & lhs, double rhs )454     friend bool operator <= ( approx const & lhs, double rhs ) { return lhs.magnitude_ < rhs || lhs == rhs; }
operator >=( double lhs, approx const & rhs )455     friend bool operator >= ( double lhs, approx const & rhs ) { return lhs > rhs.magnitude_ || lhs == rhs; }
operator >=( approx const & lhs, double rhs )456     friend bool operator >= ( approx const & lhs, double rhs ) { return lhs.magnitude_ > rhs || lhs == rhs; }
457 
458 private:
459     double epsilon_;
460     double scale_;
461     double magnitude_;
462 };
463 
is_false( )464 inline bool is_false(           ) { return false; }
is_true( bool flag )465 inline bool is_true ( bool flag ) { return  flag; }
466 
not_expr( text message )467 inline text not_expr( text message )
468 {
469     return "! ( " + message + " )";
470 }
471 
with_message( text message )472 inline text with_message( text message )
473 {
474     return "with message \"" + message + "\"";
475 }
476 
of_type( text type )477 inline text of_type( text type )
478 {
479     return "of type " + type;
480 }
481 
inform( location where, text expr )482 inline 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
to_string( std::nullptr_t const & )505 inline std::string to_string( std::nullptr_t const &      ) { return "nullptr"; }
506 #endif
to_string( std::string const & text )507 inline std::string to_string( std::string    const & text ) { return "\"" + text + "\"" ; }
to_string( char const * const & text )508 inline std::string to_string( char const *   const & text ) { return "\"" + std::string( text ) + "\"" ; }
to_string( char const & text )509 inline std::string to_string( char           const & text ) { return "\'" + std::string( 1, text ) + "\'" ; }
510 
operator <<( std::ostream & os, approx const & appr )511 inline std::ostream & operator<<( std::ostream & os, approx const & appr )
512 {
513     return os << appr.magnitude();
514 }
515 
516 template <typename T>
to_string( T const & value, int=0 )517 std::string to_string( T const & value, int=0 /* VC6 */ )
518 {
519     std::ostringstream os; os << std::boolalpha << value; return os.str();
520 }
521 
522 template<typename T1, typename T2>
to_string( std::pair<T1,T2> const & pair )523 std::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 
532 template<typename TU, std::size_t N>
533 struct make_tuple_string
534 {
makelest::make_tuple_string535     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 
543 template<typename TU>
544 struct make_tuple_string<TU, 0>
545 {
makelest::make_tuple_string546     static std::string make( TU const & ) { return ""; }
547 };
548 
549 template<typename ...TS>
550 auto 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 
556 template <typename L, typename R>
to_string( L const & lhs, std::string op, R const & rhs )557 std::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 
562 template <typename L>
563 struct expression_lhs
564 {
565     L lhs;
566 
expression_lhslest::expression_lhs567     expression_lhs( L lhs ) : lhs( lhs ) {}
568 
operator resultlest::expression_lhs569     operator result() { return result( !!lhs, to_string( lhs ) ); }
570 
operator ==lest::expression_lhs571     template <typename R> result operator==( R const & rhs ) { return result( lhs == rhs, to_string( lhs, "==", rhs ) ); }
operator !=lest::expression_lhs572     template <typename R> result operator!=( R const & rhs ) { return result( lhs != rhs, to_string( lhs, "!=", rhs ) ); }
operator <lest::expression_lhs573     template <typename R> result operator< ( R const & rhs ) { return result( lhs <  rhs, to_string( lhs, "<" , rhs ) ); }
operator <=lest::expression_lhs574     template <typename R> result operator<=( R const & rhs ) { return result( lhs <= rhs, to_string( lhs, "<=", rhs ) ); }
operator >lest::expression_lhs575     template <typename R> result operator> ( R const & rhs ) { return result( lhs >  rhs, to_string( lhs, ">" , rhs ) ); }
operator >=lest::expression_lhs576     template <typename R> result operator>=( R const & rhs ) { return result( lhs >= rhs, to_string( lhs, ">=", rhs ) ); }
577 };
578 
579 struct expression_decomposer
580 {
581     template <typename L>
operator <<lest::expression_decomposer582     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 
red( text words )592 inline text red  ( text words ) { return "\033[1;31m" + words + "\033[0m"; }
green( text words )593 inline text green( text words ) { return "\033[1;32m" + words + "\033[0m"; }
gray( text words )594 inline text gray ( text words ) { return "\033[1;30m" + words + "\033[0m"; }
595 
starts_with( text words, text with )596 inline bool starts_with( text words, text with )
597 {
598     return 0 == words.find( with );
599 }
600 
replace( text words, text from, text to )601 inline 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 
colour( text words )607 inline 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 
is_cout( std::ostream & os )615 inline bool is_cout( std::ostream & os ) { return &os == &std::cout; }
616 
617 struct colourise
618 {
619     const text words;
620 
colouriselest::colourise621     colourise( text words )
622     : words( words ) {}
623 
624     // only colourise for std::cout, not for a stringstream as used in tests:
625 
operator ()lest::colourise626     std::ostream & operator()( std::ostream & os ) const
627     {
628         return is_cout( os ) ? os << colour( words ) : os << words;
629     }
630 };
631 
operator <<( std::ostream & os, colourise words )632 inline std::ostream & operator<<( std::ostream & os, colourise words ) { return words( os ); }
633 #else
colourise( text words )634 inline text colourise( text words ) { return words; }
635 #endif
636 
pluralise( text word,int n )637 inline text pluralise( text word,int n )
638 {
639     return n == 1 ? word : word + "s";
640 }
641 
operator <<( std::ostream & os, comment note )642 inline std::ostream & operator<<( std::ostream & os, comment note )
643 {
644     return os << (note ? " " + note.info : "" );
645 }
646 
operator <<( std::ostream & os, location where )647 inline 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 
report( std::ostream & os, message const & e, text test )656 inline 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
search( text re, text line )664     inline bool search( text re, text line )
665     {
666         return std::regex_search( line, std::regex( re ) );
667     }
668 #else
case_insensitive_equal( char a, char b )669     inline bool case_insensitive_equal( char a, char b )
670     {
671         return tolower( a ) == tolower( b );
672     }
673 
search( text part, text line )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 
match( texts whats, text line )682 inline 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 
hidden( text name )692 inline 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 
none( texts args )702 inline bool none( texts args )
703 {
704     return args.size() == 0;
705 }
706 
select( text name, texts include )707 inline 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 
indefinite( int repeat )739 inline int indefinite( int repeat ) { return repeat == -1; }
740 
741 typedef unsigned long seed_t;
742 
743 struct options
744 {
optionslest::options745     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 
763 struct env
764 {
765     std::ostream & os;
766     bool pass;
767     text testing;
768 
envlest::env769     env( std::ostream & os, bool pass )
770     : os( os ), pass( pass ), testing() {}
771 
operator ()lest::env772     env & operator()( text test )
773     {
774         testing = test; return *this;
775     }
776 };
777 
778 struct action
779 {
780     std::ostream & os;
781 
actionlest::action782     action( std::ostream & os ) : os( os ) {}
783 
operator intlest::action784     operator      int() { return 0; }
abortlest::action785     bool        abort() { return false; }
operator ()lest::action786     action & operator()( test ) { return *this; }
787 
788 private:
789     action( action const & );
790 	void operator=(action const & );
791 };
792 
793 struct print : action
794 {
printlest::print795     print( std::ostream & os ) : action( os ) {}
796 
operator ()lest::print797     print &  operator()( test testing )
798     {
799         os << testing.name << "\n"; return *this;
800     }
801 };
802 
tags( text name, texts result = texts() )803 inline 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 
817 struct ptags : action
818 {
819     std::set<text> result;
820 
ptagslest::ptags821     ptags( std::ostream & os ) : action( os ), result() {}
822 
operator ()lest::ptags823     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 
~ptagslest::ptags832     ~ptags()
833     {
834         std::copy( result.begin(), result.end(), std::ostream_iterator<text>( os, "\n" ) );
835     }
836 };
837 
838 struct count : action
839 {
840     int n;
841 
countlest::count842     count( std::ostream & os ) : action( os ), n( 0 ) {}
843 
operator ()lest::count844     count & operator()( test ) { ++n; return *this; }
845 
~countlest::count846     ~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
current_ticks()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 
889 struct timer
890 {
891     const uint64_t start_ticks;
892 
timerlest::timer893     timer() : start_ticks( current_ticks() ) {}
894 
elapsed_secondslest::timer895     double elapsed_seconds() const
896     {
897         return static_cast<double>( current_ticks() - start_ticks ) / 1e6;
898     }
899 };
900 
901 struct times : action
902 {
903     env output;
904     options option;
905     int selected;
906     int failures;
907 
908     timer total;
909 
timeslest::times910     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 
operator intlest::times916     operator int() { return failures; }
917 
abortlest::times918     bool abort() { return option.abort && failures > 0; }
919 
operator ()lest::times920     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 
~timeslest::times938     ~times()
939     {
940         os << "Elapsed time: " << std::setprecision(1) << total.elapsed_seconds() << " s\n";
941     }
942 };
943 #else
timeslest::times944 struct times : action { times( std::ostream &, options ) : action( os ) {} };
945 #endif
946 
947 struct confirm : action
948 {
949     env output;
950     options option;
951     int selected;
952     int failures;
953 
confirmlest::confirm954     confirm( std::ostream & os, options option )
955     : action( os ), output( os, option.pass ), option( option ), selected( 0 ), failures( 0 ) {}
956 
operator intlest::confirm957     operator int() { return failures; }
958 
abortlest::confirm959     bool abort() { return option.abort && failures > 0; }
960 
operator ()lest::confirm961     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 
~confirmlest::confirm974     ~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 
987 template<typename Action>
abort( Action & perform )988 bool abort( Action & perform )
989 {
990     return perform.abort();
991 }
992 
993 template< typename Action >
for_test( tests specification, texts in, Action & perform, int n = 1 )994 Action & 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 
test_less( test const & a, test const & b )1010 inline bool test_less( test const & a, test const & b ) { return a.name < b.name; }
1011 
sort( tests & specification )1012 inline 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 
operator ()lest::rng1019 struct rng { int operator()( int n ) { return lest::rand() % n; } };
1020 
shuffle( tests & specification, options option )1021 inline 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 
stoi( text num )1033 inline int stoi( text num )
1034 {
1035     return static_cast<int>( lest::strtol( num.c_str(), NULL, 10 ) );
1036 }
1037 
is_number( text arg )1038 inline 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 
seed( text opt, text arg )1045 inline 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 
repeat( text opt, text arg )1058 inline 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 
1068 inline std::pair<text, text>
split_option( text arg )1069 split_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 
1078 inline std::pair<options, texts>
split_arguments( texts args )1079 split_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 
usage( std::ostream & os )1114 inline 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 
compiler()1152 inline 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 
version( std::ostream & os )1167 inline 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 
run( tests specification, texts arguments, std::ostream & os = std::cout )1175 inline 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 
1203 template<typename C, typename T>
make( T const * first, T const * const last )1204 C 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 
make_tests( test const * first, test const * const last )1214 inline tests make_tests( test const * first, test const * const last )
1215 {
1216     return make<tests>( first, last );
1217 }
1218 
make_texts( char const * const * first, char const * const * last )1219 inline 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 
test_begin( C const & c )1226 template <typename C> test const *         test_begin( C const & c ) { return &*c; }
test_end( C const & c )1227 template <typename C> test const *           test_end( C const & c ) { return test_begin( c ) + lest_DIMENSION_OF( c ); }
1228 
text_begin( C const & c )1229 template <typename C> char const * const * text_begin( C const & c ) { return &*c; }
text_end( C const & c )1230 template <typename C> char const * const *   text_end( C const & c ) { return text_begin( c ) + lest_DIMENSION_OF( c ); }
1231 
make_tests( C const & c )1232 template <typename C> tests make_tests( C const & c ) { return make_tests( test_begin( c ), test_end( c ) ); }
make_texts( C const & c )1233 template <typename C> texts make_texts( C const & c ) { return make_texts( text_begin( c ), text_end( c ) ); }
1234 
run( tests const & specification, int argc, char * argv[], std::ostream & os = std::cout )1235 inline 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 
run( tests const & specification, std::ostream & os = std::cout )1240 inline int run( tests const & specification, std::ostream & os = std::cout )
1241 {
1242     return run( specification, texts(), os );
1243 }
1244 
1245 template <typename C>
run( C const & specification, texts args, std::ostream & os = std::cout )1246 int run(  C const & specification, texts args, std::ostream & os = std::cout )
1247 {
1248     return run( make_tests( specification ), args, os  );
1249 }
1250 
1251 template <typename C>
run( C const & specification, int argc, char * argv[], std::ostream & os = std::cout )1252 int 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 
1257 template <typename C>
run( C const & specification, std::ostream & os = std::cout )1258 int 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