Tidy up bind_handler tests

This commit is contained in:
Vinnie Falco
2018-12-11 13:35:35 -08:00
parent 2afe929bb7
commit f0cbf8276e
2 changed files with 361 additions and 78 deletions

View File

@ -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:

View File

@ -11,15 +11,24 @@
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/defer.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/strand.hpp>
#include <boost/bind/placeholders.hpp>
#include <boost/core/exchange.hpp>
#include <memory>
#include <string>
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<std::size_t I>
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<class F, class Alloc>
void dispatch(F&& f, Alloc const& a)
{
s_.on_invoke();
ex_.dispatch(std::forward<F>(f), a);
}
template<class F, class Alloc>
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<class F, class Alloc>
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<int> 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<class F>
void
testHooks(net::io_context& ioc, F&& f)
{
invoked_ = false;
net::post(ioc, std::forward<F>(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<int>{}, 1)();
bind_handler(handler<int, std::string>{}, 1, "hello")();
bind_handler(handler<int>{}, ph::_1)(1);
bind_handler(handler<int, std::string>{}, 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<int> const sp =
std::make_shared<int>(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<int> const sp =
std::make_shared<int>(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));
}
}