#include #include #include #include "matador/net/reactor.hpp" #include "matador/net/handler.hpp" #include #include #include namespace { class MockHandler : public matador::net::handler { public: explicit MockHandler(int fd = 1) : fd_(fd) { reset_counters(); } void reset_counters() { read_count = 0; write_count = 0; timeout_count = 0; close_count = 0; } socket_type handle() const override { return fd_; } bool is_ready_read() const override { return ready_read_; } bool is_ready_write() const override { return ready_write_; } void on_input() override { ++read_count; } void on_output() override { ++write_count; } void on_timeout() override { ++timeout_count; } void close() override { ++close_count; } void set_ready_states(bool read, bool write) { ready_read_ = read; ready_write_ = write; } static std::atomic read_count; static std::atomic write_count; static std::atomic timeout_count; static std::atomic close_count; private: int fd_; bool ready_read_{false}; bool ready_write_{false}; }; std::atomic MockHandler::read_count{0}; std::atomic MockHandler::write_count{0}; std::atomic MockHandler::timeout_count{0}; std::atomic MockHandler::close_count{0}; class ReactorTestFixture { protected: void SetUp() { MockHandler::read_count = 0; MockHandler::write_count = 0; MockHandler::timeout_count = 0; MockHandler::close_count = 0; } matador::net::reactor reactor_; }; } // namespace SCENARIO_METHOD(ReactorTestFixture, "Reactor Handler Registration", "[reactor]") { GIVEN("A reactor and a handler") { auto handler = std::make_shared(); WHEN("Registering handler for read events") { reactor_.register_handler(handler, matador::net::event_type::READ_MASK); THEN("Handler should be registered") { auto fdsets = reactor_.fd_sets(); REQUIRE(fdsets.read_set().is_set(handler->handle())); REQUIRE_FALSE(fdsets.write_set().is_set(handler->handle())); } } WHEN("Registering handler for write events") { handler->set_ready_states(false, true); reactor_.register_handler(handler, matador::net::event_type::WRITE_MASK); THEN("Handler should be registered for write") { auto fdsets = reactor_.fd_sets(); REQUIRE(fdsets.write_set().is_set(handler->handle())); REQUIRE_FALSE(fdsets.read_set().is_set(handler->handle())); } } WHEN("Registering null handler") { THEN("Should throw exception") { REQUIRE_THROWS_AS( reactor_.register_handler(nullptr, matador::net::event_type::READ_MASK), std::invalid_argument ); } } } } SCENARIO_METHOD(ReactorTestFixture, "Reactor Handler Unregistration", "[reactor]") { GIVEN("A reactor with registered handler") { auto handler = std::make_shared(); reactor_.register_handler(handler, matador::event_type::READ_MASK); WHEN("Unregistering the handler") { reactor_.unregister_handler(handler, matador::event_type::READ_MASK); THEN("Handler should be unregistered") { auto fdsets = reactor_.fdsets(); REQUIRE_FALSE(fdsets.read_set().is_set(handler->handle())); REQUIRE(MockHandler::close_count == 1); } } } } SCENARIO_METHOD(ReactorTestFixture, "Reactor Timer Operations", "[reactor]") { GIVEN("A reactor and a handler") { auto handler = std::make_shared(); WHEN("Scheduling a timer") { reactor_.schedule_timer(handler, 1, 0); THEN("Timer should be scheduled") { std::this_thread::sleep_for(std::chrono::milliseconds(1500)); reactor_.handle_events(); REQUIRE(MockHandler::timeout_count == 1); } } WHEN("Scheduling a repeating timer") { reactor_.schedule_timer(handler, 1, 1); THEN("Timer should fire multiple times") { std::this_thread::sleep_for(std::chrono::milliseconds(3500)); reactor_.handle_events(); REQUIRE(MockHandler::timeout_count >= 2); } } WHEN("Cancelling a timer") { reactor_.schedule_timer(handler, 2, 0); reactor_.cancel_timer(handler); THEN("Timer should not fire") { std::this_thread::sleep_for(std::chrono::milliseconds(2500)); reactor_.handle_events(); REQUIRE(MockHandler::timeout_count == 0); } } } } SCENARIO_METHOD(ReactorTestFixture, "Reactor Event Handling", "[reactor]") { GIVEN("A reactor with registered handlers") { auto read_handler = std::make_shared(1); auto write_handler = std::make_shared(2); read_handler->set_ready_states(true, false); write_handler->set_ready_states(false, true); reactor_.register_handler(read_handler, matador::event_type::READ_MASK); reactor_.register_handler(write_handler, matador::event_type::WRITE_MASK); WHEN("Handling events") { std::thread reactor_thread([this]() { reactor_.run(); }); std::this_thread::sleep_for(std::chrono::milliseconds(100)); THEN("Events should be processed") { REQUIRE(MockHandler::read_count > 0); REQUIRE(MockHandler::write_count > 0); } reactor_.shutdown(); reactor_thread.join(); } } } SCENARIO_METHOD(ReactorTestFixture, "Reactor Shutdown Behavior", "[reactor]") { GIVEN("A running reactor") { auto handler = std::make_shared(); reactor_.register_handler(handler, matador::event_type::READ_MASK); std::thread reactor_thread([this]() { reactor_.run(); }); WHEN("Shutting down the reactor") { std::this_thread::sleep_for(std::chrono::milliseconds(100)); reactor_.shutdown(); reactor_thread.join(); THEN("Reactor should stop cleanly") { REQUIRE_FALSE(reactor_.is_running()); REQUIRE(MockHandler::close_count == 1); } } } } SCENARIO_METHOD(ReactorTestFixture, "Reactor Stress Test", "[reactor][stress]") { GIVEN("A reactor with multiple handlers") { std::vector> handlers; const int NUM_HANDLERS = 100; for (int i = 0; i < NUM_HANDLERS; ++i) { auto handler = std::make_shared(i + 1); handler->set_ready_states(true, true); handlers.push_back(handler); reactor_.register_handler(handler, matador::event_type::READ_MASK | matador::event_type::WRITE_MASK); } WHEN("Running under load") { std::thread reactor_thread([this]() { reactor_.run(); }); std::this_thread::sleep_for(std::chrono::seconds(2)); THEN("Should handle events without issues") { REQUIRE(MockHandler::read_count > 0); REQUIRE(MockHandler::write_count > 0); REQUIRE(reactor_.is_running()); } reactor_.shutdown(); reactor_thread.join(); THEN("Should clean up properly") { REQUIRE(MockHandler::close_count == NUM_HANDLERS); } } } } SCENARIO_METHOD(ReactorTestFixture, "Reactor Error Handling", "[reactor]") { GIVEN("A reactor with faulty handler") { class FaultyHandler : public MockHandler { void on_input() override { throw std::runtime_error("Simulated error"); } }; auto handler = std::make_shared(); handler->set_ready_states(true, false); WHEN("Handling events with error") { reactor_.register_handler(handler, matador::event_type::READ_MASK); THEN("Should handle error gracefully") { reactor_.handle_events(); REQUIRE(reactor_.get_statistics().errors_ > 0); } } } }