#ifndef ASYNC_MQTT5_TEST_MESSAGE_EXCHANGE_HPP #define ASYNC_MQTT5_TEST_MESSAGE_EXCHANGE_HPP #include #include #include #include #include #include #include "test_common/delayed_op.hpp" namespace async_mqtt5::test { using error_code = boost::system::error_code; using time_stamp = std::chrono::time_point; using duration = time_stamp::duration; class msg_exchange; class broker_message; inline duration after(duration d) { return d; } using namespace std::chrono_literals; namespace detail { class stream_message { error_code _ec; duration _after { 0 }; std::vector _content; public: template stream_message(error_code ec, duration after, Args&& ...args) : _ec(ec), _after(after) { (_content.insert(_content.end(), args.begin(), args.end()), ...); } stream_message(const stream_message&) = delete; stream_message(stream_message&&) = default; template auto to_operation(const Executor& ex) { return delayed_op> { ex, _after, _ec, std::move(_content) }; } }; } // end namespace detail class client_message { msg_exchange* _owner; error_code _write_ec; duration _complete_after { 0 }; std::vector _expected_packets; std::vector _replies; public: template client_message(msg_exchange* owner, Args&&... args) : _owner(owner), _expected_packets({ std::forward(args)... }) {} client_message(const client_message&) = delete; client_message(client_message&&) = default; client_message& complete_with(error_code ec, duration af) { _write_ec = ec; _complete_after = af; return *this; } template client_message& reply_with(Args&& ...args) { return reply_with_dur(std::make_tuple(std::forward(args)...)); } template client_message& expect(Args&& ...args); template broker_message& send(Args&& ...args); template decltype(auto) write_completion(const Executor& ex) const { return delayed_op(ex, _complete_after, _write_ec); } template decltype(auto) pop_reply_ops(const Executor& ex) { std::vector>> ret; std::transform( _replies.begin(), _replies.end(), std::back_inserter(ret), [&ex](auto& r) { return r.to_operation(ex); } ); _replies.clear(); return ret; } decltype(auto) expected_packets() { return _expected_packets; } private: template client_message& reply_with_dur(const Tuple& t) { using indices = std::make_index_sequence - 1>; return reply_with_dur(t, indices {}); } template client_message& reply_with_dur(const Tuple& t, std::index_sequence) { return reply_with_impl( std::get - 1>(t), std::get(t)... ); } template < typename ...Args, std::enable_if_t< (std::is_same_v, std::string> && ...), bool > = true > client_message& reply_with_impl(duration af, Args&& ...args) { _replies.emplace_back( error_code {}, af, std::forward(args)... ); return *this; } client_message& reply_with_impl(duration af, error_code ec) { _replies.emplace_back(ec, af); return *this; } }; class broker_message { msg_exchange* _owner; detail::stream_message _message; public: template broker_message( msg_exchange* owner, error_code ec, duration af, Args&&... args ) : _owner(owner), _message(ec, af, std::forward(args) ...) {} broker_message(const broker_message&) = delete; broker_message(broker_message&&) = default; template client_message& expect(Args&& ...args); template broker_message& send(Args&& ...args); template decltype(auto) pop_send_op(const Executor& ex) { return _message.to_operation(ex); } }; class msg_exchange { std::deque _to_broker; std::vector _from_broker; public: template < typename ...Args, std::enable_if_t< (std::is_same_v, std::string> && ...), bool > = true > client_message& expect(Args&& ...args) { _to_broker.emplace_back(this, std::forward(args)...); return _to_broker.back(); } template broker_message& send(Args&& ...args) { return send_with_dur(std::make_tuple(std::forward(args)...)); } std::optional pop_reply_action() { if (_to_broker.empty()) return std::nullopt; auto rv = std::move(_to_broker.front()); _to_broker.pop_front(); return rv; } template auto pop_broker_ops(const Executor& ex) { std::vector>> ret; std::transform( _from_broker.begin(), _from_broker.end(), std::back_inserter(ret), [&ex](auto& s) { return s.pop_send_op(ex); } ); _from_broker.clear(); return ret; } private: template broker_message& send_with_dur(const Tuple& t) { using indices = std::make_index_sequence - 1>; return send_with_dur(t, indices {}); } template broker_message& send_with_dur(const Tuple& t, std::index_sequence) { return send_impl( std::get - 1>(t), std::get(t)... ); } template < typename ...Args, std::enable_if_t< (std::is_same_v, std::string> && ...), bool > = true > broker_message& send_impl(duration after, Args&& ...args) { _from_broker.emplace_back( this, error_code {}, after, std::forward(args)... ); return _from_broker.back(); } broker_message& send_impl(duration after, error_code ec) { _from_broker.emplace_back(this, ec, after); return _from_broker.back(); } }; template client_message& client_message::expect(Args&& ...args) { return _owner->expect(std::forward(args)...); } template broker_message& client_message::send(Args&& ...args) { return _owner->send(std::forward(args)...); } template client_message& broker_message::expect(Args&& ...args) { return _owner->expect(std::forward(args)...); } template broker_message& broker_message::send(Args&& ...args) { return _owner->send(std::forward(args)...); } } // end namespace async_mqtt5::test #endif // ASYNC_MQTT5_TEST_MESSAGE_EXCHANGE_HPP