diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a9e01ac..738474ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Version 198: * Tidy up experimental files * Tidy up core files * Fix bind_handler, bind_front_handler +* Improved handler bind wrapper tests API Changes: diff --git a/test/beast/core/bind_handler.cpp b/test/beast/core/bind_handler.cpp index d6f799f5..7a656937 100644 --- a/test/beast/core/bind_handler.cpp +++ b/test/beast/core/bind_handler.cpp @@ -11,15 +11,24 @@ #include #include -#include #include #include +#include +#include +#include +#include +#include #include +#include +#include +#include #include namespace boost { namespace beast { +//------------------------------------------------------------------------------ + class bind_handler_test : public unit_test::suite { public: @@ -49,53 +58,324 @@ public: void operator()() const{}; }; + // A move-only parameter + template + struct move_arg + { + move_arg() = default; + move_arg(move_arg&&) = default; + move_arg(move_arg const&) = delete; + void operator()() const + { + }; + }; + void callback(int v) { BEAST_EXPECT(v == 42); } + class test_executor + { + bind_handler_test& s_; + net::io_context::executor_type ex_; + + public: + test_executor( + test_executor const&) = default; + + test_executor( + bind_handler_test& s, + net::io_context& ioc) + : s_(s) + , ex_(ioc.get_executor()) + { + } + + bool operator==( + test_executor const& other) const noexcept + { + return ex_ == other.ex_; + } + + bool operator!=( + test_executor const& other) const noexcept + { + return ex_ != other.ex_; + } + + net::io_context& + context() const noexcept + { + return ex_.context(); + } + + void on_work_started() const noexcept + { + ex_.on_work_started(); + } + + void on_work_finished() const noexcept + { + ex_.on_work_finished(); + } + + template + void dispatch(F&& f, Alloc const& a) + { + s_.on_invoke(); + ex_.dispatch(std::forward(f), a); + } + + template + void post(F&& f, Alloc const& a) + { + // shouldn't be called since the enclosing + // networking wrapper only uses dispatch + s_.fail("unexpected post", __FILE__, __LINE__); + } + + template + void defer(F&& f, Alloc const& a) + { + // shouldn't be called since the enclosing + // networking wrapper only uses dispatch + s_.fail("unexpected defer", __FILE__, __LINE__); + } + }; + + class test_cb + { + bind_handler_test& s_; + bool fail_ = true; + + public: + test_cb(test_cb const&) = delete; + + test_cb(bind_handler_test& s) + : s_(s) + { + } + + test_cb(test_cb&& other) + : s_(other.s_) + , fail_(boost::exchange( + other.fail_, false)) + { + } + + ~test_cb() + { + if(fail_) + s_.fail("handler not invoked", + __FILE__, __LINE__); + } + + void + operator()() + { + fail_ = false; + s_.pass(); + } + + void + operator()(int v) + { + fail_ = false; + s_.expect( + v == 42, __FILE__, __LINE__); + } + + void + operator()(int v, string_view s) + { + fail_ = false; + s_.expect( + v == 42, __FILE__, __LINE__); + s_.expect( + s == "s", __FILE__, __LINE__); + } + + void + operator()(int v, string_view s, move_arg<1>) + { + fail_ = false; + s_.expect( + v == 42, __FILE__, __LINE__); + s_.expect( + s == "s", __FILE__, __LINE__); + } + + void + operator()(int v, string_view s, move_arg<1>, move_arg<2>) + { + fail_ = false; + s_.expect( + v == 42, __FILE__, __LINE__); + s_.expect( + s == "s", __FILE__, __LINE__); + } + + void + operator()(error_code, std::size_t n) + { + fail_ = false; + s_.expect( + n == 256, __FILE__, __LINE__); + } + + void + operator()( + error_code, std::size_t n, string_view s) + { + boost::ignore_unused(s); + fail_ = false; + s_.expect( + n == 256, __FILE__, __LINE__); + } + + void + operator()(std::shared_ptr const& sp) + { + fail_ = false; + s_.expect(sp.get() != nullptr, + __FILE__, __LINE__); + } + }; + #if 0 - // This function should fail to compile + // These functions should fail to compile + void failStdBind() { - std::bind(bind_handler(handler<>{})); + std::bind(bind_handler(test_cb{*this})); } + void failStdBindFront() { - std::bind(bind_front_handler(handler<>{})); + std::bind(bind_front_handler(test_cb{*this})); } #endif - + + //-------------------------------------------------------------------------- + + bool invoked_; + + void + on_invoke() + { + invoked_ = true; + } + + template + void + testHooks(net::io_context& ioc, F&& f) + { + invoked_ = false; + net::post(ioc, std::forward(f)); + ioc.run(); + ioc.restart(); + BEAST_EXPECT(invoked_); + } + + //-------------------------------------------------------------------------- + void testBindHandler() { - // invocation - { - auto f = bind_handler(std::bind( - &bind_handler_test::callback, this, - std::placeholders::_1), 42); - f(); + using m1 = move_arg<1>; + using m2 = move_arg<2>; + { + using namespace std::placeholders; + + // 0-ary + bind_handler(test_cb{*this})(); + + // 1-ary + bind_handler(test_cb{*this}, 42)(); + bind_handler(test_cb{*this}, _1)(42); + bind_handler(test_cb{*this}, _2)(0, 42); + + // 2-ary + bind_handler(test_cb{*this}, 42, "s")(); + bind_handler(test_cb{*this}, 42, "s")(0); + bind_handler(test_cb{*this}, _1, "s")(42); + bind_handler(test_cb{*this}, 42, _1) ("s"); + bind_handler(test_cb{*this}, _1, _2)(42, "s"); + bind_handler(test_cb{*this}, _1, _2)(42, "s", "X"); + bind_handler(test_cb{*this}, _2, _1)("s", 42); + bind_handler(test_cb{*this}, _3, _2)("X", "s", 42); + + // 3-ary + bind_handler(test_cb{*this}, 42, "s")(m1{}); + bind_handler(test_cb{*this}, 42, "s", _1)(m1{}); + bind_handler(test_cb{*this}, 42, _1, m1{})("s"); + + // 4-ary + bind_handler(test_cb{*this}, 42, "s")(m1{}, m2{}); + bind_handler(test_cb{*this}, 42, "s", m1{})(m2{}); + bind_handler(test_cb{*this}, 42, "s", m1{}, m2{})(); + bind_handler(test_cb{*this}, 42, _1, m1{})("s", m2{}); + bind_handler(test_cb{*this}, _3, _1, m1{})("s", m2{}, 42); } - // placeholders { - namespace ph = std::placeholders; - bind_handler(handler<>{})(); - bind_handler(handler{}, 1)(); - bind_handler(handler{}, 1, "hello")(); - bind_handler(handler{}, ph::_1)(1); - bind_handler(handler{}, ph::_1, ph::_2)(1, "hello"); + using namespace boost::placeholders; + + // 0-ary + bind_handler(test_cb{*this})(); + + // 1-ary + bind_handler(test_cb{*this}, 42)(); + bind_handler(test_cb{*this}, _1)(42); + bind_handler(test_cb{*this}, _2)(0, 42); + + // 2-ary + bind_handler(test_cb{*this}, 42, "s")(); + bind_handler(test_cb{*this}, 42, "s")(0); + bind_handler(test_cb{*this}, _1, "s")(42); + bind_handler(test_cb{*this}, 42, _1) ("s"); + bind_handler(test_cb{*this}, _1, _2)(42, "s"); + bind_handler(test_cb{*this}, _1, _2)(42, "s", "X"); + bind_handler(test_cb{*this}, _2, _1)("s", 42); + bind_handler(test_cb{*this}, _3, _2)("X", "s", 42); + + // 3-ary + bind_handler(test_cb{*this}, 42, "s")(m1{}); + bind_handler(test_cb{*this}, 42, "s", _1)(m1{}); + bind_handler(test_cb{*this}, 42, _1, m1{})("s"); + + // 4-ary + bind_handler(test_cb{*this}, 42, "s")(m1{}, m2{}); + bind_handler(test_cb{*this}, 42, "s", m1{})(m2{}); + bind_handler(test_cb{*this}, 42, "s", m1{}, m2{})(); + bind_handler(test_cb{*this}, 42, _1, m1{})("s", m2{}); + bind_handler(test_cb{*this}, _3, _1, m1{})("s", m2{}, 42); } - // move-only + // perfect forwarding { - bind_handler([](move_only){}, move_only{})(); - bind_handler([](move_only){}, std::placeholders::_1)(move_only{}); - bind_handler([](move_only, move_only){}, move_only{}, std::placeholders::_1)(move_only{}); + std::shared_ptr const sp = + std::make_shared(42); + { + bind_handler(test_cb{*this}, sp)(); + BEAST_EXPECT(sp.get() != nullptr); + } + { + bind_handler(test_cb{*this})(sp); + BEAST_EXPECT(sp.get() != nullptr); + } + } + + // associated executor + { + net::io_context ioc; + testHooks(ioc, bind_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}))); } // asio_handler_invoke @@ -107,80 +387,82 @@ public: net::strand< net::io_context::executor_type> s{ ioc.get_executor()}; - test::stream ts{ioc}; net::post(s, - bind_handler(copyable{}, 42)); + bind_handler(test_cb{*this}, 42)); + ioc.run(); } } void testBindFrontHandler() { - // invocation - { - bind_front_handler( - std::bind( - &bind_handler_test::callback, - this, - 42)); + using m1 = move_arg<1>; + using m2 = move_arg<2>; - bind_front_handler( - std::bind( - &bind_handler_test::callback, - this, - std::placeholders::_1), 42); + // 0-ary + bind_front_handler(test_cb{*this})(); - bind_front_handler( - std::bind( - &bind_handler_test::callback, - this, - std::placeholders::_1))(42); + // 1-ary + bind_front_handler(test_cb{*this}, 42)(); + bind_front_handler(test_cb{*this})(42); - bind_front_handler( - bind_front_handler( - std::bind( - &bind_handler_test::callback, - this, - std::placeholders::_1)), - 42); + // 2-ary + bind_front_handler(test_cb{*this}, 42, "s")(); + bind_front_handler(test_cb{*this}, 42)("s"); + bind_front_handler(test_cb{*this})(42, "s"); - bind_front_handler( - bind_front_handler( - std::bind( - &bind_handler_test::callback, - this, - std::placeholders::_1)))(42); - } + // 3-ary + bind_front_handler(test_cb{*this}, 42, "s", m1{})(); + bind_front_handler(test_cb{*this}, 42, "s")(m1{}); + bind_front_handler(test_cb{*this}, 42)("s", m1{}); + bind_front_handler(test_cb{*this})(42, "s", m1{}); - // move-only - { - bind_front_handler([]{}); - } + // 4-ary + bind_front_handler(test_cb{*this}, 42, "s", m1{}, m2{})(); + bind_front_handler(test_cb{*this}, 42, "s", m1{})(m2{}); + bind_front_handler(test_cb{*this}, 42, "s")(m1{}, m2{}); + bind_front_handler(test_cb{*this}, 42)("s", m1{}, m2{}); + bind_front_handler(test_cb{*this})(42, "s", m1{}, m2{}); + + error_code ec; + std::size_t n = 256; - // specializations - { - bind_front_handler(copyable{}); - bind_front_handler(copyable{}, 1); - bind_front_handler(copyable{}, 1, 2); - bind_front_handler(copyable{}, 1, 2, 3); - bind_front_handler(copyable{}, 1, 2, 3, 4); + // void(error_code, size_t) + bind_front_handler(test_cb{*this}, ec, n)(); - bind_front_handler(copyable{}, - error_code{}, std::size_t(4)); + // void(error_code, size_t)(string_view) + bind_front_handler(test_cb{*this}, ec, n)("s"); + + // perfect forwarding + { + std::shared_ptr const sp = + std::make_shared(42); + bind_front_handler(test_cb{*this}, sp)(); + BEAST_EXPECT(sp.get() != nullptr); } - // asio_handler_invoke + // associated executor { - // make sure things compile, also can set a - // breakpoint in asio_handler_invoke to make sure - // it is instantiated. net::io_context ioc; - net::strand< - net::io_context::executor_type> s{ - ioc.get_executor()}; - test::stream ts{ioc}; - net::post(s, - bind_front_handler(copyable{}, 42)); + + testHooks(ioc, bind_front_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}) + )); + testHooks(ioc, bind_front_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}), + 42)); + testHooks(ioc, bind_front_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}), + 42, "s")); + testHooks(ioc, bind_front_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}), + 42, "s", m1{})); + testHooks(ioc, bind_front_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}), + 42, "s", m1{}, m2{})); + testHooks(ioc, bind_front_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}), + ec, n)); } }