├── .gitignore ├── src └── type │ └── type.h └── test ├── lest.hpp ├── test.bat ├── test.cpp └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /test/test 2 | /test/test.obj 3 | /test/test.exe 4 | -------------------------------------------------------------------------------- /src/type/type.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2018, ITHare.com 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | */ 32 | 33 | #ifndef ithare_util_type_type_h_included 34 | #define ithare_util_type_type_h_included 35 | 36 | #include 37 | #include 38 | #ifdef ITHARE_UTIL_INTERNAL_DEBUG 39 | #include internal debug only 40 | #define ITHARE_UTIL_ASSERT(x) assert(x) 41 | #else 42 | #define ITHARE_UTIL_ASSERT(x) 43 | #endif 44 | 45 | namespace ithare { namespace util { namespace type { 46 | 47 | namespace intuitive { 48 | using MAX_EFFICIENT_INT = intptr_t;//platform-dependent, but intptr_t is not a bad starting point 49 | // if it is suboptimal for some platform - open an issue, we'll add an #ifdef for that platform 50 | 51 | //DUPLICATED CODE (1/2). It is obviously possible to get rid of duplicates, 52 | // but it is currently unclear how it may affect performance of the compiled code, so for now we prefer it this way 53 | template 54 | inline constexpr bool lt(TA a, TB b) { 55 | static_assert(std::is_integral::value); 56 | static_assert(std::is_integral::value); 57 | constexpr bool aSigned = std::is_signed::value; 58 | constexpr bool bSigned = std::is_signed::value; 59 | if constexpr(aSigned == bSigned) 60 | return a < b;//both signed or both unsigned - no casts required, C promotions will do just fine 61 | else {//different is_signed, let's make TSIGNED always-signed, and TUNSIGNED - always-unsigned 62 | using TSIGNED = typename std::conditional::type; 63 | using TUNSIGNED = typename std::conditional::type; 64 | 65 | static_assert(sizeof(TSIGNED)+sizeof(TUNSIGNED)==sizeof(TA)+sizeof(TB));//self-check 66 | if constexpr(sizeof(TSIGNED)>sizeof(TUNSIGNED)) 67 | return a < b;//again, no casts required, C promotions will do just fine (promoting b to TA which is signed) 68 | if constexpr(sizeof(TUNSIGNED)= sizeof(TSIGNED) => no-cast will be counterintuitive 73 | if constexpr(sizeof(TUNSIGNED)=sizeof(TSIGNED)); 87 | if constexpr(aSigned) { 88 | //return a<0 ? true : TUNSIGNED(a) < b; 89 | return (a<0) | (TUNSIGNED(a) < b);//sic - single '|', seems to perform better under GCC (avoids branch) 90 | } 91 | else { 92 | ITHARE_UTIL_ASSERT(bSigned); 93 | //return b<0 ? false : a < TUNSIGNED(b); 94 | return (b>=0) & (a < TUNSIGNED(b));//sic - single '&', seems to perform better under GCC (avoids branch) 95 | } 96 | } 97 | } 98 | } 99 | 100 | //DUPLICATED CODE (2/2) 101 | template 102 | inline constexpr bool eq(TA a, TB b) { 103 | static_assert(std::is_integral::value); 104 | static_assert(std::is_integral::value); 105 | constexpr bool aSigned = std::is_signed::value; 106 | constexpr bool bSigned = std::is_signed::value; 107 | if constexpr(aSigned == bSigned) 108 | return a == b;//both signed or both unsigned - no casts required, C promotions will do just fine 109 | else {//different is_signed, let's make TSIGNED always-signed, and TUNSIGNED - always-unsigned 110 | using TSIGNED = typename std::conditional::type; 111 | using TUNSIGNED = typename std::conditional::type; 112 | 113 | static_assert(sizeof(TSIGNED)+sizeof(TUNSIGNED)==sizeof(TA)+sizeof(TB));//self-check 114 | if constexpr(sizeof(TSIGNED)>sizeof(TUNSIGNED)) 115 | return a == b;//again, no casts required, C promotions will do just fine (promoting b to TA which is signed) 116 | if constexpr(sizeof(TUNSIGNED)= sizeof(TSIGNED) => no-cast will be counterintuitive 121 | if constexpr(sizeof(TUNSIGNED)=sizeof(TSIGNED)); 134 | if constexpr(aSigned) 135 | return a<0 ? false : TUNSIGNED(a) == b; 136 | else { 137 | ITHARE_UTIL_ASSERT(bSigned); 138 | return b<0 ? false : a == TUNSIGNED(b); 139 | } 140 | } 141 | } 142 | } 143 | 144 | template 145 | inline constexpr bool gt(TA a, TB b) { 146 | return lt(b,a); 147 | } 148 | 149 | template 150 | inline constexpr bool le(TA a, TB b) { 151 | return !lt(b,a); 152 | } 153 | 154 | template 155 | inline constexpr bool ge(TA a, TB b) { 156 | return !lt(a,b); 157 | } 158 | 159 | template 160 | inline constexpr bool ne(TA a, TB b) { 161 | return !eq(a,b); 162 | } 163 | 164 | }//namespace intuitive 165 | 166 | }}} //namespace ithare::util::type 167 | 168 | 169 | #endif //ithare_util_type_type_h_included 170 | -------------------------------------------------------------------------------- /test/lest.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2018 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.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 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef __clang__ 35 | # pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" 36 | # pragma clang diagnostic ignored "-Woverloaded-shift-op-parentheses" 37 | # pragma clang diagnostic ignored "-Wunused-comparison" 38 | # pragma clang diagnostic ignored "-Wunused-value" 39 | #elif defined __GNUC__ 40 | # pragma GCC diagnostic ignored "-Wunused-value" 41 | #endif 42 | 43 | #define lest_VERSION "1.31.0" 44 | 45 | #ifndef lest_FEATURE_AUTO_REGISTER 46 | # define lest_FEATURE_AUTO_REGISTER 0 47 | #endif 48 | 49 | #ifndef lest_FEATURE_COLOURISE 50 | # define lest_FEATURE_COLOURISE 0 51 | #endif 52 | 53 | #ifndef lest_FEATURE_LITERAL_SUFFIX 54 | # define lest_FEATURE_LITERAL_SUFFIX 0 55 | #endif 56 | 57 | #ifndef lest_FEATURE_REGEX_SEARCH 58 | # define lest_FEATURE_REGEX_SEARCH 0 59 | #endif 60 | 61 | #ifndef lest_FEATURE_TIME_PRECISION 62 | #define lest_FEATURE_TIME_PRECISION 0 63 | #endif 64 | 65 | #ifndef lest_FEATURE_WSTRING 66 | #define lest_FEATURE_WSTRING 1 67 | #endif 68 | 69 | #ifdef lest_FEATURE_RTTI 70 | # define lest__cpp_rtti lest_FEATURE_RTTI 71 | #elif defined(__cpp_rtti) 72 | # define lest__cpp_rtti __cpp_rtti 73 | #elif defined(__GXX_RTTI) || defined (_CPPRTTI) 74 | # define lest__cpp_rtti 1 75 | #else 76 | # define lest__cpp_rtti 0 77 | #endif 78 | 79 | #if lest_FEATURE_REGEX_SEARCH 80 | # include 81 | #endif 82 | 83 | #if ! defined( lest_NO_SHORT_MACRO_NAMES ) && ! defined( lest_NO_SHORT_ASSERTION_NAMES ) 84 | # define MODULE lest_MODULE 85 | 86 | # if ! lest_FEATURE_AUTO_REGISTER 87 | # define CASE lest_CASE 88 | # endif 89 | 90 | # define SETUP lest_SETUP 91 | # define SECTION lest_SECTION 92 | 93 | # define EXPECT lest_EXPECT 94 | # define EXPECT_NOT lest_EXPECT_NOT 95 | # define EXPECT_NO_THROW lest_EXPECT_NO_THROW 96 | # define EXPECT_THROWS lest_EXPECT_THROWS 97 | # define EXPECT_THROWS_AS lest_EXPECT_THROWS_AS 98 | 99 | # define SCENARIO lest_SCENARIO 100 | # define GIVEN lest_GIVEN 101 | # define WHEN lest_WHEN 102 | # define THEN lest_THEN 103 | # define AND_WHEN lest_AND_WHEN 104 | # define AND_THEN lest_AND_THEN 105 | #endif 106 | 107 | #define lest_SCENARIO( sketch ) lest_CASE( lest::text("Scenario: ") + sketch ) 108 | #define lest_GIVEN( context ) lest_SETUP( lest::text( "Given: ") + context ) 109 | #define lest_WHEN( story ) lest_SECTION( lest::text( " When: ") + story ) 110 | #define lest_THEN( story ) lest_SECTION( lest::text( " Then: ") + story ) 111 | #define lest_AND_WHEN( story ) lest_SECTION( lest::text( " And: ") + story ) 112 | #define lest_AND_THEN( story ) lest_SECTION( lest::text( " And: ") + story ) 113 | 114 | #if lest_FEATURE_AUTO_REGISTER 115 | 116 | # define lest_CASE( specification, proposition ) \ 117 | static void lest_FUNCTION( lest::env & ); \ 118 | namespace { lest::add_test lest_REGISTRAR( specification, lest::test( proposition, lest_FUNCTION ) ); } \ 119 | static void lest_FUNCTION( lest::env & lest_env ) 120 | 121 | #else // lest_FEATURE_AUTO_REGISTER 122 | 123 | # define lest_CASE( proposition, ... ) \ 124 | proposition, [__VA_ARGS__]( lest::env & lest_env ) 125 | 126 | # define lest_MODULE( specification, module ) \ 127 | namespace { lest::add_module _( specification, module ); } 128 | 129 | #endif //lest_FEATURE_AUTO_REGISTER 130 | 131 | #define lest_SETUP( context ) \ 132 | for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ ) 133 | 134 | #define lest_SECTION( proposition ) \ 135 | static int lest_UNIQUE( id ) = 0; \ 136 | if ( lest::guard( lest_UNIQUE( id ), lest__section, lest__count ) ) \ 137 | for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ ) 138 | 139 | #define lest_EXPECT( expr ) \ 140 | do { \ 141 | try \ 142 | { \ 143 | if ( lest::result score = lest_DECOMPOSE( expr ) ) \ 144 | throw lest::failure{ lest_LOCATION, #expr, score.decomposition }; \ 145 | else if ( lest_env.pass ) \ 146 | lest::report( lest_env.os, lest::passing{ lest_LOCATION, #expr, score.decomposition }, lest_env.testing ); \ 147 | } \ 148 | catch(...) \ 149 | { \ 150 | lest::inform( lest_LOCATION, #expr ); \ 151 | } \ 152 | } while ( lest::is_false() ) 153 | 154 | #define lest_EXPECT_NOT( expr ) \ 155 | do { \ 156 | try \ 157 | { \ 158 | if ( lest::result score = lest_DECOMPOSE( expr ) ) \ 159 | { \ 160 | if ( lest_env.pass ) \ 161 | lest::report( lest_env.os, lest::passing{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) }, lest_env.testing ); \ 162 | } \ 163 | else \ 164 | throw lest::failure{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) }; \ 165 | } \ 166 | catch(...) \ 167 | { \ 168 | lest::inform( lest_LOCATION, lest::not_expr( #expr ) ); \ 169 | } \ 170 | } while ( lest::is_false() ) 171 | 172 | #define lest_EXPECT_NO_THROW( expr ) \ 173 | do \ 174 | { \ 175 | try \ 176 | { \ 177 | expr; \ 178 | } \ 179 | catch (...) \ 180 | { \ 181 | lest::inform( lest_LOCATION, #expr ); \ 182 | } \ 183 | if ( lest_env.pass ) \ 184 | lest::report( lest_env.os, lest::got_none( lest_LOCATION, #expr ), lest_env.testing ); \ 185 | } while ( lest::is_false() ) 186 | 187 | #define lest_EXPECT_THROWS( expr ) \ 188 | do \ 189 | { \ 190 | try \ 191 | { \ 192 | expr; \ 193 | } \ 194 | catch (...) \ 195 | { \ 196 | if ( lest_env.pass ) \ 197 | lest::report( lest_env.os, lest::got{ lest_LOCATION, #expr }, lest_env.testing ); \ 198 | break; \ 199 | } \ 200 | throw lest::expected{ lest_LOCATION, #expr }; \ 201 | } \ 202 | while ( lest::is_false() ) 203 | 204 | #define lest_EXPECT_THROWS_AS( expr, excpt ) \ 205 | do \ 206 | { \ 207 | try \ 208 | { \ 209 | expr; \ 210 | } \ 211 | catch ( excpt & ) \ 212 | { \ 213 | if ( lest_env.pass ) \ 214 | lest::report( lest_env.os, lest::got{ lest_LOCATION, #expr, lest::of_type( #excpt ) }, lest_env.testing ); \ 215 | break; \ 216 | } \ 217 | catch (...) {} \ 218 | throw lest::expected{ lest_LOCATION, #expr, lest::of_type( #excpt ) }; \ 219 | } \ 220 | while ( lest::is_false() ) 221 | 222 | #define lest_UNIQUE( name ) lest_UNIQUE2( name, __LINE__ ) 223 | #define lest_UNIQUE2( name, line ) lest_UNIQUE3( name, line ) 224 | #define lest_UNIQUE3( name, line ) name ## line 225 | 226 | #define lest_DECOMPOSE( expr ) ( lest::expression_decomposer() << expr ) 227 | 228 | #define lest_FUNCTION lest_UNIQUE(__lest_function__ ) 229 | #define lest_REGISTRAR lest_UNIQUE(__lest_registrar__ ) 230 | 231 | #define lest_LOCATION lest::location{__FILE__, __LINE__} 232 | 233 | namespace lest { 234 | 235 | using text = std::string; 236 | using texts = std::vector; 237 | 238 | struct env; 239 | 240 | struct test 241 | { 242 | text name; 243 | std::function behaviour; 244 | 245 | #if lest_FEATURE_AUTO_REGISTER 246 | test( text name, std::function behaviour ) 247 | : name( name ), behaviour( behaviour ) {} 248 | #endif 249 | }; 250 | 251 | using tests = std::vector; 252 | 253 | #if lest_FEATURE_AUTO_REGISTER 254 | 255 | struct add_test 256 | { 257 | add_test( tests & specification, test const & test_case ) 258 | { 259 | specification.push_back( test_case ); 260 | } 261 | }; 262 | 263 | #else 264 | 265 | struct add_module 266 | { 267 | template 268 | add_module( tests & specification, test const (&module)[N] ) 269 | { 270 | specification.insert( specification.end(), std::begin( module ), std::end( module ) ); 271 | } 272 | }; 273 | 274 | #endif 275 | 276 | struct result 277 | { 278 | const bool passed; 279 | const text decomposition; 280 | 281 | template< typename T > 282 | result( T const & passed, text decomposition ) 283 | : passed( !!passed ), decomposition( decomposition ) {} 284 | 285 | explicit operator bool() { return ! passed; } 286 | }; 287 | 288 | struct location 289 | { 290 | const text file; 291 | const int line; 292 | 293 | location( text file, int line ) 294 | : file( file ), line( line ) {} 295 | }; 296 | 297 | struct comment 298 | { 299 | const text info; 300 | 301 | comment( text info ) : info( info ) {} 302 | explicit operator bool() { return ! info.empty(); } 303 | }; 304 | 305 | struct message : std::runtime_error 306 | { 307 | const text kind; 308 | const location where; 309 | const comment note; 310 | 311 | ~message() throw() {} // GCC 4.6 312 | 313 | message( text kind, location where, text expr, text note = "" ) 314 | : std::runtime_error( expr ), kind( kind ), where( where ), note( note ) {} 315 | }; 316 | 317 | struct failure : message 318 | { 319 | failure( location where, text expr, text decomposition ) 320 | : message{ "failed", where, expr + " for " + decomposition } {} 321 | }; 322 | 323 | struct success : message 324 | { 325 | // using message::message; // VC is lagging here 326 | 327 | success( text kind, location where, text expr, text note = "" ) 328 | : message( kind, where, expr, note ) {} 329 | }; 330 | 331 | struct passing : success 332 | { 333 | passing( location where, text expr, text decomposition ) 334 | : success( "passed", where, expr + " for " + decomposition ) {} 335 | }; 336 | 337 | struct got_none : success 338 | { 339 | got_none( location where, text expr ) 340 | : success( "passed: got no exception", where, expr ) {} 341 | }; 342 | 343 | struct got : success 344 | { 345 | got( location where, text expr ) 346 | : success( "passed: got exception", where, expr ) {} 347 | 348 | got( location where, text expr, text excpt ) 349 | : success( "passed: got exception " + excpt, where, expr ) {} 350 | }; 351 | 352 | struct expected : message 353 | { 354 | expected( location where, text expr, text excpt = "" ) 355 | : message{ "failed: didn't get exception", where, expr, excpt } {} 356 | }; 357 | 358 | struct unexpected : message 359 | { 360 | unexpected( location where, text expr, text note = "" ) 361 | : message{ "failed: got unexpected exception", where, expr, note } {} 362 | }; 363 | 364 | struct guard 365 | { 366 | int & id; 367 | int const & section; 368 | 369 | guard( int & id, int const & section, int & count ) 370 | : id( id ), section( section ) 371 | { 372 | if ( section == 0 ) 373 | id = count++ - 1; 374 | } 375 | operator bool() { return id == section; } 376 | }; 377 | 378 | class approx 379 | { 380 | public: 381 | explicit approx ( double magnitude ) 382 | : epsilon_ { std::numeric_limits::epsilon() * 100 } 383 | , scale_ { 1.0 } 384 | , magnitude_{ magnitude } {} 385 | 386 | approx( approx const & other ) = default; 387 | 388 | static approx custom() { return approx( 0 ); } 389 | 390 | approx operator()( double magnitude ) 391 | { 392 | approx approx ( magnitude ); 393 | approx.epsilon( epsilon_ ); 394 | approx.scale ( scale_ ); 395 | return approx; 396 | } 397 | 398 | double magnitude() const { return magnitude_; } 399 | 400 | approx & epsilon( double epsilon ) { epsilon_ = epsilon; return *this; } 401 | approx & scale ( double scale ) { scale_ = scale; return *this; } 402 | 403 | friend bool operator == ( double lhs, approx const & rhs ) 404 | { 405 | // Thanks to Richard Harris for his help refining this formula. 406 | return std::abs( lhs - rhs.magnitude_ ) < rhs.epsilon_ * ( rhs.scale_ + (std::min)( std::abs( lhs ), std::abs( rhs.magnitude_ ) ) ); 407 | } 408 | 409 | friend bool operator == ( approx const & lhs, double rhs ) { return operator==( rhs, lhs ); } 410 | friend bool operator != ( double lhs, approx const & rhs ) { return !operator==( lhs, rhs ); } 411 | friend bool operator != ( approx const & lhs, double rhs ) { return !operator==( rhs, lhs ); } 412 | 413 | friend bool operator <= ( double lhs, approx const & rhs ) { return lhs < rhs.magnitude_ || lhs == rhs; } 414 | friend bool operator <= ( approx const & lhs, double rhs ) { return lhs.magnitude_ < rhs || lhs == rhs; } 415 | friend bool operator >= ( double lhs, approx const & rhs ) { return lhs > rhs.magnitude_ || lhs == rhs; } 416 | friend bool operator >= ( approx const & lhs, double rhs ) { return lhs.magnitude_ > rhs || lhs == rhs; } 417 | 418 | private: 419 | double epsilon_; 420 | double scale_; 421 | double magnitude_; 422 | }; 423 | 424 | inline bool is_false( ) { return false; } 425 | inline bool is_true ( bool flag ) { return flag; } 426 | 427 | inline text not_expr( text message ) 428 | { 429 | return "! ( " + message + " )"; 430 | } 431 | 432 | inline text with_message( text message ) 433 | { 434 | return "with message \"" + message + "\""; 435 | } 436 | 437 | inline text of_type( text type ) 438 | { 439 | return "of type " + type; 440 | } 441 | 442 | inline void inform( location where, text expr ) 443 | { 444 | try 445 | { 446 | throw; 447 | } 448 | catch( message const & ) 449 | { 450 | throw; 451 | } 452 | catch( std::exception const & e ) 453 | { 454 | throw unexpected{ where, expr, with_message( e.what() ) }; \ 455 | } 456 | catch(...) 457 | { 458 | throw unexpected{ where, expr, "of unknown type" }; \ 459 | } 460 | } 461 | 462 | // Expression decomposition: 463 | 464 | template 465 | auto make_value_string( T const & value ) -> std::string; 466 | 467 | template 468 | auto make_memory_string( T const & item ) -> std::string; 469 | 470 | #if lest_FEATURE_LITERAL_SUFFIX 471 | inline char const * sfx( char const * text ) { return text; } 472 | #else 473 | inline char const * sfx( char const * ) { return ""; } 474 | #endif 475 | 476 | inline std::string to_string( std::nullptr_t ) { return "nullptr"; } 477 | inline std::string to_string( std::string const & text ) { return "\"" + text + "\"" ; } 478 | #if lest_FEATURE_WSTRING 479 | inline std::string to_string( std::wstring const & text ) ; 480 | #endif 481 | 482 | inline std::string to_string( char const * const text ) { return text ? to_string( std::string ( text ) ) : "{null string}"; } 483 | inline std::string to_string( char * const text ) { return text ? to_string( std::string ( text ) ) : "{null string}"; } 484 | #if lest_FEATURE_WSTRING 485 | inline std::string to_string( wchar_t const * const text ) { return text ? to_string( std::wstring( text ) ) : "{null string}"; } 486 | inline std::string to_string( wchar_t * const text ) { return text ? to_string( std::wstring( text ) ) : "{null string}"; } 487 | #endif 488 | 489 | inline std::string to_string( char text ) { return "\'" + std::string( 1, text ) + "\'" ; } 490 | inline std::string to_string( signed char text ) { return "\'" + std::string( 1, text ) + "\'" ; } 491 | inline std::string to_string( unsigned char text ) { return "\'" + std::string( 1, text ) + "\'" ; } 492 | 493 | inline std::string to_string( bool flag ) { return flag ? "true" : "false"; } 494 | 495 | inline std::string to_string( signed short value ) { return make_value_string( value ) ; } 496 | inline std::string to_string( unsigned short value ) { return make_value_string( value ) + sfx("u" ); } 497 | inline std::string to_string( signed int value ) { return make_value_string( value ) ; } 498 | inline std::string to_string( unsigned int value ) { return make_value_string( value ) + sfx("u" ); } 499 | inline std::string to_string( signed long value ) { return make_value_string( value ) + sfx("l" ); } 500 | inline std::string to_string( unsigned long value ) { return make_value_string( value ) + sfx("ul" ); } 501 | inline std::string to_string( signed long long value ) { return make_value_string( value ) + sfx("ll" ); } 502 | inline std::string to_string( unsigned long long value ) { return make_value_string( value ) + sfx("ull"); } 503 | inline std::string to_string( double value ) { return make_value_string( value ) ; } 504 | inline std::string to_string( float value ) { return make_value_string( value ) + sfx("f" ); } 505 | 506 | template 507 | struct is_streamable 508 | { 509 | template 510 | static auto test( int ) -> decltype( std::declval() << std::declval(), std::true_type() ); 511 | 512 | template 513 | static auto test( ... ) -> std::false_type; 514 | 515 | #ifdef _MSC_VER 516 | enum { value = std::is_same< decltype( test(0) ), std::true_type >::value }; 517 | #else 518 | static constexpr bool value = std::is_same< decltype( test(0) ), std::true_type >::value; 519 | #endif 520 | }; 521 | 522 | template 523 | struct is_container 524 | { 525 | template 526 | static auto test( int ) -> decltype( std::declval().begin() == std::declval().end(), std::true_type() ); 527 | 528 | template 529 | static auto test( ... ) -> std::false_type; 530 | 531 | #ifdef _MSC_VER 532 | enum { value = std::is_same< decltype( test(0) ), std::true_type >::value }; 533 | #else 534 | static constexpr bool value = std::is_same< decltype( test(0) ), std::true_type >::value; 535 | #endif 536 | }; 537 | 538 | template 539 | using ForEnum = typename std::enable_if< std::is_enum::value, R>::type; 540 | 541 | template 542 | using ForNonEnum = typename std::enable_if< ! std::is_enum::value, R>::type; 543 | 544 | template 545 | using ForStreamable = typename std::enable_if< is_streamable::value, R>::type; 546 | 547 | template 548 | using ForNonStreamable = typename std::enable_if< ! is_streamable::value, R>::type; 549 | 550 | template 551 | using ForContainer = typename std::enable_if< is_container::value, R>::type; 552 | 553 | template 554 | using ForNonContainer = typename std::enable_if< ! is_container::value, R>::type; 555 | 556 | template 557 | auto make_enum_string( T const & ) -> ForNonEnum 558 | { 559 | #if lest__cpp_rtti 560 | return text("[type: ") + typeid(T).name() + "]"; 561 | #else 562 | return text("[type: (no RTTI)]"); 563 | #endif 564 | } 565 | 566 | template 567 | auto make_enum_string( T const & item ) -> ForEnum 568 | { 569 | return to_string( static_cast::type>( item ) ); 570 | } 571 | 572 | template 573 | auto make_string( T const & item ) -> ForNonStreamable 574 | { 575 | return make_enum_string( item ); 576 | } 577 | 578 | template 579 | auto make_string( T const & item ) -> ForStreamable 580 | { 581 | std::ostringstream os; os << item; return os.str(); 582 | } 583 | 584 | template 585 | auto make_string( T * p )-> std::string 586 | { 587 | if ( p ) return make_memory_string( p ); 588 | else return "NULL"; 589 | } 590 | 591 | template 592 | auto make_string( R C::* p ) -> std::string 593 | { 594 | if ( p ) return make_memory_string( p ); 595 | else return "NULL"; 596 | } 597 | 598 | template 599 | auto make_string( std::pair const & pair ) -> std::string 600 | { 601 | std::ostringstream oss; 602 | oss << "{ " << to_string( pair.first ) << ", " << to_string( pair.second ) << " }"; 603 | return oss.str(); 604 | } 605 | 606 | template 607 | struct make_tuple_string 608 | { 609 | static std::string make( TU const & tuple ) 610 | { 611 | std::ostringstream os; 612 | os << to_string( std::get( tuple ) ) << ( N < std::tuple_size::value ? ", ": " "); 613 | return make_tuple_string::make( tuple ) + os.str(); 614 | } 615 | }; 616 | 617 | template 618 | struct make_tuple_string 619 | { 620 | static std::string make( TU const & ) { return ""; } 621 | }; 622 | 623 | template 624 | auto make_string( std::tuple const & tuple ) -> std::string 625 | { 626 | return "{ " + make_tuple_string, sizeof...(TS)>::make( tuple ) + "}"; 627 | } 628 | 629 | template 630 | auto to_string( T const & item ) -> ForNonContainer 631 | { 632 | return make_string( item ); 633 | } 634 | 635 | template 636 | auto to_string( C const & cont ) -> ForContainer 637 | { 638 | std::ostringstream os; 639 | os << "{ "; 640 | for ( auto & x : cont ) 641 | { 642 | os << to_string( x ) << ", "; 643 | } 644 | os << "}"; 645 | return os.str(); 646 | } 647 | 648 | #if lest_FEATURE_WSTRING 649 | inline 650 | auto to_string( std::wstring const & text ) -> std::string 651 | { 652 | std::string result; result.reserve( text.size() ); 653 | 654 | for( auto & chr : text ) 655 | { 656 | result += chr <= 0xff ? static_cast( chr ) : '?'; 657 | } 658 | return to_string( result ); 659 | } 660 | #endif 661 | 662 | template 663 | auto make_value_string( T const & value ) -> std::string 664 | { 665 | std::ostringstream os; os << value; return os.str(); 666 | } 667 | 668 | inline 669 | auto make_memory_string( void const * item, std::size_t size ) -> std::string 670 | { 671 | // reverse order for little endian architectures: 672 | 673 | auto is_little_endian = [] 674 | { 675 | union U { int i = 1; char c[ sizeof(int) ]; }; 676 | 677 | return 1 != U{}.c[ sizeof(int) - 1 ]; 678 | }; 679 | 680 | int i = 0, end = static_cast( size ), inc = 1; 681 | 682 | if ( is_little_endian() ) { i = end - 1; end = inc = -1; } 683 | 684 | unsigned char const * bytes = static_cast( item ); 685 | 686 | std::ostringstream os; 687 | os << "0x" << std::setfill( '0' ) << std::hex; 688 | for ( ; i != end; i += inc ) 689 | { 690 | os << std::setw(2) << static_cast( bytes[i] ) << " "; 691 | } 692 | return os.str(); 693 | } 694 | 695 | template 696 | auto make_memory_string( T const & item ) -> std::string 697 | { 698 | return make_memory_string( &item, sizeof item ); 699 | } 700 | 701 | inline 702 | auto to_string( approx const & appr ) -> std::string 703 | { 704 | return to_string( appr.magnitude() ); 705 | } 706 | 707 | template 708 | auto to_string( L const & lhs, std::string op, R const & rhs ) -> std::string 709 | { 710 | std::ostringstream os; os << to_string( lhs ) << " " << op << " " << to_string( rhs ); return os.str(); 711 | } 712 | 713 | template 714 | struct expression_lhs 715 | { 716 | const L lhs; 717 | 718 | expression_lhs( L lhs ) : lhs( lhs ) {} 719 | 720 | operator result() { return result{ !!lhs, to_string( lhs ) }; } 721 | 722 | template result operator==( R const & rhs ) { return result{ lhs == rhs, to_string( lhs, "==", rhs ) }; } 723 | template result operator!=( R const & rhs ) { return result{ lhs != rhs, to_string( lhs, "!=", rhs ) }; } 724 | template result operator< ( R const & rhs ) { return result{ lhs < rhs, to_string( lhs, "<" , rhs ) }; } 725 | template result operator<=( R const & rhs ) { return result{ lhs <= rhs, to_string( lhs, "<=", rhs ) }; } 726 | template result operator> ( R const & rhs ) { return result{ lhs > rhs, to_string( lhs, ">" , rhs ) }; } 727 | template result operator>=( R const & rhs ) { return result{ lhs >= rhs, to_string( lhs, ">=", rhs ) }; } 728 | }; 729 | 730 | struct expression_decomposer 731 | { 732 | template 733 | expression_lhs operator<< ( L const & operand ) 734 | { 735 | return expression_lhs( operand ); 736 | } 737 | }; 738 | 739 | // Reporter: 740 | 741 | #if lest_FEATURE_COLOURISE 742 | 743 | inline text red ( text words ) { return "\033[1;31m" + words + "\033[0m"; } 744 | inline text green( text words ) { return "\033[1;32m" + words + "\033[0m"; } 745 | inline text gray ( text words ) { return "\033[1;30m" + words + "\033[0m"; } 746 | 747 | inline bool starts_with( text words, text with ) 748 | { 749 | return 0 == words.find( with ); 750 | } 751 | 752 | inline text replace( text words, text from, text to ) 753 | { 754 | size_t pos = words.find( from ); 755 | return pos == std::string::npos ? words : words.replace( pos, from.length(), to ); 756 | } 757 | 758 | inline text colour( text words ) 759 | { 760 | if ( starts_with( words, "failed" ) ) return replace( words, "failed", red ( "failed" ) ); 761 | else if ( starts_with( words, "passed" ) ) return replace( words, "passed", green( "passed" ) ); 762 | 763 | return replace( words, "for", gray( "for" ) ); 764 | } 765 | 766 | inline bool is_cout( std::ostream & os ) { return &os == &std::cout; } 767 | 768 | struct colourise 769 | { 770 | const text words; 771 | 772 | colourise( text words ) 773 | : words( words ) {} 774 | 775 | // only colourise for std::cout, not for a stringstream as used in tests: 776 | 777 | std::ostream & operator()( std::ostream & os ) const 778 | { 779 | return is_cout( os ) ? os << colour( words ) : os << words; 780 | } 781 | }; 782 | 783 | inline std::ostream & operator<<( std::ostream & os, colourise words ) { return words( os ); } 784 | #else 785 | inline text colourise( text words ) { return words; } 786 | #endif 787 | 788 | inline text pluralise( text word, int n ) 789 | { 790 | return n == 1 ? word : word + "s"; 791 | } 792 | 793 | inline std::ostream & operator<<( std::ostream & os, comment note ) 794 | { 795 | return os << (note ? " " + note.info : "" ); 796 | } 797 | 798 | inline std::ostream & operator<<( std::ostream & os, location where ) 799 | { 800 | #ifdef __GNUG__ 801 | return os << where.file << ":" << where.line; 802 | #else 803 | return os << where.file << "(" << where.line << ")"; 804 | #endif 805 | } 806 | 807 | inline void report( std::ostream & os, message const & e, text test ) 808 | { 809 | os << e.where << ": " << colourise( e.kind ) << e.note << ": " << test << ": " << colourise( e.what() ) << std::endl; 810 | } 811 | 812 | // Test runner: 813 | 814 | #if lest_FEATURE_REGEX_SEARCH 815 | inline bool search( text re, text line ) 816 | { 817 | return std::regex_search( line, std::regex( re ) ); 818 | } 819 | #else 820 | inline bool search( text part, text line ) 821 | { 822 | auto case_insensitive_equal = []( char a, char b ) 823 | { 824 | return tolower( a ) == tolower( b ); 825 | }; 826 | 827 | return std::search( 828 | line.begin(), line.end(), 829 | part.begin(), part.end(), case_insensitive_equal ) != line.end(); 830 | } 831 | #endif 832 | 833 | inline bool match( texts whats, text line ) 834 | { 835 | for ( auto & what : whats ) 836 | { 837 | if ( search( what, line ) ) 838 | return true; 839 | } 840 | return false; 841 | } 842 | 843 | inline bool select( text name, texts include ) 844 | { 845 | auto none = []( texts args ) { return args.size() == 0; }; 846 | 847 | #if lest_FEATURE_REGEX_SEARCH 848 | auto hidden = []( text name ){ return match( { "\\[\\..*", "\\[hide\\]" }, name ); }; 849 | #else 850 | auto hidden = []( text name ){ return match( { "[.", "[hide]" }, name ); }; 851 | #endif 852 | 853 | if ( none( include ) ) 854 | { 855 | return ! hidden( name ); 856 | } 857 | 858 | bool any = false; 859 | for ( auto pos = include.rbegin(); pos != include.rend(); ++pos ) 860 | { 861 | auto & part = *pos; 862 | 863 | if ( part == "@" || part == "*" ) 864 | return true; 865 | 866 | if ( search( part, name ) ) 867 | return true; 868 | 869 | if ( '!' == part[0] ) 870 | { 871 | any = true; 872 | if ( search( part.substr(1), name ) ) 873 | return false; 874 | } 875 | else 876 | { 877 | any = false; 878 | } 879 | } 880 | return any && ! hidden( name ); 881 | } 882 | 883 | inline int indefinite( int repeat ) { return repeat == -1; } 884 | 885 | using seed_t = unsigned long; 886 | 887 | struct options 888 | { 889 | bool help = false; 890 | bool abort = false; 891 | bool count = false; 892 | bool list = false; 893 | bool tags = false; 894 | bool time = false; 895 | bool pass = false; 896 | bool lexical = false; 897 | bool random = false; 898 | bool version = false; 899 | int repeat = 1; 900 | seed_t seed = 0; 901 | }; 902 | 903 | struct env 904 | { 905 | std::ostream & os; 906 | bool pass; 907 | text testing; 908 | 909 | env( std::ostream & os, bool pass ) 910 | : os( os ), pass( pass ), testing() {} 911 | 912 | env & operator()( text test ) 913 | { 914 | testing = test; return *this; 915 | } 916 | }; 917 | 918 | struct action 919 | { 920 | std::ostream & os; 921 | 922 | action( std::ostream & os ) : os( os ) {} 923 | 924 | action( action const & ) = delete; 925 | void operator=( action const & ) = delete; 926 | 927 | operator int() { return 0; } 928 | bool abort() { return false; } 929 | action & operator()( test ) { return *this; } 930 | }; 931 | 932 | struct print : action 933 | { 934 | print( std::ostream & os ) : action( os ) {} 935 | 936 | print & operator()( test testing ) 937 | { 938 | os << testing.name << "\n"; return *this; 939 | } 940 | }; 941 | 942 | inline texts tags( text name, texts result = {} ) 943 | { 944 | auto none = std::string::npos; 945 | auto lb = name.find_first_of( "[" ); 946 | auto rb = name.find_first_of( "]" ); 947 | 948 | if ( lb == none || rb == none ) 949 | return result; 950 | 951 | result.emplace_back( name.substr( lb, rb - lb + 1 ) ); 952 | 953 | return tags( name.substr( rb + 1 ), result ); 954 | } 955 | 956 | struct ptags : action 957 | { 958 | std::set result; 959 | 960 | ptags( std::ostream & os ) : action( os ), result() {} 961 | 962 | ptags & operator()( test testing ) 963 | { 964 | for ( auto & tag : tags( testing.name ) ) 965 | result.insert( tag ); 966 | 967 | return *this; 968 | } 969 | 970 | ~ptags() 971 | { 972 | std::copy( result.begin(), result.end(), std::ostream_iterator( os, "\n" ) ); 973 | } 974 | }; 975 | 976 | struct count : action 977 | { 978 | int n = 0; 979 | 980 | count( std::ostream & os ) : action( os ) {} 981 | 982 | count & operator()( test ) { ++n; return *this; } 983 | 984 | ~count() 985 | { 986 | os << n << " selected " << pluralise("test", n) << "\n"; 987 | } 988 | }; 989 | 990 | struct timer 991 | { 992 | using time = std::chrono::high_resolution_clock; 993 | 994 | time::time_point start = time::now(); 995 | 996 | double elapsed_seconds() const 997 | { 998 | return 1e-6 * std::chrono::duration_cast< std::chrono::microseconds >( time::now() - start ).count(); 999 | } 1000 | }; 1001 | 1002 | struct times : action 1003 | { 1004 | env output; 1005 | options option; 1006 | int selected = 0; 1007 | int failures = 0; 1008 | 1009 | timer total; 1010 | 1011 | times( std::ostream & os, options option ) 1012 | : action( os ), output( os, option.pass ), option( option ), total() 1013 | { 1014 | os << std::setfill(' ') << std::fixed << std::setprecision( lest_FEATURE_TIME_PRECISION ); 1015 | } 1016 | 1017 | operator int() { return failures; } 1018 | 1019 | bool abort() { return option.abort && failures > 0; } 1020 | 1021 | times & operator()( test testing ) 1022 | { 1023 | timer t; 1024 | 1025 | try 1026 | { 1027 | testing.behaviour( output( testing.name ) ); 1028 | } 1029 | catch( message const & ) 1030 | { 1031 | ++failures; 1032 | } 1033 | 1034 | os << std::setw(3) << ( 1000 * t.elapsed_seconds() ) << " ms: " << testing.name << "\n"; 1035 | 1036 | return *this; 1037 | } 1038 | 1039 | ~times() 1040 | { 1041 | os << "Elapsed time: " << std::setprecision(1) << total.elapsed_seconds() << " s\n"; 1042 | } 1043 | }; 1044 | 1045 | struct confirm : action 1046 | { 1047 | env output; 1048 | options option; 1049 | int selected = 0; 1050 | int failures = 0; 1051 | 1052 | confirm( std::ostream & os, options option ) 1053 | : action( os ), output( os, option.pass ), option( option ) {} 1054 | 1055 | operator int() { return failures; } 1056 | 1057 | bool abort() { return option.abort && failures > 0; } 1058 | 1059 | confirm & operator()( test testing ) 1060 | { 1061 | try 1062 | { 1063 | ++selected; testing.behaviour( output( testing.name ) ); 1064 | } 1065 | catch( message const & e ) 1066 | { 1067 | ++failures; report( os, e, testing.name ); 1068 | } 1069 | return *this; 1070 | } 1071 | 1072 | ~confirm() 1073 | { 1074 | if ( failures > 0 ) 1075 | { 1076 | os << failures << " out of " << selected << " selected " << pluralise("test", selected) << " " << colourise( "failed.\n" ); 1077 | } 1078 | else if ( option.pass ) 1079 | { 1080 | os << "All " << selected << " selected " << pluralise("test", selected) << " " << colourise( "passed.\n" ); 1081 | } 1082 | } 1083 | }; 1084 | 1085 | template 1086 | bool abort( Action & perform ) 1087 | { 1088 | return perform.abort(); 1089 | } 1090 | 1091 | template< typename Action > 1092 | Action && for_test( tests specification, texts in, Action && perform, int n = 1 ) 1093 | { 1094 | for ( int i = 0; indefinite( n ) || i < n; ++i ) 1095 | { 1096 | for ( auto & testing : specification ) 1097 | { 1098 | if ( select( testing.name, in ) ) 1099 | if ( abort( perform( testing ) ) ) 1100 | return std::move( perform ); 1101 | } 1102 | } 1103 | return std::move( perform ); 1104 | } 1105 | 1106 | inline void sort( tests & specification ) 1107 | { 1108 | auto test_less = []( test const & a, test const & b ) { return a.name < b.name; }; 1109 | std::sort( specification.begin(), specification.end(), test_less ); 1110 | } 1111 | 1112 | inline void shuffle( tests & specification, options option ) 1113 | { 1114 | std::shuffle( specification.begin(), specification.end(), std::mt19937( option.seed ) ); 1115 | } 1116 | 1117 | // workaround MinGW bug, http://stackoverflow.com/a/16132279: 1118 | 1119 | inline int stoi( text num ) 1120 | { 1121 | return static_cast( std::strtol( num.c_str(), nullptr, 10 ) ); 1122 | } 1123 | 1124 | inline bool is_number( text arg ) 1125 | { 1126 | return std::all_of( arg.begin(), arg.end(), ::isdigit ); 1127 | } 1128 | 1129 | inline seed_t seed( text opt, text arg ) 1130 | { 1131 | if ( is_number( arg ) ) 1132 | return static_cast( lest::stoi( arg ) ); 1133 | 1134 | if ( arg == "time" ) 1135 | return static_cast( std::chrono::high_resolution_clock::now().time_since_epoch().count() ); 1136 | 1137 | throw std::runtime_error( "expecting 'time' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" ); 1138 | } 1139 | 1140 | inline int repeat( text opt, text arg ) 1141 | { 1142 | const int num = lest::stoi( arg ); 1143 | 1144 | if ( indefinite( num ) || num >= 0 ) 1145 | return num; 1146 | 1147 | throw std::runtime_error( "expecting '-1' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" ); 1148 | } 1149 | 1150 | inline auto split_option( text arg ) -> std::tuple 1151 | { 1152 | auto pos = arg.rfind( '=' ); 1153 | 1154 | return pos == text::npos 1155 | ? std::make_tuple( arg, "" ) 1156 | : std::make_tuple( arg.substr( 0, pos ), arg.substr( pos + 1 ) ); 1157 | } 1158 | 1159 | inline auto split_arguments( texts args ) -> std::tuple 1160 | { 1161 | options option; texts in; 1162 | 1163 | bool in_options = true; 1164 | 1165 | for ( auto & arg : args ) 1166 | { 1167 | if ( in_options ) 1168 | { 1169 | text opt, val; 1170 | std::tie( opt, val ) = split_option( arg ); 1171 | 1172 | if ( opt[0] != '-' ) { in_options = false; } 1173 | else if ( opt == "--" ) { in_options = false; continue; } 1174 | else if ( opt == "-h" || "--help" == opt ) { option.help = true; continue; } 1175 | else if ( opt == "-a" || "--abort" == opt ) { option.abort = true; continue; } 1176 | else if ( opt == "-c" || "--count" == opt ) { option.count = true; continue; } 1177 | else if ( opt == "-g" || "--list-tags" == opt ) { option.tags = true; continue; } 1178 | else if ( opt == "-l" || "--list-tests" == opt ) { option.list = true; continue; } 1179 | else if ( opt == "-t" || "--time" == opt ) { option.time = true; continue; } 1180 | else if ( opt == "-p" || "--pass" == opt ) { option.pass = true; continue; } 1181 | else if ( "--version" == opt ) { option.version = true; continue; } 1182 | else if ( opt == "--order" && "declared" == val ) { /* by definition */ ; continue; } 1183 | else if ( opt == "--order" && "lexical" == val ) { option.lexical = true; continue; } 1184 | else if ( opt == "--order" && "random" == val ) { option.random = true; continue; } 1185 | else if ( opt == "--random-seed" ) { option.seed = seed ( "--random-seed", val ); continue; } 1186 | else if ( opt == "--repeat" ) { option.repeat = repeat( "--repeat" , val ); continue; } 1187 | else throw std::runtime_error( "unrecognised option '" + arg + "' (try option --help)" ); 1188 | } 1189 | in.push_back( arg ); 1190 | } 1191 | return std::make_tuple( option, in ); 1192 | } 1193 | 1194 | inline int usage( std::ostream & os ) 1195 | { 1196 | os << 1197 | "\nUsage: test [options] [test-spec ...]\n" 1198 | "\n" 1199 | "Options:\n" 1200 | " -h, --help this help message\n" 1201 | " -a, --abort abort at first failure\n" 1202 | " -c, --count count selected tests\n" 1203 | " -g, --list-tags list tags of selected tests\n" 1204 | " -l, --list-tests list selected tests\n" 1205 | " -p, --pass also report passing tests\n" 1206 | " -t, --time list duration of selected tests\n" 1207 | " --order=declared use source code test order (default)\n" 1208 | " --order=lexical use lexical sort test order\n" 1209 | " --order=random use random test order\n" 1210 | " --random-seed=n use n for random generator seed\n" 1211 | " --random-seed=time use time for random generator seed\n" 1212 | " --repeat=n repeat selected tests n times (-1: indefinite)\n" 1213 | " --version report lest version and compiler used\n" 1214 | " -- end options\n" 1215 | "\n" 1216 | "Test specification:\n" 1217 | " \"@\", \"*\" all tests, unless excluded\n" 1218 | " empty all tests, unless tagged [hide] or [.optional-name]\n" 1219 | #if lest_FEATURE_REGEX_SEARCH 1220 | " \"re\" select tests that match regular expression\n" 1221 | " \"!re\" omit tests that match regular expression\n" 1222 | #else 1223 | " \"text\" select tests that contain text (case insensitive)\n" 1224 | " \"!text\" omit tests that contain text (case insensitive)\n" 1225 | #endif 1226 | ; 1227 | return 0; 1228 | } 1229 | 1230 | inline text compiler() 1231 | { 1232 | std::ostringstream os; 1233 | #if defined (__clang__ ) 1234 | os << "clang " << __clang_version__; 1235 | #elif defined (__GNUC__ ) 1236 | os << "gcc " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__; 1237 | #elif defined ( _MSC_VER ) 1238 | os << "MSVC " << (_MSC_VER / 100 - 5 - (_MSC_VER < 1900)) << " (" << _MSC_VER << ")"; 1239 | #else 1240 | os << "[compiler]"; 1241 | #endif 1242 | return os.str(); 1243 | } 1244 | 1245 | inline int version( std::ostream & os ) 1246 | { 1247 | os << "lest version " << lest_VERSION << "\n" 1248 | << "Compiled with " << compiler() << " on " << __DATE__ << " at " << __TIME__ << ".\n" 1249 | << "For more information, see https://github.com/martinmoene/lest.\n"; 1250 | return 0; 1251 | } 1252 | 1253 | inline int run( tests specification, texts arguments, std::ostream & os = std::cout ) 1254 | { 1255 | try 1256 | { 1257 | options option; texts in; 1258 | std::tie( option, in ) = split_arguments( arguments ); 1259 | 1260 | if ( option.lexical ) { sort( specification ); } 1261 | if ( option.random ) { shuffle( specification, option ); } 1262 | 1263 | if ( option.help ) { return usage ( os ); } 1264 | if ( option.version ) { return version ( os ); } 1265 | if ( option.count ) { return for_test( specification, in, count( os ) ); } 1266 | if ( option.list ) { return for_test( specification, in, print( os ) ); } 1267 | if ( option.tags ) { return for_test( specification, in, ptags( os ) ); } 1268 | if ( option.time ) { return for_test( specification, in, times( os, option ) ); } 1269 | 1270 | return for_test( specification, in, confirm( os, option ), option.repeat ); 1271 | } 1272 | catch ( std::exception const & e ) 1273 | { 1274 | os << "Error: " << e.what() << "\n"; 1275 | return 1; 1276 | } 1277 | } 1278 | 1279 | inline int run( tests specification, int argc, char * argv[], std::ostream & os = std::cout ) 1280 | { 1281 | return run( specification, texts( argv + 1, argv + argc ), os ); 1282 | } 1283 | 1284 | template 1285 | int run( test const (&specification)[N], texts arguments, std::ostream & os = std::cout ) 1286 | { 1287 | return run( tests( specification, specification + N ), arguments, os ); 1288 | } 1289 | 1290 | template 1291 | int run( test const (&specification)[N], std::ostream & os = std::cout ) 1292 | { 1293 | return run( tests( specification, specification + N ), {}, os ); 1294 | } 1295 | 1296 | template 1297 | int run( test const (&specification)[N], int argc, char * argv[], std::ostream & os = std::cout ) 1298 | { 1299 | return run( tests( specification, specification + N ), texts( argv + 1, argv + argc ), os ); 1300 | } 1301 | 1302 | } // namespace lest 1303 | 1304 | #endif // LEST_LEST_HPP_INCLUDED 1305 | -------------------------------------------------------------------------------- /test/test.bat: -------------------------------------------------------------------------------- 1 | @REM Copyright (c) 2018, ITHare.com 2 | @REM All rights reserved. 3 | @REM 4 | @REM Redistribution and use in source and binary forms, with or without 5 | @REM modification, are permitted provided that the following conditions are met: 6 | @REM 7 | @REM * Redistributions of source code must retain the above copyright notice, this 8 | @REM list of conditions and the following disclaimer. 9 | @REM 10 | @REM * Redistributions in binary form must reproduce the above copyright notice, 11 | @REM this list of conditions and the following disclaimer in the documentation 12 | @REM and/or other materials provided with the distribution. 13 | @REM 14 | @REM * Neither the name of the copyright holder nor the names of its 15 | @REM contributors may be used to endorse or promote products derived from 16 | @REM this software without specific prior written permission. 17 | @REM 18 | @REM THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | @REM AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | @REM IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | @REM DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | @REM FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | @REM DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | @REM SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | @REM CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | @REM OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | @REM OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | @ECHO OFF 30 | ECHO EXPECT C4018 WARNINGS in the next compile 31 | cl /std:c++latest /EHsc /W3 /DWARNINGS_EXPECTED test.cpp 32 | IF NOT ERRORLEVEL 1 GOTO LABEL1 33 | EXIT /B 34 | :LABEL1 35 | test.exe 36 | IF NOT ERRORLEVEL 1 GOTO LABEL2 37 | EXIT /B 38 | :LABEL2 39 | echo NO warnings expected in this one 40 | cl /std:c++latest /EHsc /W3 /WX test.cpp 41 | IF NOT ERRORLEVEL 1 GOTO LABEL3 42 | EXIT /B 43 | :LABEL3 44 | test.exe 45 | IF NOT ERRORLEVEL 1 GOTO LABEL4 46 | EXIT /B 47 | :LABEL4 48 | ECHO OK 49 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2018, ITHare.com 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | */ 32 | 33 | #include "../src/type/type.h" 34 | #include "lest.hpp" 35 | 36 | using namespace ithare::util::type; 37 | 38 | #ifdef WARNINGS_EXPECTED 39 | static_assert(-1>1U); 40 | static_assert( short(-1) < (unsigned short)(1) );//stands (if sizeof(short) uint64_t(1) ); 43 | static_assert( -1 == unsigned(-1) ); 44 | #endif 45 | 46 | static_assert( intuitive::lt(-1,1U) ); 47 | static_assert( intuitive::lt(short(-1),(unsigned short)(1)) ); 48 | static_assert( intuitive::lt(int64_t(-1), 1U) ); 49 | static_assert( intuitive::lt(-1,uint64_t(1)) ); 50 | static_assert( intuitive::le(-1,1U) ); 51 | static_assert( intuitive::le(short(-1),(unsigned short)(1)) ); 52 | static_assert( intuitive::le(int64_t(-1), 1U) ); 53 | static_assert( intuitive::le(-1,uint64_t(1)) ); 54 | static_assert( !intuitive::lt(1U,-1) ); 55 | static_assert( !intuitive::lt((unsigned short)(1),short(-1)) ); 56 | static_assert( !intuitive::lt( 1U,int64_t(-1)) ); 57 | static_assert( !intuitive::lt(uint64_t(1),-1) ); 58 | static_assert( !intuitive::le(1U,-1) ); 59 | static_assert( !intuitive::le((unsigned short)(1),short(-1)) ); 60 | static_assert( !intuitive::le( 1U,int64_t(-1)) ); 61 | static_assert( !intuitive::le(uint64_t(1),-1) ); 62 | 63 | static_assert( !intuitive::gt(-1,1U) ); 64 | static_assert( !intuitive::gt(short(-1),(unsigned short)(1)) ); 65 | static_assert( !intuitive::gt(int64_t(-1), 1U) ); 66 | static_assert( !intuitive::gt(-1,uint64_t(1)) ); 67 | static_assert( intuitive::gt(1U,-1) ); 68 | static_assert( intuitive::gt((unsigned short)(1),short(-1)) ); 69 | static_assert( intuitive::gt( 1U,int64_t(-1)) ); 70 | static_assert( intuitive::gt(uint64_t(1),-1) ); 71 | static_assert( intuitive::ge(1U,-1) ); 72 | static_assert( intuitive::ge((unsigned short)(1),short(-1)) ); 73 | static_assert( intuitive::ge( 1U,int64_t(-1)) ); 74 | static_assert( intuitive::ge(uint64_t(1),-1) ); 75 | 76 | static_assert( intuitive::eq(-1,long(-1)) ); 77 | static_assert( !intuitive::eq(-1,unsigned(-1)) ); 78 | static_assert( !intuitive::ne(-1,(long long)(-1)) ); 79 | static_assert( intuitive::ne(-1,unsigned(-1)) ); 80 | 81 | const lest::test specification[] = { 82 | CASE("built-in") { 83 | EXPECT(-1<1); 84 | #ifdef WARNINGS_EXPECTED 85 | EXPECT(-1>1U); 86 | EXPECT( short(-1) < (unsigned short)(1) );//stands (if sizeof(short) uint64_t(1) ); 89 | EXPECT( -1 == unsigned(-1) ); 90 | #endif 91 | }, 92 | CASE("lt") { 93 | EXPECT( intuitive::lt(-1,1U) ); 94 | EXPECT( intuitive::lt(short(-1),(unsigned short)(1)) ); 95 | EXPECT( intuitive::lt(int64_t(-1), 1U) ); 96 | EXPECT( intuitive::lt(-1,uint64_t(1)) ); 97 | EXPECT( intuitive::le(-1,1U) ); 98 | }, 99 | CASE("gt") { 100 | EXPECT( intuitive::gt(1U,-1) ); 101 | EXPECT( intuitive::gt((unsigned short)(1),short(-1)) ); 102 | EXPECT( intuitive::gt( 1U,int64_t(-1)) ); 103 | EXPECT( intuitive::gt(uint64_t(1),-1) ); 104 | EXPECT( intuitive::ge(1U,-1) ); 105 | }, 106 | CASE("eq") { 107 | EXPECT( !intuitive::eq(-1,unsigned(-1)) ); 108 | EXPECT( intuitive::ne(-1,unsigned(-1)) ); 109 | }, 110 | }; 111 | 112 | #define OUT_SIZEOF(t) "sizeof(" #t ")=" << sizeof(t) 113 | #define OUT_CMP(cond) #cond ": " << ((cond)?"true":"false") 114 | 115 | int main(int argc, char** argv) { 116 | std::cout << OUT_SIZEOF(short) << " " << OUT_SIZEOF(int) << " " << OUT_SIZEOF(int64_t) << std::endl; 117 | 118 | #ifdef WARNINGS_EXPECTED 119 | std::cout << OUT_CMP( int(-1) < unsigned(1) ) << std::endl; 120 | std::cout << OUT_CMP( -1 < 1U ) << std::endl; 121 | std::cout << OUT_CMP( short(-1) < (unsigned short)(1) ) << std::endl; 122 | std::cout << OUT_CMP( int64_t(-1) < unsigned(1) ) << std::endl; 123 | std::cout << OUT_CMP( int(-1) < uint64_t(1) ) << std::endl; 124 | #endif 125 | 126 | std::cout << OUT_CMP( intuitive::lt(int(-1),unsigned(1)) ) << std::endl; 127 | std::cout << OUT_CMP( intuitive::lt(-1,1U) ) << std::endl; 128 | std::cout << OUT_CMP( intuitive::lt(short(-1), (unsigned short)(1)) ) << std::endl; 129 | std::cout << OUT_CMP( intuitive::lt(int64_t(-1), unsigned(1)) ) << std::endl; 130 | std::cout << OUT_CMP( intuitive::lt(int(-1), uint64_t(1)) ) << std::endl; 131 | 132 | std::cout << OUT_CMP( intuitive::gt(unsigned(1),int(-1)) ) << std::endl; 133 | std::cout << OUT_CMP( intuitive::gt(1U,-1) ) << std::endl; 134 | std::cout << OUT_CMP( intuitive::gt((unsigned short)(1),short(-1)) ) << std::endl; 135 | std::cout << OUT_CMP( intuitive::gt(unsigned(1),int64_t(-1)) ) << std::endl; 136 | std::cout << OUT_CMP( intuitive::gt(uint64_t(1),int(-1)) ) << std::endl; 137 | 138 | return lest::run(specification,argc,argv); 139 | } 140 | 141 | -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018, ITHare.com 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # * Neither the name of the copyright holder nor the names of its 15 | # contributors may be used to endorse or promote products derived from 16 | # this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | echo "EXPECT -Wsign-compare WARNINGS in the next compile" 30 | g++ -std=c++1z -lstdc++ -Wall -Wextra -Wno-missing-braces -DWARNINGS_EXPECTED -o test test.cpp 31 | if [ ! $? -eq 0 ]; then 32 | exit 1 33 | fi 34 | ./test 35 | if [ ! $? -eq 0 ]; then 36 | exit 1 37 | fi 38 | echo "NO warnings expected in this one" 39 | g++ -std=c++1z -lstdc++ -Wall -Wextra -Wno-missing-braces -Werror -o test test.cpp 40 | if [ ! $? -eq 0 ]; then 41 | exit 1 42 | fi 43 | ./test 44 | if [ ! $? -eq 0 ]; then 45 | exit 1 46 | fi 47 | echo "OK" 48 | --------------------------------------------------------------------------------