File indexing completed on 2025-01-18 09:29:23
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #ifndef BOOST_BEAST_UNIT_TEST_SUITE_HPP
0011 #define BOOST_BEAST_UNIT_TEST_SUITE_HPP
0012
0013 #include <boost/beast/_experimental/unit_test/runner.hpp>
0014 #include <boost/throw_exception.hpp>
0015 #include <ostream>
0016 #include <sstream>
0017 #include <string>
0018
0019 namespace boost {
0020 namespace beast {
0021 namespace unit_test {
0022
0023 namespace detail {
0024
0025 template<class String>
0026 std::string
0027 make_reason(String const& reason,
0028 char const* file, int line)
0029 {
0030 std::string s(reason);
0031 if(! s.empty())
0032 s.append(": ");
0033 char const* path = file + strlen(file);
0034 while(path != file)
0035 {
0036 #ifdef _MSC_VER
0037 if(path[-1] == '\\')
0038 #else
0039 if(path[-1] == '/')
0040 #endif
0041 break;
0042 --path;
0043 }
0044 s.append(path);
0045 s.append("(");
0046 s.append(std::to_string(line));
0047 s.append(")");
0048 return s;
0049 }
0050
0051 }
0052
0053 class thread;
0054
0055 enum abort_t
0056 {
0057 no_abort_on_fail,
0058 abort_on_fail
0059 };
0060
0061
0062
0063
0064
0065
0066
0067
0068 class suite
0069 {
0070 private:
0071 bool abort_ = false;
0072 bool aborted_ = false;
0073 runner* runner_ = nullptr;
0074
0075
0076
0077 struct abort_exception : public std::exception
0078 {
0079 char const*
0080 what() const noexcept override
0081 {
0082 return "test suite aborted";
0083 }
0084 };
0085
0086 template<class CharT, class Traits, class Allocator>
0087 class log_buf
0088 : public std::basic_stringbuf<CharT, Traits, Allocator>
0089 {
0090 suite& suite_;
0091
0092 public:
0093 explicit
0094 log_buf(suite& self)
0095 : suite_(self)
0096 {
0097 }
0098
0099 ~log_buf()
0100 {
0101 sync();
0102 }
0103
0104 int
0105 sync() override
0106 {
0107 auto const& s = this->str();
0108 if(s.size() > 0)
0109 suite_.runner_->log(s);
0110 this->str("");
0111 return 0;
0112 }
0113 };
0114
0115 template<
0116 class CharT,
0117 class Traits = std::char_traits<CharT>,
0118 class Allocator = std::allocator<CharT>
0119 >
0120 class log_os : public std::basic_ostream<CharT, Traits>
0121 {
0122 log_buf<CharT, Traits, Allocator> buf_;
0123
0124 public:
0125 explicit
0126 log_os(suite& self)
0127 : std::basic_ostream<CharT, Traits>(&buf_)
0128 , buf_(self)
0129 {
0130 }
0131 };
0132
0133 class scoped_testcase;
0134
0135 class testcase_t
0136 {
0137 suite& suite_;
0138 std::stringstream ss_;
0139
0140 public:
0141 explicit
0142 testcase_t(suite& self)
0143 : suite_(self)
0144 {
0145 }
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157 void
0158 operator()(std::string const& name,
0159 abort_t abort = no_abort_on_fail);
0160
0161 scoped_testcase
0162 operator()(abort_t abort);
0163
0164 template<class T>
0165 scoped_testcase
0166 operator<<(T const& t);
0167 };
0168
0169 public:
0170
0171
0172
0173
0174
0175 log_os<char> log;
0176
0177
0178 testcase_t testcase;
0179
0180
0181
0182
0183 static
0184 suite*
0185 this_suite()
0186 {
0187 return *p_this_suite();
0188 }
0189
0190 suite()
0191 : log(*this)
0192 , testcase(*this)
0193 {
0194 }
0195
0196 virtual ~suite() = default;
0197 suite(suite const&) = delete;
0198 suite& operator=(suite const&) = delete;
0199
0200
0201
0202
0203
0204
0205
0206
0207 template<class = void>
0208 void
0209 operator()(runner& r);
0210
0211
0212 template<class = void>
0213 void
0214 pass();
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225 template<class String>
0226 void
0227 fail(String const& reason, char const* file, int line);
0228
0229 template<class = void>
0230 void
0231 fail(std::string const& reason = "");
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251
0252 template<class Condition>
0253 bool
0254 expect(Condition const& shouldBeTrue)
0255 {
0256 return expect(shouldBeTrue, "");
0257 }
0258
0259 template<class Condition, class String>
0260 bool
0261 expect(Condition const& shouldBeTrue, String const& reason);
0262
0263 template<class Condition>
0264 bool
0265 expect(Condition const& shouldBeTrue,
0266 char const* file, int line)
0267 {
0268 return expect(shouldBeTrue, "", file, line);
0269 }
0270
0271 template<class Condition, class String>
0272 bool
0273 expect(Condition const& shouldBeTrue,
0274 String const& reason, char const* file, int line);
0275
0276
0277
0278
0279
0280
0281 template<class F, class String>
0282 bool
0283 except(F&& f, String const& reason);
0284 template<class F>
0285 bool
0286 except(F&& f)
0287 {
0288 return except(f, "");
0289 }
0290 template<class E, class F, class String>
0291 bool
0292 except(F&& f, String const& reason);
0293 template<class E, class F>
0294 bool
0295 except(F&& f)
0296 {
0297 return except<E>(f, "");
0298 }
0299 template<class F, class String>
0300 bool
0301 unexcept(F&& f, String const& reason);
0302 template<class F>
0303 bool
0304 unexcept(F&& f)
0305 {
0306 return unexcept(f, "");
0307 }
0308
0309
0310 std::string const&
0311 arg() const
0312 {
0313 return runner_->arg();
0314 }
0315
0316
0317
0318 template<class Condition, class String>
0319 bool
0320 unexpected(Condition shouldBeFalse,
0321 String const& reason);
0322
0323 template<class Condition>
0324 bool
0325 unexpected(Condition shouldBeFalse)
0326 {
0327 return unexpected(shouldBeFalse, "");
0328 }
0329
0330 private:
0331 friend class thread;
0332
0333 static
0334 suite**
0335 p_this_suite()
0336 {
0337 static suite* pts = nullptr;
0338 return &pts;
0339 }
0340
0341
0342 virtual
0343 void
0344 run() = 0;
0345
0346 void
0347 propagate_abort();
0348
0349 template<class = void>
0350 void
0351 run(runner& r);
0352 };
0353
0354
0355
0356
0357 class suite::scoped_testcase
0358 {
0359 private:
0360 suite& suite_;
0361 std::stringstream& ss_;
0362
0363 public:
0364 scoped_testcase& operator=(scoped_testcase const&) = delete;
0365
0366 ~scoped_testcase()
0367 {
0368 auto const& name = ss_.str();
0369 if(! name.empty())
0370 suite_.runner_->testcase(name);
0371 }
0372
0373 scoped_testcase(suite& self, std::stringstream& ss)
0374 : suite_(self)
0375 , ss_(ss)
0376 {
0377 ss_.clear();
0378 ss_.str({});
0379 }
0380
0381 template<class T>
0382 scoped_testcase(suite& self,
0383 std::stringstream& ss, T const& t)
0384 : suite_(self)
0385 , ss_(ss)
0386 {
0387 ss_.clear();
0388 ss_.str({});
0389 ss_ << t;
0390 }
0391
0392 template<class T>
0393 scoped_testcase&
0394 operator<<(T const& t)
0395 {
0396 ss_ << t;
0397 return *this;
0398 }
0399 };
0400
0401
0402
0403 inline
0404 void
0405 suite::testcase_t::operator()(
0406 std::string const& name, abort_t abort)
0407 {
0408 suite_.abort_ = abort == abort_on_fail;
0409 suite_.runner_->testcase(name);
0410 }
0411
0412 inline
0413 suite::scoped_testcase
0414 suite::testcase_t::operator()(abort_t abort)
0415 {
0416 suite_.abort_ = abort == abort_on_fail;
0417 return { suite_, ss_ };
0418 }
0419
0420 template<class T>
0421 inline
0422 suite::scoped_testcase
0423 suite::testcase_t::operator<<(T const& t)
0424 {
0425 return { suite_, ss_, t };
0426 }
0427
0428
0429
0430 template<class>
0431 void
0432 suite::
0433 operator()(runner& r)
0434 {
0435 *p_this_suite() = this;
0436 try
0437 {
0438 run(r);
0439 *p_this_suite() = nullptr;
0440 }
0441 catch(...)
0442 {
0443 *p_this_suite() = nullptr;
0444 throw;
0445 }
0446 }
0447
0448 template<class Condition, class String>
0449 bool
0450 suite::
0451 expect(
0452 Condition const& shouldBeTrue, String const& reason)
0453 {
0454 if(shouldBeTrue)
0455 {
0456 pass();
0457 return true;
0458 }
0459 fail(reason);
0460 return false;
0461 }
0462
0463 template<class Condition, class String>
0464 bool
0465 suite::
0466 expect(Condition const& shouldBeTrue,
0467 String const& reason, char const* file, int line)
0468 {
0469 if(shouldBeTrue)
0470 {
0471 pass();
0472 return true;
0473 }
0474 fail(detail::make_reason(reason, file, line));
0475 return false;
0476 }
0477
0478
0479
0480 template<class F, class String>
0481 bool
0482 suite::
0483 except(F&& f, String const& reason)
0484 {
0485 try
0486 {
0487 f();
0488 fail(reason);
0489 return false;
0490 }
0491 catch(...)
0492 {
0493 pass();
0494 }
0495 return true;
0496 }
0497
0498 template<class E, class F, class String>
0499 bool
0500 suite::
0501 except(F&& f, String const& reason)
0502 {
0503 try
0504 {
0505 f();
0506 fail(reason);
0507 return false;
0508 }
0509 catch(E const&)
0510 {
0511 pass();
0512 }
0513 return true;
0514 }
0515
0516 template<class F, class String>
0517 bool
0518 suite::
0519 unexcept(F&& f, String const& reason)
0520 {
0521 try
0522 {
0523 f();
0524 pass();
0525 return true;
0526 }
0527 catch(...)
0528 {
0529 fail(reason);
0530 }
0531 return false;
0532 }
0533
0534 template<class Condition, class String>
0535 bool
0536 suite::
0537 unexpected(
0538 Condition shouldBeFalse, String const& reason)
0539 {
0540 bool const b =
0541 static_cast<bool>(shouldBeFalse);
0542 if(! b)
0543 pass();
0544 else
0545 fail(reason);
0546 return ! b;
0547 }
0548
0549 template<class>
0550 void
0551 suite::
0552 pass()
0553 {
0554 propagate_abort();
0555 runner_->pass();
0556 }
0557
0558
0559 template<class>
0560 void
0561 suite::
0562 fail(std::string const& reason)
0563 {
0564 propagate_abort();
0565 runner_->fail(reason);
0566 if(abort_)
0567 {
0568 aborted_ = true;
0569 BOOST_THROW_EXCEPTION(abort_exception());
0570 }
0571 }
0572
0573 template<class String>
0574 void
0575 suite::
0576 fail(String const& reason, char const* file, int line)
0577 {
0578 fail(detail::make_reason(reason, file, line));
0579 }
0580
0581 inline
0582 void
0583 suite::
0584 propagate_abort()
0585 {
0586 if(abort_ && aborted_)
0587 BOOST_THROW_EXCEPTION(abort_exception());
0588 }
0589
0590 template<class>
0591 void
0592 suite::
0593 run(runner& r)
0594 {
0595 runner_ = &r;
0596
0597 try
0598 {
0599 run();
0600 }
0601 catch(abort_exception const&)
0602 {
0603
0604 }
0605 catch(std::exception const& e)
0606 {
0607 runner_->fail("unhandled exception: " +
0608 std::string(e.what()));
0609 }
0610 catch(...)
0611 {
0612 runner_->fail("unhandled exception");
0613 }
0614 }
0615
0616 #ifndef BEAST_PASS
0617 #define BEAST_PASS() ::boost::beast::unit_test::suite::this_suite()->pass()
0618 #endif
0619
0620 #ifndef BEAST_FAIL
0621 #define BEAST_FAIL() ::boost::beast::unit_test::suite::this_suite()->fail("", __FILE__, __LINE__)
0622 #endif
0623
0624 #ifndef BEAST_EXPECT
0625
0626
0627
0628
0629 #define BEAST_EXPECT(cond) ::boost::beast::unit_test::suite::this_suite()->expect(cond, __FILE__, __LINE__)
0630 #endif
0631
0632 #ifndef BEAST_EXPECTS
0633
0634
0635
0636
0637 #define BEAST_EXPECTS(cond, reason) ((cond) ? \
0638 (::boost::beast::unit_test::suite::this_suite()->pass(), true) : \
0639 (::boost::beast::unit_test::suite::this_suite()->fail((reason), __FILE__, __LINE__), false))
0640 #endif
0641
0642
0643
0644 #define BEAST_THROWS( EXPR, EXCEP ) \
0645 try { \
0646 EXPR; \
0647 BEAST_FAIL(); \
0648 } \
0649 catch(EXCEP const&) { \
0650 BEAST_PASS(); \
0651 } \
0652 catch(...) { \
0653 BEAST_FAIL(); \
0654 }
0655
0656
0657
0658 #define BEAST_NO_THROW( EXPR ) \
0659 try { \
0660 EXPR; \
0661 BEAST_PASS(); \
0662 } \
0663 catch(...) { \
0664 BEAST_FAIL(); \
0665 }
0666
0667 }
0668 }
0669 }
0670
0671
0672
0673
0674
0675 #define BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,manual) \
0676 static ::boost::beast::unit_test::detail::insert_suite <Class##_test> \
0677 Library ## Module ## Class ## _test_instance( \
0678 #Class, #Module, #Library, manual)
0679
0680
0681
0682
0683
0684
0685
0686
0687 #ifndef BEAST_DEFINE_TESTSUITE
0688
0689
0690
0691
0692
0693
0694 #ifndef BEAST_NO_UNIT_TEST_INLINE
0695 #define BEAST_NO_UNIT_TEST_INLINE 0
0696 #endif
0697
0698
0699
0700
0701
0702
0703
0704
0705
0706
0707
0708
0709
0710
0711
0712
0713
0714
0715
0716
0717
0718
0719
0720 #if BEAST_NO_UNIT_TEST_INLINE
0721 #define BEAST_DEFINE_TESTSUITE(Class,Module,Library)
0722
0723 #else
0724 #include <boost/beast/_experimental/unit_test/global_suites.hpp>
0725 #define BEAST_DEFINE_TESTSUITE(Library,Module,Class) \
0726 BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,false)
0727 #define BEAST_DEFINE_TESTSUITE_MANUAL(Library,Module,Class) \
0728 BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,true)
0729
0730 #endif
0731
0732 #endif
0733
0734
0735
0736 #endif