1 #ifndef RAPID_CXX_TEST_HPP 2 #define RAPID_CXX_TEST_HPP 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 #else // TEST_HAS_NO_EXCEPTIONS 225 226 # define TEST_CHECK_NO_THROW(...) \ 227 do { \ 228 TEST_SET_CHECKPOINT(); \ 229 ::rapid_cxx_test::test_outcome m_f( \ 230 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 231 , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", "" \ 232 ); \ 233 (static_cast<void>(__VA_ARGS__)); \ 234 ::rapid_cxx_test::get_reporter().report(m_f); \ 235 } while (false) 236 # 237 238 #define TEST_CHECK_THROW(Except, ...) ((void)0) 239 240 #endif // TEST_HAS_NO_EXCEPTIONS 241 242 243 //////////////////////////////////////////////////////////////////////////////// 244 // TEST_REQUIRE_NO_THROW / TEST_REQUIRE_THROWs 245 //////////////////////////////////////////////////////////////////////////////// 246 #ifndef TEST_HAS_NO_EXCEPTIONS 247 248 # define TEST_REQUIRE_NO_THROW(...) \ 249 do { \ 250 TEST_SET_CHECKPOINT(); \ 251 ::rapid_cxx_test::test_outcome m_f( \ 252 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 253 , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \ 254 ); \ 255 try { \ 256 (static_cast<void>(__VA_ARGS__)); \ 257 } catch (...) { \ 258 m_f.type = ::rapid_cxx_test::failure_type::require; \ 259 } \ 260 ::rapid_cxx_test::get_reporter().report(m_f); \ 261 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 262 return; \ 263 } \ 264 } while (false) 265 # 266 267 # define TEST_REQUIRE_THROW(Except, ...) \ 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_THROW(" #Except "," #__VA_ARGS__ ")", "" \ 273 ); \ 274 try { \ 275 (static_cast<void>(__VA_ARGS__)); \ 276 m_f.type = ::rapid_cxx_test::failure_type::require; \ 277 } catch (Except const &) {} \ 278 ::rapid_cxx_test::get_reporter().report(m_f); \ 279 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 280 return; \ 281 } \ 282 } while (false) 283 # 284 285 #else // TEST_HAS_NO_EXCEPTIONS 286 287 # define TEST_REQUIRE_NO_THROW(...) \ 288 do { \ 289 TEST_SET_CHECKPOINT(); \ 290 ::rapid_cxx_test::test_outcome m_f( \ 291 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 292 , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \ 293 ); \ 294 (static_cast<void>(__VA_ARGS__)); \ 295 ::rapid_cxx_test::get_reporter().report(m_f); \ 296 } while (false) 297 # 298 299 #define TEST_REQUIRE_THROW(Except, ...) ((void)0) 300 301 #endif // TEST_HAS_NO_EXCEPTIONS 302 303 //////////////////////////////////////////////////////////////////////////////// 304 // TEST_ASSERT_NO_THROW / TEST_ASSERT_THROW 305 //////////////////////////////////////////////////////////////////////////////// 306 #ifndef TEST_HAS_NO_EXCEPTIONS 307 308 # define TEST_ASSERT_NO_THROW(...) \ 309 do { \ 310 TEST_SET_CHECKPOINT(); \ 311 ::rapid_cxx_test::test_outcome m_f( \ 312 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 313 , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \ 314 ); \ 315 try { \ 316 (static_cast<void>(__VA_ARGS__)); \ 317 } catch (...) { \ 318 m_f.type = ::rapid_cxx_test::failure_type::assert; \ 319 } \ 320 ::rapid_cxx_test::get_reporter().report(m_f); \ 321 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 322 std::abort(); \ 323 } \ 324 } while (false) 325 # 326 327 # define TEST_ASSERT_THROW(Except, ...) \ 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_THROW(" #Except "," #__VA_ARGS__ ")", "" \ 333 ); \ 334 try { \ 335 (static_cast<void>(__VA_ARGS__)); \ 336 m_f.type = ::rapid_cxx_test::failure_type::assert; \ 337 } catch (Except const &) {} \ 338 ::rapid_cxx_test::get_reporter().report(m_f); \ 339 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 340 std::abort(); \ 341 } \ 342 } while (false) 343 # 344 345 #else // TEST_HAS_NO_EXCEPTIONS 346 347 # define TEST_ASSERT_NO_THROW(...) \ 348 do { \ 349 TEST_SET_CHECKPOINT(); \ 350 ::rapid_cxx_test::test_outcome m_f( \ 351 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 352 , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \ 353 ); \ 354 (static_cast<void>(__VA_ARGS__)); \ 355 ::rapid_cxx_test::get_reporter().report(m_f); \ 356 } while (false) 357 # 358 359 #define TEST_ASSERT_THROW(Except, ...) ((void)0) 360 361 #endif // TEST_HAS_NO_EXCEPTIONS 362 363 //////////////////////////////////////////////////////////////////////////////// 364 // 365 //////////////////////////////////////////////////////////////////////////////// 366 367 # define TEST_WARN_EQUAL_COLLECTIONS(...) \ 368 do { \ 369 TEST_SET_CHECKPOINT(); \ 370 ::rapid_cxx_test::test_outcome m_f( \ 371 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 372 , "TEST_WARN_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ 373 ); \ 374 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ 375 m_f.type = ::rapid_cxx_test::failure_type::warn; \ 376 } \ 377 ::rapid_cxx_test::get_reporter().report(m_f); \ 378 } while (false) 379 # 380 381 # define TEST_CHECK_EQUAL_COLLECTIONS(...) \ 382 do { \ 383 TEST_SET_CHECKPOINT(); \ 384 ::rapid_cxx_test::test_outcome m_f( \ 385 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 386 , "TEST_CHECK_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ 387 ); \ 388 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ 389 m_f.type = ::rapid_cxx_test::failure_type::check; \ 390 } \ 391 ::rapid_cxx_test::get_reporter().report(m_f); \ 392 } while (false) 393 # 394 395 # define TEST_REQUIRE_EQUAL_COLLECTIONS(...) \ 396 do { \ 397 TEST_SET_CHECKPOINT(); \ 398 ::rapid_cxx_test::test_outcome m_f( \ 399 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 400 , "TEST_REQUIRE_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ 401 ); \ 402 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ 403 m_f.type = ::rapid_cxx_test::failure_type::require; \ 404 } \ 405 ::rapid_cxx_test::get_reporter().report(m_f); \ 406 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 407 return; \ 408 } \ 409 } while (false) 410 # 411 412 # define TEST_ASSERT_EQUAL_COLLECTIONS(...) \ 413 do { \ 414 TEST_SET_CHECKPOINT(); \ 415 ::rapid_cxx_test::test_outcome m_f( \ 416 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ 417 , "TEST_ASSERT_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \ 418 ); \ 419 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ 420 m_f.type = ::rapid_cxx_test::failure_type::assert; \ 421 } \ 422 ::rapid_cxx_test::get_reporter().report(m_f); \ 423 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \ 424 ::std::abort(); \ 425 } \ 426 } while (false) 427 # 428 429 namespace rapid_cxx_test 430 { 431 typedef void (*invoker_t)(); 432 433 //////////////////////////////////////////////////////////////////////////// 434 struct test_case 435 { test_caserapid_cxx_test::test_case436 test_case() 437 : file(""), func(""), line(0), invoke(NULL) 438 {} 439 test_caserapid_cxx_test::test_case440 test_case(const char* file1, const char* func1, std::size_t line1, 441 invoker_t invoke1) 442 : file(file1), func(func1), line(line1), invoke(invoke1) 443 {} 444 445 const char *file; 446 const char *func; 447 std::size_t line; 448 invoker_t invoke; 449 }; 450 451 //////////////////////////////////////////////////////////////////////////// 452 struct failure_type 453 { 454 enum enum_type { 455 none, 456 unsupported, 457 warn, 458 check, 459 require, 460 assert, 461 uncaught_exception 462 }; 463 }; 464 465 typedef failure_type::enum_type failure_type_t; 466 467 //////////////////////////////////////////////////////////////////////////// 468 struct test_outcome 469 { test_outcomerapid_cxx_test::test_outcome470 test_outcome() 471 : type(failure_type::none), 472 file(""), func(""), line(0), 473 expression(""), message("") 474 {} 475 test_outcomerapid_cxx_test::test_outcome476 test_outcome(failure_type_t type1, const char* file1, const char* func1, 477 std::size_t line1, const char* expression1, 478 const char* message1) 479 : type(type1), file(file1), func(func1), line(line1), 480 expression(expression1), message(message1) 481 { 482 trim_func_string(); 483 } 484 485 failure_type_t type; 486 const char *file; 487 const char *func; 488 std::size_t line; 489 const char *expression; 490 const char *message; 491 492 private: trim_file_stringrapid_cxx_test::test_outcome493 void trim_file_string() { 494 const char* f_start = file; 495 const char* prev_start = f_start; 496 const char* last_start = f_start; 497 char last; 498 while ((last = *f_start) != '\0') { 499 ++f_start; 500 if (last == '/' && *f_start) { 501 prev_start = last_start; 502 last_start = f_start; 503 } 504 } 505 file = prev_start; 506 } trim_func_stringrapid_cxx_test::test_outcome507 void trim_func_string() { 508 const char* void_loc = ::strstr(func, "void "); 509 if (void_loc == func) { 510 func += strlen("void "); 511 } 512 const char* namespace_loc = ::strstr(func, "::"); 513 if (namespace_loc) { 514 func = namespace_loc + 2; 515 } 516 } 517 }; 518 519 //////////////////////////////////////////////////////////////////////////// 520 struct checkpoint 521 { 522 const char *file; 523 const char *func; 524 std::size_t line; 525 }; 526 527 namespace detail 528 { global_checkpoint()529 inline checkpoint & global_checkpoint() 530 { 531 static checkpoint cp = {"", "", 0}; 532 return cp; 533 } 534 } 535 536 //////////////////////////////////////////////////////////////////////////// set_checkpoint(const char * file,const char * func,std::size_t line)537 inline void set_checkpoint(const char* file, const char* func, std::size_t line) 538 { 539 checkpoint& cp = detail::global_checkpoint(); 540 cp.file = file; 541 cp.func = func; 542 cp.line = line; 543 } 544 545 //////////////////////////////////////////////////////////////////////////// get_checkpoint()546 inline checkpoint const & get_checkpoint() 547 { 548 return detail::global_checkpoint(); 549 } 550 551 //////////////////////////////////////////////////////////////////////////// 552 class test_suite 553 { 554 public: 555 typedef test_case const* iterator; 556 typedef iterator const_iterator; 557 558 public: test_suite(const char * xname)559 test_suite(const char *xname) 560 : m_name(xname), m_tests(), m_size(0) 561 { 562 assert(xname); 563 } 564 565 public: name() const566 const char *name() const { return m_name; } 567 size() const568 std::size_t size() const { return m_size; } 569 operator [](std::size_t i) const570 test_case const & operator[](std::size_t i) const 571 { 572 assert(i < m_size); 573 return m_tests[i]; 574 } 575 begin() const576 const_iterator begin() const 577 { return m_tests; } 578 end() const579 const_iterator end() const 580 { 581 return m_tests + m_size; 582 } 583 584 public: register_test(test_case tc)585 std::size_t register_test(test_case tc) 586 { 587 static std::size_t test_case_max = sizeof(m_tests) / sizeof(test_case); 588 assert(m_size < test_case_max); 589 m_tests[m_size] = tc; 590 return m_size++; 591 } 592 593 private: 594 test_suite(test_suite const &); 595 test_suite & operator=(test_suite const &); 596 597 private: 598 const char* m_name; 599 // Since fast compile times in a priority, we use simple containers 600 // with hard limits. 601 test_case m_tests[256]; 602 std::size_t m_size; 603 }; 604 605 //////////////////////////////////////////////////////////////////////////// 606 class registrar 607 { 608 public: registrar(test_suite & st,test_case tc)609 registrar(test_suite & st, test_case tc) 610 { 611 st.register_test(tc); 612 } 613 }; 614 615 //////////////////////////////////////////////////////////////////////////// 616 class test_reporter 617 { 618 public: test_reporter()619 test_reporter() 620 : m_testcases(0), m_testcase_failures(0), m_unsupported(0), 621 m_assertions(0), m_warning_failures(0), m_check_failures(0), 622 m_require_failures(0), m_uncaught_exceptions(0), m_failure() 623 { 624 } 625 test_case_begin()626 void test_case_begin() 627 { 628 ++m_testcases; 629 clear_failure(); 630 } 631 test_case_end()632 void test_case_end() 633 { 634 if (m_failure.type != failure_type::none 635 && m_failure.type != failure_type::unsupported) { 636 ++m_testcase_failures; 637 } 638 } 639 640 # if defined(__GNUC__) 641 # pragma GCC diagnostic push 642 # pragma GCC diagnostic ignored "-Wswitch-default" 643 # endif 644 // Each assertion and failure is reported through this function. report(test_outcome o)645 void report(test_outcome o) 646 { 647 ++m_assertions; 648 switch (o.type) 649 { 650 case failure_type::none: 651 break; 652 case failure_type::unsupported: 653 ++m_unsupported; 654 m_failure = o; 655 break; 656 case failure_type::warn: 657 ++m_warning_failures; 658 report_error(o); 659 break; 660 case failure_type::check: 661 ++m_check_failures; 662 report_error(o); 663 m_failure = o; 664 break; 665 case failure_type::require: 666 ++m_require_failures; 667 report_error(o); 668 m_failure = o; 669 break; 670 case failure_type::assert: 671 report_error(o); 672 break; 673 case failure_type::uncaught_exception: 674 ++m_uncaught_exceptions; 675 std::fprintf(stderr 676 , "Test case FAILED with uncaught exception:\n" 677 " last checkpoint near %s::%lu %s\n\n" 678 , o.file, o.line, o.func 679 ); 680 m_failure = o; 681 break; 682 } 683 } 684 # if defined(__GNUC__) 685 # pragma GCC diagnostic pop 686 # endif 687 current_failure() const688 test_outcome current_failure() const 689 { 690 return m_failure; 691 } 692 clear_failure()693 void clear_failure() 694 { 695 m_failure.type = failure_type::none; 696 m_failure.file = ""; 697 m_failure.func = ""; 698 m_failure.line = 0; 699 m_failure.expression = ""; 700 m_failure.message = ""; 701 } 702 test_case_count() const703 std::size_t test_case_count() const 704 { return m_testcases; } 705 test_case_failure_count() const706 std::size_t test_case_failure_count() const 707 { return m_testcase_failures; } 708 unsupported_count() const709 std::size_t unsupported_count() const 710 { return m_unsupported; } 711 assertion_count() const712 std::size_t assertion_count() const 713 { return m_assertions; } 714 warning_failure_count() const715 std::size_t warning_failure_count() const 716 { return m_warning_failures; } 717 check_failure_count() const718 std::size_t check_failure_count() const 719 { return m_check_failures; } 720 require_failure_count() const721 std::size_t require_failure_count() const 722 { return m_require_failures; } 723 failure_count() const724 std::size_t failure_count() const 725 { return m_check_failures + m_require_failures + m_uncaught_exceptions; } 726 727 // Print a summary of what was run and the outcome. print_summary(const char * suitename) const728 void print_summary(const char* suitename) const 729 { 730 FILE* out = failure_count() ? stderr : stdout; 731 std::size_t testcases_run = m_testcases - m_unsupported; 732 std::fprintf(out, "Summary for testsuite %s:\n", suitename); 733 std::fprintf(out, " %lu of %lu test cases passed.\n", testcases_run - m_testcase_failures, testcases_run); 734 std::fprintf(out, " %lu of %lu assertions passed.\n", m_assertions - (m_warning_failures + m_check_failures + m_require_failures), m_assertions); 735 std::fprintf(out, " %lu unsupported test case%s.\n", m_unsupported, (m_unsupported != 1 ? "s" : "")); 736 } 737 738 private: 739 test_reporter(test_reporter const &); 740 test_reporter const & operator=(test_reporter const &); 741 report_error(test_outcome o) const742 void report_error(test_outcome o) const 743 { 744 std::fprintf(stderr, "In %s:%lu Assertion %s failed.\n in file: %s\n %s\n" 745 , o.func, o.line, o.expression, o.file, o.message ? o.message : "" 746 ); 747 } 748 749 private: 750 // counts of testcases, failed testcases, and unsupported testcases. 751 std::size_t m_testcases; 752 std::size_t m_testcase_failures; 753 std::size_t m_unsupported; 754 755 // counts of assertions and assertion failures. 756 std::size_t m_assertions; 757 std::size_t m_warning_failures; 758 std::size_t m_check_failures; 759 std::size_t m_require_failures; 760 std::size_t m_uncaught_exceptions; 761 762 // The last failure. This is cleared between testcases. 763 test_outcome m_failure; 764 }; 765 766 //////////////////////////////////////////////////////////////////////////// get_reporter()767 inline test_reporter & get_reporter() 768 { 769 static test_reporter o; 770 return o; 771 } 772 773 //////////////////////////////////////////////////////////////////////////// 774 class test_runner 775 { 776 public: test_runner(test_suite & ts)777 test_runner(test_suite & ts) 778 : m_ts(ts) 779 {} 780 781 public: run()782 int run() 783 { 784 // for each testcase 785 for (test_suite::const_iterator b = m_ts.begin(), e = m_ts.end(); 786 b != e; ++b) 787 { 788 test_case const& tc = *b; 789 set_checkpoint(tc.file, tc.func, tc.line); 790 get_reporter().test_case_begin(); 791 #ifndef TEST_HAS_NO_EXCEPTIONS 792 try { 793 #endif 794 tc.invoke(); 795 #ifndef TEST_HAS_NO_EXCEPTIONS 796 } catch (...) { 797 test_outcome o; 798 o.type = failure_type::uncaught_exception; 799 o.file = get_checkpoint().file; 800 o.func = get_checkpoint().func; 801 o.line = get_checkpoint().line; 802 o.expression = ""; 803 o.message = ""; 804 get_reporter().report(o); 805 } 806 #endif 807 get_reporter().test_case_end(); 808 } 809 auto exit_code = get_reporter().failure_count() ? EXIT_FAILURE : EXIT_SUCCESS; 810 if (exit_code == EXIT_FAILURE) 811 get_reporter().print_summary(m_ts.name()); 812 return exit_code; 813 } 814 815 private: 816 test_runner(test_runner const &); 817 test_runner operator=(test_runner const &); 818 819 test_suite & m_ts; 820 }; 821 822 namespace detail 823 { 824 template <class Iter1, class Iter2> check_equal_collections_impl(Iter1 start1,Iter1 const end1,Iter2 start2,Iter2 const end2)825 bool check_equal_collections_impl( 826 Iter1 start1, Iter1 const end1 827 , Iter2 start2, Iter2 const end2 828 ) 829 { 830 while (start1 != end1 && start2 != end2) { 831 if (*start1 != *start2) { 832 return false; 833 } 834 ++start1; ++start2; 835 } 836 return (start1 == end1 && start2 == end2); 837 } 838 } // namespace detail 839 840 } // namespace rapid_cxx_test 841 842 843 # if defined(__GNUC__) 844 # pragma GCC diagnostic pop 845 # endif 846 847 #endif /* RAPID_CXX_TEST_HPP */ 848