1 #ifndef RAPID_CXX_TEST_H 2 #define RAPID_CXX_TEST_H 3 4 # include <cstddef> 5 # include <cstdlib> 6 # include <cstdio> 7 # include <cstring> 8 # include <cassert> 9 10 #include "test_macros.h" 11 12 #if !defined(RAPID_CXX_TEST_NO_SYSTEM_HEADER) || !defined(__GNUC__) 13 #pragma GCC system_header 14 #endif 15 16 # define RAPID_CXX_TEST_PP_CAT(x, y) RAPID_CXX_TEST_PP_CAT_2(x, y) 17 # define RAPID_CXX_TEST_PP_CAT_2(x, y) x##y 18 19 # define RAPID_CXX_TEST_PP_STR(...) RAPID_CXX_TEST_PP_STR_2(__VA_ARGS__) 20 # define RAPID_CXX_TEST_PP_STR_2(...) #__VA_ARGS__ 21 22 # if defined(__GNUC__) 23 # define TEST_FUNC_NAME() __PRETTY_FUNCTION__ 24 # define RAPID_CXX_TEST_UNUSED __attribute__((unused)) 25 # else 26 # define TEST_FUNC_NAME() __func__ 27 # define RAPID_CXX_TEST_UNUSED 28 # endif 29 30 //////////////////////////////////////////////////////////////////////////////// 31 // TEST_SUITE 32 //////////////////////////////////////////////////////////////////////////////// 33 # define TEST_SUITE(Name) \ 34 namespace Name \ 35 { \ 36 inline ::rapid_cxx_test::test_suite & get_test_suite() \ 37 { \ 38 static ::rapid_cxx_test::test_suite m_suite(#Name); \ 39 return m_suite; \ 40 } \ 41 \ 42 inline int unit_test_main(int, char**) \ 43 { \ 44 ::rapid_cxx_test::test_runner runner(get_test_suite()); \ 45 return runner.run(); \ 46 } \ 47 } \ 48 int main(int argc, char **argv) \ 49 { \ 50 return Name::unit_test_main(argc, argv); \ 51 } \ 52 namespace Name \ 53 { /* namespace closed in TEST_SUITE_END */ 54 # 55 56 //////////////////////////////////////////////////////////////////////////////// 57 // TEST_SUITE_END 58 //////////////////////////////////////////////////////////////////////////////// 59 # define TEST_SUITE_END() \ 60 } /* namespace opened in TEST_SUITE(...) */ 61 # 62 63 //////////////////////////////////////////////////////////////////////////////// 64 // TEST_CASE 65 //////////////////////////////////////////////////////////////////////////////// 66 67 # if !defined(__clang__) 68 # 69 # define TEST_CASE(Name) \ 70 void Name(); \ 71 static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)() \ 72 { \ 73 Name(); \ 74 } \ 75 static ::rapid_cxx_test::registrar \ 76 RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)( \ 77 get_test_suite() \ 78 , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \ 79 ); \ 80 void Name() 81 # 82 # else /* __clang__ */ 83 # 84 # define TEST_CASE(Name) \ 85 void Name(); \ 86 static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)() \ 87 { \ 88 Name(); \ 89 } \ 90 _Pragma("clang diagnostic push") \ 91 _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ 92 static ::rapid_cxx_test::registrar \ 93 RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)( \ 94 get_test_suite() \ 95 , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \ 96 ); \ 97 _Pragma("clang diagnostic pop") \ 98 void Name() 99 # 100 # endif /* !defined(__clang__) */ 101 102 103 # define TEST_SET_CHECKPOINT() ::rapid_cxx_test::set_checkpoint(__FILE__, TEST_FUNC_NAME(), __LINE__) 104 105 #define RAPID_CXX_TEST_OUTCOME() 106 107 //////////////////////////////////////////////////////////////////////////////// 108 // TEST_UNSUPPORTED 109 //////////////////////////////////////////////////////////////////////////////// 110 # define TEST_UNSUPPORTED() \ 111 do { \ 112 TEST_SET_CHECKPOINT(); \ 113 ::rapid_cxx_test::test_outcome m_f( \ 114 ::rapid_cxx_test::failure_type::unsupported, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 115 , "", "" \ 116 ); \ 117 ::rapid_cxx_test::get_reporter().report(m_f); \ 118 return; \ 119 } while (false) 120 # 121 122 123 //////////////////////////////////////////////////////////////////////////////// 124 // BASIC ASSERTIONS 125 //////////////////////////////////////////////////////////////////////////////// 126 # define TEST_WARN(...) \ 127 do { \ 128 TEST_SET_CHECKPOINT(); \ 129 ::rapid_cxx_test::test_outcome m_f( \ 130 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 131 , "TEST_WARN(" #__VA_ARGS__ ")", "" \ 132 ); \ 133 if (not (__VA_ARGS__)) { \ 134 m_f.type = ::rapid_cxx_test::failure_type::warn; \ 135 } \ 136 ::rapid_cxx_test::get_reporter().report(m_f); \ 137 } while (false) 138 # 139 140 # define TEST_CHECK(...) \ 141 do { \ 142 TEST_SET_CHECKPOINT(); \ 143 ::rapid_cxx_test::test_outcome m_f( \ 144 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 145 , "TEST_CHECK(" #__VA_ARGS__ ")", "" \ 146 ); \ 147 if (not (__VA_ARGS__)) { \ 148 m_f.type = ::rapid_cxx_test::failure_type::check; \ 149 } \ 150 ::rapid_cxx_test::get_reporter().report(m_f); \ 151 } while (false) 152 # 153 154 # define TEST_REQUIRE(...) \ 155 do { \ 156 TEST_SET_CHECKPOINT(); \ 157 ::rapid_cxx_test::test_outcome m_f( \ 158 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 159 , "TEST_REQUIRE(" #__VA_ARGS__ ")", "" \ 160 ); \ 161 if (not (__VA_ARGS__)) { \ 162 m_f.type = ::rapid_cxx_test::failure_type::require; \ 163 } \ 164 ::rapid_cxx_test::get_reporter().report(m_f); \ 165 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 166 return; \ 167 } \ 168 } while (false) 169 # 170 171 # define TEST_ASSERT(...) \ 172 do { \ 173 TEST_SET_CHECKPOINT(); \ 174 ::rapid_cxx_test::test_outcome m_f( \ 175 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 176 , "TEST_ASSERT(" #__VA_ARGS__ ")", "" \ 177 ); \ 178 if (not (__VA_ARGS__)) { \ 179 m_f.type = ::rapid_cxx_test::failure_type::assert; \ 180 } \ 181 ::rapid_cxx_test::get_reporter().report(m_f); \ 182 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 183 std::abort(); \ 184 } \ 185 } while (false) 186 # 187 188 //////////////////////////////////////////////////////////////////////////////// 189 // TEST_CHECK_NO_THROW / TEST_CHECK_THROW 190 //////////////////////////////////////////////////////////////////////////////// 191 #ifndef TEST_HAS_NO_EXCEPTIONS 192 193 # define TEST_CHECK_NO_THROW(...) \ 194 do { \ 195 TEST_SET_CHECKPOINT(); \ 196 ::rapid_cxx_test::test_outcome m_f( \ 197 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 198 , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", "" \ 199 ); \ 200 try { \ 201 (static_cast<void>(__VA_ARGS__)); \ 202 } catch (...) { \ 203 m_f.type = ::rapid_cxx_test::failure_type::check; \ 204 } \ 205 ::rapid_cxx_test::get_reporter().report(m_f); \ 206 } while (false) 207 # 208 209 # define TEST_CHECK_THROW(Except, ...) \ 210 do { \ 211 TEST_SET_CHECKPOINT(); \ 212 ::rapid_cxx_test::test_outcome m_f( \ 213 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 214 , "TEST_CHECK_THROW(" #Except "," #__VA_ARGS__ ")", "" \ 215 ); \ 216 try { \ 217 (static_cast<void>(__VA_ARGS__)); \ 218 m_f.type = ::rapid_cxx_test::failure_type::check; \ 219 } catch (Except const &) {} \ 220 ::rapid_cxx_test::get_reporter().report(m_f); \ 221 } while (false) 222 # 223 224 #define TEST_CHECK_THROW_RESULT(Except, Checker, ...) \ 225 do { \ 226 TEST_SET_CHECKPOINT(); \ 227 ::rapid_cxx_test::test_outcome m_f(::rapid_cxx_test::failure_type::none, \ 228 __FILE__, TEST_FUNC_NAME(), __LINE__, \ 229 "TEST_CHECK_THROW_RESULT(" #Except \ 230 "," #Checker "," #__VA_ARGS__ ")", \ 231 ""); \ 232 try { \ 233 (static_cast<void>(__VA_ARGS__)); \ 234 m_f.type = ::rapid_cxx_test::failure_type::check; \ 235 } catch (Except const& Caught) { \ 236 Checker(Caught); \ 237 } \ 238 ::rapid_cxx_test::get_reporter().report(m_f); \ 239 } while (false) 240 # 241 242 #else // TEST_HAS_NO_EXCEPTIONS 243 244 # define TEST_CHECK_NO_THROW(...) \ 245 do { \ 246 TEST_SET_CHECKPOINT(); \ 247 ::rapid_cxx_test::test_outcome m_f( \ 248 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 249 , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", "" \ 250 ); \ 251 (static_cast<void>(__VA_ARGS__)); \ 252 ::rapid_cxx_test::get_reporter().report(m_f); \ 253 } while (false) 254 # 255 256 #define TEST_CHECK_THROW(Except, ...) ((void)0) 257 #define TEST_CHECK_THROW_RESULT(Except, Checker, ...) ((void)0) 258 259 #endif // TEST_HAS_NO_EXCEPTIONS 260 261 262 //////////////////////////////////////////////////////////////////////////////// 263 // TEST_REQUIRE_NO_THROW / TEST_REQUIRE_THROWs 264 //////////////////////////////////////////////////////////////////////////////// 265 #ifndef TEST_HAS_NO_EXCEPTIONS 266 267 # define TEST_REQUIRE_NO_THROW(...) \ 268 do { \ 269 TEST_SET_CHECKPOINT(); \ 270 ::rapid_cxx_test::test_outcome m_f( \ 271 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 272 , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \ 273 ); \ 274 try { \ 275 (static_cast<void>(__VA_ARGS__)); \ 276 } catch (...) { \ 277 m_f.type = ::rapid_cxx_test::failure_type::require; \ 278 } \ 279 ::rapid_cxx_test::get_reporter().report(m_f); \ 280 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 281 return; \ 282 } \ 283 } while (false) 284 # 285 286 # define TEST_REQUIRE_THROW(Except, ...) \ 287 do { \ 288 TEST_SET_CHECKPOINT(); \ 289 ::rapid_cxx_test::test_outcome m_f( \ 290 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 291 , "TEST_REQUIRE_THROW(" #Except "," #__VA_ARGS__ ")", "" \ 292 ); \ 293 try { \ 294 (static_cast<void>(__VA_ARGS__)); \ 295 m_f.type = ::rapid_cxx_test::failure_type::require; \ 296 } catch (Except const &) {} \ 297 ::rapid_cxx_test::get_reporter().report(m_f); \ 298 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 299 return; \ 300 } \ 301 } while (false) 302 # 303 304 #else // TEST_HAS_NO_EXCEPTIONS 305 306 # define TEST_REQUIRE_NO_THROW(...) \ 307 do { \ 308 TEST_SET_CHECKPOINT(); \ 309 ::rapid_cxx_test::test_outcome m_f( \ 310 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 311 , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \ 312 ); \ 313 (static_cast<void>(__VA_ARGS__)); \ 314 ::rapid_cxx_test::get_reporter().report(m_f); \ 315 } while (false) 316 # 317 318 #define TEST_REQUIRE_THROW(Except, ...) ((void)0) 319 320 #endif // TEST_HAS_NO_EXCEPTIONS 321 322 //////////////////////////////////////////////////////////////////////////////// 323 // TEST_ASSERT_NO_THROW / TEST_ASSERT_THROW 324 //////////////////////////////////////////////////////////////////////////////// 325 #ifndef TEST_HAS_NO_EXCEPTIONS 326 327 # define TEST_ASSERT_NO_THROW(...) \ 328 do { \ 329 TEST_SET_CHECKPOINT(); \ 330 ::rapid_cxx_test::test_outcome m_f( \ 331 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 332 , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \ 333 ); \ 334 try { \ 335 (static_cast<void>(__VA_ARGS__)); \ 336 } catch (...) { \ 337 m_f.type = ::rapid_cxx_test::failure_type::assert; \ 338 } \ 339 ::rapid_cxx_test::get_reporter().report(m_f); \ 340 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 341 std::abort(); \ 342 } \ 343 } while (false) 344 # 345 346 # define TEST_ASSERT_THROW(Except, ...) \ 347 do { \ 348 TEST_SET_CHECKPOINT(); \ 349 ::rapid_cxx_test::test_outcome m_f( \ 350 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 351 , "TEST_ASSERT_THROW(" #Except "," #__VA_ARGS__ ")", "" \ 352 ); \ 353 try { \ 354 (static_cast<void>(__VA_ARGS__)); \ 355 m_f.type = ::rapid_cxx_test::failure_type::assert; \ 356 } catch (Except const &) {} \ 357 ::rapid_cxx_test::get_reporter().report(m_f); \ 358 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 359 std::abort(); \ 360 } \ 361 } while (false) 362 # 363 364 #else // TEST_HAS_NO_EXCEPTIONS 365 366 # define TEST_ASSERT_NO_THROW(...) \ 367 do { \ 368 TEST_SET_CHECKPOINT(); \ 369 ::rapid_cxx_test::test_outcome m_f( \ 370 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 371 , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \ 372 ); \ 373 (static_cast<void>(__VA_ARGS__)); \ 374 ::rapid_cxx_test::get_reporter().report(m_f); \ 375 } while (false) 376 # 377 378 #define TEST_ASSERT_THROW(Except, ...) ((void)0) 379 380 #endif // TEST_HAS_NO_EXCEPTIONS 381 382 //////////////////////////////////////////////////////////////////////////////// 383 // 384 //////////////////////////////////////////////////////////////////////////////// 385 386 # define TEST_WARN_EQUAL_COLLECTIONS(...) \ 387 do { \ 388 TEST_SET_CHECKPOINT(); \ 389 ::rapid_cxx_test::test_outcome m_f( \ 390 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 391 , "TEST_WARN_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ 392 ); \ 393 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ 394 m_f.type = ::rapid_cxx_test::failure_type::warn; \ 395 } \ 396 ::rapid_cxx_test::get_reporter().report(m_f); \ 397 } while (false) 398 # 399 400 # define TEST_CHECK_EQUAL_COLLECTIONS(...) \ 401 do { \ 402 TEST_SET_CHECKPOINT(); \ 403 ::rapid_cxx_test::test_outcome m_f( \ 404 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 405 , "TEST_CHECK_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ 406 ); \ 407 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ 408 m_f.type = ::rapid_cxx_test::failure_type::check; \ 409 } \ 410 ::rapid_cxx_test::get_reporter().report(m_f); \ 411 } while (false) 412 # 413 414 # define TEST_REQUIRE_EQUAL_COLLECTIONS(...) \ 415 do { \ 416 TEST_SET_CHECKPOINT(); \ 417 ::rapid_cxx_test::test_outcome m_f( \ 418 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 419 , "TEST_REQUIRE_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ 420 ); \ 421 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ 422 m_f.type = ::rapid_cxx_test::failure_type::require; \ 423 } \ 424 ::rapid_cxx_test::get_reporter().report(m_f); \ 425 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 426 return; \ 427 } \ 428 } while (false) 429 # 430 431 # define TEST_ASSERT_EQUAL_COLLECTIONS(...) \ 432 do { \ 433 TEST_SET_CHECKPOINT(); \ 434 ::rapid_cxx_test::test_outcome m_f( \ 435 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 436 , "TEST_ASSERT_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ 437 ); \ 438 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ 439 m_f.type = ::rapid_cxx_test::failure_type::assert; \ 440 } \ 441 ::rapid_cxx_test::get_reporter().report(m_f); \ 442 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 443 ::std::abort(); \ 444 } \ 445 } while (false) 446 # 447 448 namespace rapid_cxx_test 449 { 450 typedef void (*invoker_t)(); 451 452 //////////////////////////////////////////////////////////////////////////// 453 struct test_case 454 { test_casetest_case455 test_case() 456 : file(""), func(""), line(0), invoke(NULL) 457 {} 458 test_casetest_case459 test_case(const char* file1, const char* func1, std::size_t line1, 460 invoker_t invoke1) 461 : file(file1), func(func1), line(line1), invoke(invoke1) 462 {} 463 464 const char *file; 465 const char *func; 466 std::size_t line; 467 invoker_t invoke; 468 }; 469 470 //////////////////////////////////////////////////////////////////////////// 471 struct failure_type 472 { 473 enum enum_type { 474 none, 475 unsupported, 476 warn, 477 check, 478 require, 479 assert, 480 uncaught_exception 481 }; 482 }; 483 484 typedef failure_type::enum_type failure_type_t; 485 486 //////////////////////////////////////////////////////////////////////////// 487 struct test_outcome 488 { test_outcometest_outcome489 test_outcome() 490 : type(failure_type::none), 491 file(""), func(""), line(0), 492 expression(""), message("") 493 {} 494 test_outcometest_outcome495 test_outcome(failure_type_t type1, const char* file1, const char* func1, 496 std::size_t line1, const char* expression1, 497 const char* message1) 498 : type(type1), file(file1), func(func1), line(line1), 499 expression(expression1), message(message1) 500 { 501 trim_func_string(); 502 } 503 504 failure_type_t type; 505 const char *file; 506 const char *func; 507 std::size_t line; 508 const char *expression; 509 const char *message; 510 511 private: trim_file_stringtest_outcome512 void trim_file_string() { 513 const char* f_start = file; 514 const char* prev_start = f_start; 515 const char* last_start = f_start; 516 char last; 517 while ((last = *f_start) != '\0') { 518 ++f_start; 519 if (last == '/' && *f_start) { 520 prev_start = last_start; 521 last_start = f_start; 522 } 523 } 524 file = prev_start; 525 } trim_func_stringtest_outcome526 void trim_func_string() { 527 const char* void_loc = ::strstr(func, "void "); 528 if (void_loc == func) { 529 func += strlen("void "); 530 } 531 const char* namespace_loc = ::strstr(func, "::"); 532 if (namespace_loc) { 533 func = namespace_loc + 2; 534 } 535 } 536 }; 537 538 //////////////////////////////////////////////////////////////////////////// 539 struct checkpoint 540 { 541 const char *file; 542 const char *func; 543 std::size_t line; 544 }; 545 546 namespace detail 547 { global_checkpoint()548 inline checkpoint & global_checkpoint() 549 { 550 static checkpoint cp = {"", "", 0}; 551 return cp; 552 } 553 } 554 555 //////////////////////////////////////////////////////////////////////////// set_checkpoint(const char * file,const char * func,std::size_t line)556 inline void set_checkpoint(const char* file, const char* func, std::size_t line) 557 { 558 checkpoint& cp = detail::global_checkpoint(); 559 cp.file = file; 560 cp.func = func; 561 cp.line = line; 562 } 563 564 //////////////////////////////////////////////////////////////////////////// get_checkpoint()565 inline checkpoint const & get_checkpoint() 566 { 567 return detail::global_checkpoint(); 568 } 569 570 //////////////////////////////////////////////////////////////////////////// 571 class test_suite 572 { 573 public: 574 typedef test_case const* iterator; 575 typedef iterator const_iterator; 576 577 public: test_suite(const char * xname)578 test_suite(const char *xname) 579 : m_name(xname), m_tests(), m_size(0) 580 { 581 assert(xname); 582 } 583 584 public: name()585 const char *name() const { return m_name; } 586 size()587 std::size_t size() const { return m_size; } 588 589 test_case const & operator[](std::size_t i) const 590 { 591 assert(i < m_size); 592 return m_tests[i]; 593 } 594 begin()595 const_iterator begin() const 596 { return m_tests; } 597 end()598 const_iterator end() const 599 { 600 return m_tests + m_size; 601 } 602 603 public: register_test(test_case tc)604 std::size_t register_test(test_case tc) 605 { 606 static std::size_t test_case_max = sizeof(m_tests) / sizeof(test_case); 607 assert(m_size < test_case_max); 608 m_tests[m_size] = tc; 609 return m_size++; 610 } 611 612 private: 613 test_suite(test_suite const &); 614 test_suite & operator=(test_suite const &); 615 616 private: 617 const char* m_name; 618 // Since fast compile times in a priority, we use simple containers 619 // with hard limits. 620 test_case m_tests[256]; 621 std::size_t m_size; 622 }; 623 624 //////////////////////////////////////////////////////////////////////////// 625 class registrar 626 { 627 public: registrar(test_suite & st,test_case tc)628 registrar(test_suite & st, test_case tc) 629 { 630 st.register_test(tc); 631 } 632 }; 633 634 //////////////////////////////////////////////////////////////////////////// 635 class test_reporter 636 { 637 public: test_reporter()638 test_reporter() 639 : m_testcases(0), m_testcase_failures(0), m_unsupported(0), 640 m_assertions(0), m_warning_failures(0), m_check_failures(0), 641 m_require_failures(0), m_uncaught_exceptions(0), m_failure() 642 { 643 } 644 test_case_begin()645 void test_case_begin() 646 { 647 ++m_testcases; 648 clear_failure(); 649 } 650 test_case_end()651 void test_case_end() 652 { 653 if (m_failure.type != failure_type::none 654 && m_failure.type != failure_type::unsupported) { 655 ++m_testcase_failures; 656 } 657 } 658 659 # if defined(__GNUC__) 660 # pragma GCC diagnostic push 661 # pragma GCC diagnostic ignored "-Wswitch-default" 662 # endif 663 // Each assertion and failure is reported through this function. report(test_outcome o)664 void report(test_outcome o) 665 { 666 ++m_assertions; 667 switch (o.type) 668 { 669 case failure_type::none: 670 break; 671 case failure_type::unsupported: 672 ++m_unsupported; 673 m_failure = o; 674 break; 675 case failure_type::warn: 676 ++m_warning_failures; 677 report_error(o); 678 break; 679 case failure_type::check: 680 ++m_check_failures; 681 report_error(o); 682 m_failure = o; 683 break; 684 case failure_type::require: 685 ++m_require_failures; 686 report_error(o); 687 m_failure = o; 688 break; 689 case failure_type::assert: 690 report_error(o); 691 break; 692 case failure_type::uncaught_exception: 693 ++m_uncaught_exceptions; 694 std::fprintf(stderr 695 , "Test case FAILED with uncaught exception:\n" 696 " last checkpoint near %s::%lu %s\n\n" 697 , o.file, o.line, o.func 698 ); 699 m_failure = o; 700 break; 701 } 702 } 703 # if defined(__GNUC__) 704 # pragma GCC diagnostic pop 705 # endif 706 current_failure()707 test_outcome current_failure() const 708 { 709 return m_failure; 710 } 711 clear_failure()712 void clear_failure() 713 { 714 m_failure.type = failure_type::none; 715 m_failure.file = ""; 716 m_failure.func = ""; 717 m_failure.line = 0; 718 m_failure.expression = ""; 719 m_failure.message = ""; 720 } 721 test_case_count()722 std::size_t test_case_count() const 723 { return m_testcases; } 724 test_case_failure_count()725 std::size_t test_case_failure_count() const 726 { return m_testcase_failures; } 727 unsupported_count()728 std::size_t unsupported_count() const 729 { return m_unsupported; } 730 assertion_count()731 std::size_t assertion_count() const 732 { return m_assertions; } 733 warning_failure_count()734 std::size_t warning_failure_count() const 735 { return m_warning_failures; } 736 check_failure_count()737 std::size_t check_failure_count() const 738 { return m_check_failures; } 739 require_failure_count()740 std::size_t require_failure_count() const 741 { return m_require_failures; } 742 failure_count()743 std::size_t failure_count() const 744 { return m_check_failures + m_require_failures + m_uncaught_exceptions; } 745 746 // Print a summary of what was run and the outcome. print_summary(const char * suitename)747 void print_summary(const char* suitename) const 748 { 749 FILE* out = failure_count() ? stderr : stdout; 750 std::size_t testcases_run = m_testcases - m_unsupported; 751 std::fprintf(out, "Summary for testsuite %s:\n", suitename); 752 std::fprintf(out, " %lu of %lu test cases passed.\n", testcases_run - m_testcase_failures, testcases_run); 753 std::fprintf(out, " %lu of %lu assertions passed.\n", m_assertions - (m_warning_failures + m_check_failures + m_require_failures), m_assertions); 754 std::fprintf(out, " %lu unsupported test case%s.\n", m_unsupported, (m_unsupported != 1 ? "s" : "")); 755 } 756 757 private: 758 test_reporter(test_reporter const &); 759 test_reporter const & operator=(test_reporter const &); 760 report_error(test_outcome o)761 void report_error(test_outcome o) const 762 { 763 std::fprintf(stderr, "In %s:%lu Assertion %s failed.\n in file: %s\n %s\n" 764 , o.func, o.line, o.expression, o.file, o.message ? o.message : "" 765 ); 766 } 767 768 private: 769 // counts of testcases, failed testcases, and unsupported testcases. 770 std::size_t m_testcases; 771 std::size_t m_testcase_failures; 772 std::size_t m_unsupported; 773 774 // counts of assertions and assertion failures. 775 std::size_t m_assertions; 776 std::size_t m_warning_failures; 777 std::size_t m_check_failures; 778 std::size_t m_require_failures; 779 std::size_t m_uncaught_exceptions; 780 781 // The last failure. This is cleared between testcases. 782 test_outcome m_failure; 783 }; 784 785 //////////////////////////////////////////////////////////////////////////// get_reporter()786 inline test_reporter & get_reporter() 787 { 788 static test_reporter o; 789 return o; 790 } 791 792 //////////////////////////////////////////////////////////////////////////// 793 class test_runner 794 { 795 public: test_runner(test_suite & ts)796 test_runner(test_suite & ts) 797 : m_ts(ts) 798 {} 799 800 public: run()801 int run() 802 { 803 // for each testcase 804 for (test_suite::const_iterator b = m_ts.begin(), e = m_ts.end(); 805 b != e; ++b) 806 { 807 test_case const& tc = *b; 808 set_checkpoint(tc.file, tc.func, tc.line); 809 get_reporter().test_case_begin(); 810 #ifndef TEST_HAS_NO_EXCEPTIONS 811 try { 812 #endif 813 tc.invoke(); 814 #ifndef TEST_HAS_NO_EXCEPTIONS 815 } catch (...) { 816 test_outcome o; 817 o.type = failure_type::uncaught_exception; 818 o.file = get_checkpoint().file; 819 o.func = get_checkpoint().func; 820 o.line = get_checkpoint().line; 821 o.expression = ""; 822 o.message = ""; 823 get_reporter().report(o); 824 } 825 #endif 826 get_reporter().test_case_end(); 827 } 828 auto exit_code = get_reporter().failure_count() ? EXIT_FAILURE : EXIT_SUCCESS; 829 if (exit_code == EXIT_FAILURE || get_reporter().unsupported_count()) 830 get_reporter().print_summary(m_ts.name()); 831 return exit_code; 832 } 833 834 private: 835 test_runner(test_runner const &); 836 test_runner operator=(test_runner const &); 837 838 test_suite & m_ts; 839 }; 840 841 namespace detail 842 { 843 template <class Iter1, class Iter2> check_equal_collections_impl(Iter1 start1,Iter1 const end1,Iter2 start2,Iter2 const end2)844 bool check_equal_collections_impl( 845 Iter1 start1, Iter1 const end1 846 , Iter2 start2, Iter2 const end2 847 ) 848 { 849 while (start1 != end1 && start2 != end2) { 850 if (*start1 != *start2) { 851 return false; 852 } 853 ++start1; ++start2; 854 } 855 return (start1 == end1 && start2 == end2); 856 } 857 } // namespace detail 858 859 } // namespace rapid_cxx_test 860 861 862 # if defined(__GNUC__) 863 # pragma GCC diagnostic pop 864 # endif 865 866 #endif /* RAPID_CXX_TEST_H */ 867