From cf5b3a074e6e2d02dff9ac78865b4fd4e05536b4 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 27 Sep 2025 22:27:14 -0500 Subject: [PATCH] added unit test --- .../include/comm/packet/common.hpp | 63 ++++++++++++++++ .../librepertory/src/comm/packet/common.cpp | 56 -------------- .../src/packet_comm_common_test.cpp | 74 +++++++++++++++++++ 3 files changed, 137 insertions(+), 56 deletions(-) create mode 100644 repertory/repertory_test/src/packet_comm_common_test.cpp diff --git a/repertory/librepertory/include/comm/packet/common.hpp b/repertory/librepertory/include/comm/packet/common.hpp index a0507969..3af28a53 100644 --- a/repertory/librepertory/include/comm/packet/common.hpp +++ b/repertory/librepertory/include/comm/packet/common.hpp @@ -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 +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 +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 done{false}; + std::atomic timed_out{false}; + boost::system::error_code err; + }; + auto state = std::make_shared(); + + 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( + 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_ diff --git a/repertory/librepertory/src/comm/packet/common.cpp b/repertory/librepertory/src/comm/packet/common.cpp index e39ebef7..e4ece9d9 100644 --- a/repertory/librepertory/src/comm/packet/common.cpp +++ b/repertory/librepertory/src/comm/packet/common.cpp @@ -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 -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 done{false}; - std::atomic timed_out{false}; - boost::system::error_code err; - }; - auto state = std::make_shared(); - - 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( - 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::results_type diff --git a/repertory/repertory_test/src/packet_comm_common_test.cpp b/repertory/repertory_test/src/packet_comm_common_test.cpp new file mode 100644 index 00000000..56509cdb --- /dev/null +++ b/repertory/repertory_test/src/packet_comm_common_test.cpp @@ -0,0 +1,74 @@ +/* + Copyright <2018-2025> + 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 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 completed{false}; + + auto operation = [&](auto &&handler) { + auto delayed = std::make_shared(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); +}