added unit test
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
#ifndef REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_
|
||||
#define REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/types/packet_client_timeout.hpp"
|
||||
#include "utils/common.hpp"
|
||||
|
||||
namespace repertory::comm {
|
||||
@@ -46,6 +48,14 @@ private:
|
||||
boost::asio::ip::tcp::socket &sock;
|
||||
};
|
||||
|
||||
template <class op_t>
|
||||
inline void run_with_deadline(boost::asio::io_context &io_ctx,
|
||||
boost::asio::ip::tcp::socket &sock,
|
||||
op_t operation,
|
||||
std::chrono::milliseconds deadline,
|
||||
const std::string &event_name,
|
||||
const std::string &function_name);
|
||||
|
||||
void apply_common_socket_properties(boost::asio::ip::tcp::socket &sock);
|
||||
|
||||
[[nodiscard]] auto is_socket_still_alive(boost::asio::ip::tcp::socket &sock)
|
||||
@@ -66,6 +76,59 @@ void write_all_with_deadline(boost::asio::io_context &io_ctx,
|
||||
boost::asio::ip::tcp::socket &sock,
|
||||
boost::asio::mutable_buffer buf,
|
||||
std::chrono::milliseconds deadline);
|
||||
|
||||
template <class op_t>
|
||||
inline void run_with_deadline(boost::asio::io_context &io_ctx,
|
||||
boost::asio::ip::tcp::socket &sock,
|
||||
op_t operation,
|
||||
std::chrono::milliseconds deadline,
|
||||
const std::string &event_name,
|
||||
const std::string &function_name) {
|
||||
deadline = std::max(deadline, std::chrono::milliseconds{250});
|
||||
|
||||
struct request_state final {
|
||||
std::atomic<bool> done{false};
|
||||
std::atomic<bool> timed_out{false};
|
||||
boost::system::error_code err;
|
||||
};
|
||||
auto state = std::make_shared<request_state>();
|
||||
|
||||
boost::asio::steady_timer timer{io_ctx};
|
||||
timer.expires_after(deadline);
|
||||
|
||||
timer.async_wait([state, &sock](auto &&err) {
|
||||
if (not err && not state->done) {
|
||||
state->timed_out = true;
|
||||
boost::system::error_code ignored_ec;
|
||||
[[maybe_unused]] auto res = sock.cancel(ignored_ec);
|
||||
}
|
||||
});
|
||||
|
||||
operation([state](auto &&err) {
|
||||
state->err = err;
|
||||
state->done = true;
|
||||
});
|
||||
|
||||
io_ctx.restart();
|
||||
while (not state->done && not state->timed_out) {
|
||||
io_ctx.run_one();
|
||||
}
|
||||
|
||||
timer.cancel();
|
||||
|
||||
io_ctx.poll();
|
||||
|
||||
if (state->timed_out) {
|
||||
repertory::event_system::instance().raise<repertory::packet_client_timeout>(
|
||||
std::string(event_name), std::string(function_name));
|
||||
throw std::runtime_error(event_name + " timed-out");
|
||||
}
|
||||
|
||||
if (state->err) {
|
||||
throw std::runtime_error(event_name + " failed|err|" +
|
||||
state->err.message());
|
||||
}
|
||||
}
|
||||
} // namespace repertory::comm
|
||||
|
||||
#endif // REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_
|
||||
|
@@ -21,9 +21,6 @@
|
||||
*/
|
||||
#include "comm/packet/common.hpp"
|
||||
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/types/packet_client_timeout.hpp"
|
||||
|
||||
namespace repertory::comm {
|
||||
non_blocking_guard::non_blocking_guard(boost::asio::ip::tcp::socket &sock_)
|
||||
: non_blocking(sock_.non_blocking()), sock(sock_) {
|
||||
@@ -83,59 +80,6 @@ void apply_common_socket_properties(boost::asio::ip::tcp::socket &sock) {
|
||||
sock.set_option(boost::asio::socket_base::keep_alive(true));
|
||||
}
|
||||
|
||||
template <class op_t>
|
||||
static void run_with_deadline(boost::asio::io_context &io_ctx,
|
||||
boost::asio::ip::tcp::socket &sock,
|
||||
op_t &&operation,
|
||||
std::chrono::milliseconds deadline,
|
||||
const std::string &event_name,
|
||||
const std::string &function_name) {
|
||||
deadline = std::max(deadline, std::chrono::milliseconds{250});
|
||||
|
||||
struct request_state final {
|
||||
std::atomic<bool> done{false};
|
||||
std::atomic<bool> timed_out{false};
|
||||
boost::system::error_code err;
|
||||
};
|
||||
auto state = std::make_shared<request_state>();
|
||||
|
||||
boost::asio::steady_timer timer{io_ctx};
|
||||
timer.expires_after(deadline);
|
||||
|
||||
timer.async_wait([state, &sock](auto &&err) {
|
||||
if (not err && not state->done) {
|
||||
state->timed_out = true;
|
||||
boost::system::error_code ignored_ec;
|
||||
[[maybe_unused]] auto res = sock.cancel(ignored_ec);
|
||||
}
|
||||
});
|
||||
|
||||
operation([state](auto &&err) {
|
||||
state->err = err;
|
||||
state->done = true;
|
||||
});
|
||||
|
||||
io_ctx.restart();
|
||||
while (not state->done && not state->timed_out) {
|
||||
io_ctx.run_one();
|
||||
}
|
||||
|
||||
timer.cancel();
|
||||
|
||||
io_ctx.poll();
|
||||
|
||||
if (state->timed_out) {
|
||||
repertory::event_system::instance().raise<repertory::packet_client_timeout>(
|
||||
std::string(event_name), std::string(function_name));
|
||||
throw std::runtime_error(event_name + " timed-out");
|
||||
}
|
||||
|
||||
if (state->err) {
|
||||
throw std::runtime_error(event_name + " failed|err|" +
|
||||
state->err.message());
|
||||
}
|
||||
}
|
||||
|
||||
void connect_with_deadline(
|
||||
boost::asio::io_context &io_ctx, boost::asio::ip::tcp::socket &sock,
|
||||
boost::asio::ip::basic_resolver<boost::asio::ip::tcp>::results_type
|
||||
|
74
repertory/repertory_test/src/packet_comm_common_test.cpp
Normal file
74
repertory/repertory_test/src/packet_comm_common_test.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions: The above copyright
|
||||
notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS",
|
||||
WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "comm/packet/common.hpp"
|
||||
|
||||
using namespace repertory;
|
||||
using namespace repertory::comm;
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
TEST(packet_comm_common_test, operation_completes_prior_to_timeout) {
|
||||
boost::asio::io_context io_ctx;
|
||||
tcp::socket sock(io_ctx);
|
||||
|
||||
std::atomic<bool> completed{false};
|
||||
|
||||
auto operation = [&](auto &&handler) {
|
||||
boost::asio::post(io_ctx, [&completed, handler]() {
|
||||
completed = true;
|
||||
handler(boost::system::error_code{});
|
||||
});
|
||||
};
|
||||
|
||||
EXPECT_NO_THROW(run_with_deadline(io_ctx, sock, operation,
|
||||
std::chrono::milliseconds(300), "read",
|
||||
"packet_deadline_test"));
|
||||
|
||||
EXPECT_TRUE(completed);
|
||||
|
||||
io_ctx.poll();
|
||||
}
|
||||
|
||||
TEST(packet_comm_common_test, timeout_completes_prior_to_operation) {
|
||||
boost::asio::io_context io_ctx;
|
||||
tcp::socket sock(io_ctx);
|
||||
|
||||
std::atomic<bool> completed{false};
|
||||
|
||||
auto operation = [&](auto &&handler) {
|
||||
auto delayed = std::make_shared<boost::asio::steady_timer>(io_ctx);
|
||||
delayed->expires_after(std::chrono::milliseconds(500));
|
||||
delayed->async_wait([&completed, delayed, handler](auto &&) {
|
||||
completed = true;
|
||||
handler(boost::system::error_code{});
|
||||
});
|
||||
};
|
||||
|
||||
EXPECT_THROW(run_with_deadline(io_ctx, sock, operation,
|
||||
std::chrono::milliseconds(300), "read",
|
||||
"packet_deadline_test"),
|
||||
std::runtime_error);
|
||||
|
||||
for (std::uint8_t idx = 0; idx < 80U && not completed; ++idx) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
io_ctx.poll();
|
||||
}
|
||||
|
||||
EXPECT_TRUE(completed);
|
||||
}
|
Reference in New Issue
Block a user