added unit test

This commit is contained in:
2025-09-27 22:27:14 -05:00
parent 4f24a1bc1d
commit cf5b3a074e
3 changed files with 137 additions and 56 deletions

View File

@@ -22,6 +22,8 @@
#ifndef REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_ #ifndef REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_
#define 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" #include "utils/common.hpp"
namespace repertory::comm { namespace repertory::comm {
@@ -46,6 +48,14 @@ private:
boost::asio::ip::tcp::socket &sock; 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); void apply_common_socket_properties(boost::asio::ip::tcp::socket &sock);
[[nodiscard]] auto is_socket_still_alive(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::ip::tcp::socket &sock,
boost::asio::mutable_buffer buf, boost::asio::mutable_buffer buf,
std::chrono::milliseconds deadline); 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 } // namespace repertory::comm
#endif // REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_ #endif // REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_

View File

@@ -21,9 +21,6 @@
*/ */
#include "comm/packet/common.hpp" #include "comm/packet/common.hpp"
#include "events/event_system.hpp"
#include "events/types/packet_client_timeout.hpp"
namespace repertory::comm { namespace repertory::comm {
non_blocking_guard::non_blocking_guard(boost::asio::ip::tcp::socket &sock_) non_blocking_guard::non_blocking_guard(boost::asio::ip::tcp::socket &sock_)
: non_blocking(sock_.non_blocking()), sock(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)); 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( void connect_with_deadline(
boost::asio::io_context &io_ctx, boost::asio::ip::tcp::socket &sock, boost::asio::io_context &io_ctx, boost::asio::ip::tcp::socket &sock,
boost::asio::ip::basic_resolver<boost::asio::ip::tcp>::results_type boost::asio::ip::basic_resolver<boost::asio::ip::tcp>::results_type

View 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);
}