diff --git a/tests/test_buffer.cpp b/tests/test_buffer.cpp new file mode 100644 index 0000000..8461929 --- /dev/null +++ b/tests/test_buffer.cpp @@ -0,0 +1,158 @@ +#include "../lib/Buffer.hpp" +#include +#include + +void test_basic_operations() +{ + std::cout << "Testing basic buffer operations...\n"; + + reactor::Buffer buf; + assert(buf.readableBytes() == 0); + assert(buf.writableBytes() > 0); + + std::string data = "Hello World"; + buf.append(data); + assert(buf.readableBytes() == data.size()); + + std::string result = buf.read(5); + assert(result == "Hello"); + assert(buf.readableBytes() == 6); + + std::string remaining = buf.readAll(); + assert(remaining == " World"); + assert(buf.readableBytes() == 0); + + std::cout << "✓ Basic operations passed\n"; +} + +void test_network_byte_order() +{ + std::cout << "Testing network byte order operations...\n"; + + reactor::Buffer buf; + + uint8_t val8 = 0x42; + uint16_t val16 = 0x1234; + uint32_t val32 = 0x12345678; + uint64_t val64 = 0x123456789ABCDEF0; + + buf.appendInt8(val8); + buf.appendInt16(val16); + buf.appendInt32(val32); + buf.appendInt64(val64); + + assert(buf.readInt8() == val8); + assert(buf.readInt16() == val16); + assert(buf.readInt32() == val32); + assert(buf.readInt64() == val64); + + std::cout << "✓ Network byte order passed\n"; +} + +void test_prepend_operations() +{ + std::cout << "Testing prepend operations...\n"; + + reactor::Buffer buf; + buf.append("World"); + + std::string hello = "Hello "; + buf.prepend(hello.data(), hello.size()); + + std::string result = buf.readAll(); + assert(result == "Hello World"); + + reactor::Buffer buf2; + buf2.append("Test"); + buf2.prependInt32(42); + + assert(buf2.readInt32() == 42); + assert(buf2.read(4) == "Test"); + + std::cout << "✓ Prepend operations passed\n"; +} + +void test_buffer_growth() +{ + std::cout << "Testing buffer growth...\n"; + + reactor::Buffer buf(64); + std::string large_data(2048, 'X'); + + buf.append(large_data); + assert(buf.readableBytes() == large_data.size()); + + std::string result = buf.readAll(); + assert(result == large_data); + + std::cout << "✓ Buffer growth passed\n"; +} + +void test_buffer_compaction() +{ + std::cout << "Testing buffer compaction...\n"; + + reactor::Buffer buf(128); + + for (int i = 0; i < 10; ++i) { + buf.append("data"); + buf.read(2); + } + + assert(buf.readableBytes() == 20); + + buf.append("more data"); + assert(buf.readableBytes() == 29); + + std::cout << "✓ Buffer compaction passed\n"; +} + +void test_peek_operations() +{ + std::cout << "Testing peek operations...\n"; + + reactor::Buffer buf; + buf.appendInt32(0x12345678); + buf.appendInt16(0xABCD); + + assert(buf.peekInt32() == 0x12345678); + assert(buf.readableBytes() == 6); + + buf.readInt32(); + assert(buf.peekInt16() == 0xABCD); + assert(buf.readableBytes() == 2); + + std::cout << "✓ Peek operations passed\n"; +} + +void test_buffer_swap() +{ + std::cout << "Testing buffer swap...\n"; + + reactor::Buffer buf1, buf2; + buf1.append("Buffer1"); + buf2.append("Buffer2"); + + buf1.swap(buf2); + + assert(buf1.readAll() == "Buffer2"); + assert(buf2.readAll() == "Buffer1"); + + std::cout << "✓ Buffer swap passed\n"; +} + +int main() +{ + std::cout << "=== Buffer Tests ===\n"; + + test_basic_operations(); + test_network_byte_order(); + test_prepend_operations(); + test_buffer_growth(); + test_buffer_compaction(); + test_peek_operations(); + test_buffer_swap(); + + std::cout << "All buffer tests passed! ✓\n"; + return 0; +} diff --git a/tests/test_core.cpp b/tests/test_core.cpp new file mode 100644 index 0000000..4b69f93 --- /dev/null +++ b/tests/test_core.cpp @@ -0,0 +1,301 @@ +#include "../lib/Core.hpp" +#include +#include +#include +#include +#include +#include + +class TestEventLoop +{ +private: + std::unique_ptr loop_; + std::thread thread_; + +public: + TestEventLoop() + { + thread_ = std::thread([this]() { + loop_ = std::make_unique(); + loop_->loop(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + ~TestEventLoop() + { + if (loop_) { + loop_->quit(); + } + if (thread_.joinable()) { + thread_.join(); + } + } + + reactor::EventLoop* getLoop() { return loop_.get(); } +}; + +void test_timer_basic() +{ + std::cout << "Testing basic timer functionality...\n"; + + TestEventLoop test_loop; + auto loop = test_loop.getLoop(); + + bool timer_fired = false; + auto timer_id = loop->runAfter(reactor::Duration(50), [&timer_fired]() { + timer_fired = true; + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + assert(timer_fired); + + std::cout << "✓ Basic timer passed\n"; +} + +void test_timer_cancellation() +{ + std::cout << "Testing timer cancellation...\n"; + + TestEventLoop test_loop; + auto loop = test_loop.getLoop(); + + bool timer_fired = false; + auto timer_id = loop->runAfter(reactor::Duration(100), [&timer_fired]() { + timer_fired = true; + }); + + loop->cancel(timer_id); + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + assert(!timer_fired); + + std::cout << "✓ Timer cancellation passed\n"; +} + +void test_repeating_timer() +{ + std::cout << "Testing repeating timer...\n"; + + TestEventLoop test_loop; + auto loop = test_loop.getLoop(); + + int count = 0; + auto timer_id = loop->runEvery(reactor::Duration(20), [&count]() { + count++; + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(85)); + loop->cancel(timer_id); + + assert(count >= 3 && count <= 5); + std::cout << "✓ Repeating timer passed (count: " << count << ")\n"; +} + +void test_run_in_loop() +{ + std::cout << "Testing runInLoop functionality...\n"; + + TestEventLoop test_loop; + auto loop = test_loop.getLoop(); + + bool task_executed = false; + loop->runInLoop([&task_executed]() { + task_executed = true; + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + assert(task_executed); + + std::cout << "✓ runInLoop passed\n"; +} + +void test_queue_in_loop() +{ + std::cout << "Testing queueInLoop functionality...\n"; + + TestEventLoop test_loop; + auto loop = test_loop.getLoop(); + + std::vector execution_order; + + loop->queueInLoop([&execution_order]() { + execution_order.push_back(1); + }); + + loop->queueInLoop([&execution_order]() { + execution_order.push_back(2); + }); + + loop->runInLoop([&execution_order]() { + execution_order.push_back(3); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + assert(execution_order.size() == 3); + assert(execution_order[2] == 3); + + std::cout << "✓ queueInLoop passed\n"; +} + +void test_channel_basic() +{ + std::cout << "Testing basic channel functionality...\n"; + + TestEventLoop test_loop; + auto loop = test_loop.getLoop(); + + int event_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + assert(event_fd >= 0); + + auto channel = std::make_unique(loop, event_fd); + + bool read_callback_called = false; + channel->setReadCallback([&read_callback_called]() { + read_callback_called = true; + }); + + channel->enableReading(); + + uint64_t val = 1; + write(event_fd, &val, sizeof(val)); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + assert(read_callback_called); + + channel->disableAll(); + channel->remove(); + close(event_fd); + + std::cout << "✓ Basic channel passed\n"; +} + +void test_channel_write_events() +{ + std::cout << "Testing channel write events...\n"; + + TestEventLoop test_loop; + auto loop = test_loop.getLoop(); + + int event_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + assert(event_fd >= 0); + + auto channel = std::make_unique(loop, event_fd); + + bool write_callback_called = false; + channel->setWriteCallback([&write_callback_called]() { + write_callback_called = true; + }); + + channel->enableWriting(); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + assert(write_callback_called); + + channel->disableAll(); + channel->remove(); + close(event_fd); + + std::cout << "✓ Channel write events passed\n"; +} + +void test_multiple_timers() +{ + std::cout << "Testing multiple timers...\n"; + + TestEventLoop test_loop; + auto loop = test_loop.getLoop(); + + std::vector timer_states(5, false); + + for (int i = 0; i < 5; ++i) { + loop->runAfter(reactor::Duration(10 + i * 20), [&timer_states, i]() { + timer_states[i] = true; + }); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(120)); + + for (bool state : timer_states) { + assert(state); + } + + std::cout << "✓ Multiple timers passed\n"; +} + +void test_timer_precision() +{ + std::cout << "Testing timer precision...\n"; + + TestEventLoop test_loop; + auto loop = test_loop.getLoop(); + + auto start_time = std::chrono::steady_clock::now(); + bool timer_fired = false; + + loop->runAfter(reactor::Duration(100), [&timer_fired, start_time]() { + auto end_time = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast(end_time - start_time); + timer_fired = true; + + assert(duration.count() >= 90 && duration.count() <= 150); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + assert(timer_fired); + + std::cout << "✓ Timer precision passed\n"; +} + +void test_event_loop_thread_safety() +{ + std::cout << "Testing event loop thread safety...\n"; + + TestEventLoop test_loop; + auto loop = test_loop.getLoop(); + + std::atomic counter{0}; + constexpr int num_tasks = 100; + + std::vector threads; + for (int t = 0; t < 4; ++t) { + threads.emplace_back([loop, &counter, num_tasks]() { + for (int i = 0; i < num_tasks; ++i) { + loop->queueInLoop([&counter]() { + counter++; + }); + } + }); + } + + for (auto& thread : threads) { + thread.join(); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + assert(counter == 4 * num_tasks); + + std::cout << "✓ Event loop thread safety passed\n"; +} + +int main() +{ + std::cout << "=== Core Tests ===\n"; + + test_timer_basic(); + test_timer_cancellation(); + test_repeating_timer(); + test_run_in_loop(); + test_queue_in_loop(); + test_channel_basic(); + test_channel_write_events(); + test_multiple_timers(); + test_timer_precision(); + test_event_loop_thread_safety(); + + std::cout << "All core tests passed! ✓\n"; + return 0; +} diff --git a/tests/test_inet_address.cpp b/tests/test_inet_address.cpp new file mode 100644 index 0000000..9a9826a --- /dev/null +++ b/tests/test_inet_address.cpp @@ -0,0 +1,188 @@ +#include "../lib/InetAddress.hpp" +#include +#include +#include + +void test_ipv4_construction() +{ + std::cout << "Testing IPv4 address construction...\n"; + + reactor::InetAddress addr1(8080); + assert(addr1.port() == 8080); + assert(!addr1.isIpV6()); + assert(addr1.toIp() == "0.0.0.0"); + + reactor::InetAddress addr2(9000, false, true); + assert(addr2.port() == 9000); + assert(addr2.toIp() == "127.0.0.1"); + + reactor::InetAddress addr3("192.168.1.100", 3000); + assert(addr3.port() == 3000); + assert(addr3.toIp() == "192.168.1.100"); + + std::cout << "✓ IPv4 construction passed\n"; +} + +void test_ipv6_construction() +{ + std::cout << "Testing IPv6 address construction...\n"; + + reactor::InetAddress addr1(8080, true); + assert(addr1.port() == 8080); + assert(addr1.isIpV6()); + assert(addr1.toIp() == "::"); + + reactor::InetAddress addr2(9000, true, true); + assert(addr2.port() == 9000); + assert(addr2.toIp() == "::1"); + + reactor::InetAddress addr3("::1", 3000); + assert(addr3.port() == 3000); + assert(addr3.toIp() == "::1"); + assert(addr3.isIpV6()); + + std::cout << "✓ IPv6 construction passed\n"; +} + +void test_address_comparison() +{ + std::cout << "Testing address comparison...\n"; + + reactor::InetAddress addr1("127.0.0.1", 8080); + reactor::InetAddress addr2("127.0.0.1", 8080); + reactor::InetAddress addr3("127.0.0.1", 9000); + reactor::InetAddress addr4("192.168.1.1", 8080); + + assert(addr1 == addr2); + assert(addr1 != addr3); + assert(addr1 != addr4); + + reactor::InetAddress addr5("::1", 8080); + reactor::InetAddress addr6("::1", 8080); + assert(addr5 == addr6); + assert(addr1 != addr5); + + std::cout << "✓ Address comparison passed\n"; +} + +void test_address_ordering() +{ + std::cout << "Testing address ordering...\n"; + + reactor::InetAddress addr1("127.0.0.1", 8080); + reactor::InetAddress addr2("127.0.0.1", 9000); + reactor::InetAddress addr3("192.168.1.1", 8080); + reactor::InetAddress addr4("::1", 8080); + + std::cout << "addr1 < addr2: " << (addr1 < addr2) << "\n"; + std::cout << "addr1 < addr3: " << (addr1 < addr3) << "\n"; + std::cout << "addr1 < addr4: " << (addr1 < addr4) << "\n"; + + std::cout << "✓ Address ordering passed\n"; +} + +void test_string_conversion() +{ + std::cout << "Testing string conversion...\n"; + + reactor::InetAddress addr1("192.168.1.100", 8080); + assert(addr1.toIpPort() == "192.168.1.100:8080"); + + reactor::InetAddress addr2("::1", 8080); + assert(addr2.toIpPort() == "[::1]:8080"); + + assert(addr1.familyToString() == "IPv4"); + assert(addr2.familyToString() == "IPv6"); + + std::cout << "✓ String conversion passed\n"; +} + +void test_socket_address_conversion() +{ + std::cout << "Testing socket address conversion...\n"; + + reactor::InetAddress addr("127.0.0.1", 8080); + const sockaddr* sa = addr.getSockAddr(); + socklen_t len = addr.getSockLen(); + + assert(sa != nullptr); + assert(len == sizeof(sockaddr_in)); + + const sockaddr_in* sa4 = reinterpret_cast(sa); + assert(sa4->sin_family == AF_INET); + assert(ntohs(sa4->sin_port) == 8080); + + std::cout << "✓ Socket address conversion passed\n"; +} + +void test_hash_functionality() +{ + std::cout << "Testing hash functionality...\n"; + + std::unordered_set addr_set; + + addr_set.insert(reactor::InetAddress("127.0.0.1", 8080)); + addr_set.insert(reactor::InetAddress("127.0.0.1", 9000)); + addr_set.insert(reactor::InetAddress("192.168.1.1", 8080)); + addr_set.insert(reactor::InetAddress("::1", 8080)); + + assert(addr_set.size() == 4); + + addr_set.insert(reactor::InetAddress("127.0.0.1", 8080)); + assert(addr_set.size() == 4); + + std::cout << "✓ Hash functionality passed\n"; +} + +void test_hostname_resolution() +{ + std::cout << "Testing hostname resolution...\n"; + + reactor::InetAddress result; + bool success = reactor::InetAddress::resolve("localhost", result); + + if (success) { + std::cout << "Resolved localhost to: " << result.toIpPort() << "\n"; + assert(result.toIp() == "127.0.0.1"); + } + + bool failed = reactor::InetAddress::resolve("invalid.hostname.test", result); + assert(!failed); + + std::cout << "✓ Hostname resolution passed\n"; +} + +void test_edge_cases() +{ + std::cout << "Testing edge cases...\n"; + + reactor::InetAddress addr1(0); + assert(addr1.port() == 0); + + reactor::InetAddress addr2(65535); + assert(addr2.port() == 65535); + + reactor::InetAddress addr3("0.0.0.0", 0); + assert(addr3.toIp() == "0.0.0.0"); + assert(addr3.port() == 0); + + std::cout << "✓ Edge cases passed\n"; +} + +int main() +{ + std::cout << "=== InetAddress Tests ===\n"; + + test_ipv4_construction(); + test_ipv6_construction(); + test_address_comparison(); + test_address_ordering(); + test_string_conversion(); + test_socket_address_conversion(); + test_hash_functionality(); + test_hostname_resolution(); + test_edge_cases(); + + std::cout << "All InetAddress tests passed! ✓\n"; + return 0; +} diff --git a/tests/test_socket.cpp b/tests/test_socket.cpp new file mode 100644 index 0000000..3bb5c98 --- /dev/null +++ b/tests/test_socket.cpp @@ -0,0 +1,286 @@ +#include "../lib/Socket.hpp" +#include +#include +#include +#include +#include + +void test_socket_creation() +{ + std::cout << "Testing socket creation...\n"; + + auto tcp_socket = reactor::Socket::createTcp(); + assert(tcp_socket.fd() >= 0); + + auto tcp6_socket = reactor::Socket::createTcp(true); + assert(tcp6_socket.fd() >= 0); + + auto udp_socket = reactor::Socket::createUdp(); + assert(udp_socket.fd() >= 0); + + auto udp6_socket = reactor::Socket::createUdp(true); + assert(udp6_socket.fd() >= 0); + + std::cout << "✓ Socket creation passed\n"; +} + +void test_socket_move() +{ + std::cout << "Testing socket move semantics...\n"; + + auto socket1 = reactor::Socket::createTcp(); + int fd = socket1.fd(); + assert(fd >= 0); + + auto socket2 = std::move(socket1); + assert(socket2.fd() == fd); + assert(socket1.fd() == -1); + + reactor::Socket socket3 = reactor::Socket::createTcp(); + int fd3 = socket3.fd(); + socket3 = std::move(socket2); + assert(socket3.fd() == fd); + + std::cout << "✓ Socket move semantics passed\n"; +} + +void test_socket_bind_listen() +{ + std::cout << "Testing socket bind and listen...\n"; + + auto socket = reactor::Socket::createTcp(); + reactor::InetAddress addr(0); + + socket.setReuseAddr(true); + socket.bind(addr); + socket.listen(); + + auto local_addr = reactor::Socket::getLocalAddr(socket.fd()); + assert(local_addr.port() > 0); + + std::cout << "✓ Socket bind and listen passed\n"; +} + +void test_socket_options() +{ + std::cout << "Testing socket options...\n"; + + auto socket = reactor::Socket::createTcp(); + + socket.setReuseAddr(true); + socket.setReusePort(true); + socket.setTcpNoDelay(true); + socket.setKeepAlive(true); + socket.setTcpKeepAlive(7200, 30, 9); + socket.setRecvBuffer(65536); + socket.setSendBuffer(65536); + + std::cout << "✓ Socket options passed\n"; +} + +void test_socket_connection() +{ + std::cout << "Testing socket connection...\n"; + + auto server_socket = reactor::Socket::createTcp(); + reactor::InetAddress server_addr(0); + + server_socket.setReuseAddr(true); + server_socket.bind(server_addr); + server_socket.listen(); + + auto actual_addr = reactor::Socket::getLocalAddr(server_socket.fd()); + std::cout << "Server listening on: " << actual_addr.toIpPort() << "\n"; + + std::thread server_thread([&server_socket]() { + reactor::InetAddress peer_addr; + int client_fd = server_socket.accept(peer_addr); + if (client_fd >= 0) { + std::cout << "Server accepted connection from: " << peer_addr.toIpPort() << "\n"; + + char buffer[1024]; + ssize_t n = read(client_fd, buffer, sizeof(buffer)); + if (n > 0) { + write(client_fd, buffer, n); + } + close(client_fd); + } + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + auto client_socket = reactor::Socket::createTcp(); + reactor::InetAddress connect_addr("127.0.0.1", actual_addr.port()); + + int result = client_socket.connect(connect_addr); + if (result == 0 || errno == EINPROGRESS) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + const char* message = "Hello Server"; + ssize_t sent = client_socket.write(message, strlen(message)); + assert(sent > 0); + + char response[1024]; + ssize_t received = client_socket.read(response, sizeof(response)); + assert(received > 0); + assert(strncmp(message, response, sent) == 0); + } + + server_thread.join(); + std::cout << "✓ Socket connection passed\n"; +} + +void test_udp_socket() +{ + std::cout << "Testing UDP socket operations...\n"; + + auto server_socket = reactor::Socket::createUdp(); + reactor::InetAddress server_addr(0); + + server_socket.setReuseAddr(true); + server_socket.bind(server_addr); + + auto actual_addr = reactor::Socket::getLocalAddr(server_socket.fd()); + std::cout << "UDP server bound to: " << actual_addr.toIpPort() << "\n"; + + std::thread server_thread([&server_socket]() { + char buffer[1024]; + reactor::InetAddress client_addr; + ssize_t n = server_socket.recvFrom(buffer, sizeof(buffer), client_addr); + if (n > 0) { + server_socket.sendTo(buffer, n, client_addr); + } + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + auto client_socket = reactor::Socket::createUdp(); + reactor::InetAddress target_addr("127.0.0.1", actual_addr.port()); + + const char* message = "UDP Hello"; + ssize_t sent = client_socket.sendTo(message, strlen(message), target_addr); + assert(sent > 0); + + char response[1024]; + reactor::InetAddress from_addr; + ssize_t received = client_socket.recvFrom(response, sizeof(response), from_addr); + assert(received > 0); + assert(strncmp(message, response, sent) == 0); + + server_thread.join(); + std::cout << "✓ UDP socket operations passed\n"; +} + +void test_socket_shutdown() +{ + std::cout << "Testing socket shutdown...\n"; + + auto server_socket = reactor::Socket::createTcp(); + reactor::InetAddress server_addr(0); + + server_socket.setReuseAddr(true); + server_socket.bind(server_addr); + server_socket.listen(); + + auto actual_addr = reactor::Socket::getLocalAddr(server_socket.fd()); + + std::thread server_thread([&server_socket]() { + reactor::InetAddress peer_addr; + int client_fd = server_socket.accept(peer_addr); + if (client_fd >= 0) { + reactor::Socket client_sock(client_fd); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + client_sock.shutdownWrite(); + close(client_fd); + } + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + auto client_socket = reactor::Socket::createTcp(); + reactor::InetAddress connect_addr("127.0.0.1", actual_addr.port()); + + int result = client_socket.connect(connect_addr); + if (result == 0 || errno == EINPROGRESS) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + char buffer[1024]; + ssize_t n = client_socket.read(buffer, sizeof(buffer)); + assert(n == 0); + } + + server_thread.join(); + std::cout << "✓ Socket shutdown passed\n"; +} + +void test_socket_error_handling() +{ + std::cout << "Testing socket error handling...\n"; + + auto socket = reactor::Socket::createTcp(); + + int error = socket.getSocketError(); + assert(error == 0); + + reactor::InetAddress invalid_addr("192.0.2.1", 12345); + int result = socket.connect(invalid_addr); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + std::cout << "✓ Socket error handling passed\n"; +} + +void test_address_retrieval() +{ + std::cout << "Testing address retrieval...\n"; + + auto server_socket = reactor::Socket::createTcp(); + reactor::InetAddress server_addr("127.0.0.1", 0); + + server_socket.setReuseAddr(true); + server_socket.bind(server_addr); + server_socket.listen(); + + auto local_addr = reactor::Socket::getLocalAddr(server_socket.fd()); + assert(local_addr.toIp() == "127.0.0.1"); + assert(local_addr.port() > 0); + + std::cout << "✓ Address retrieval passed\n"; +} + +void test_self_connection_detection() +{ + std::cout << "Testing self connection detection...\n"; + + auto socket = reactor::Socket::createTcp(); + reactor::InetAddress addr("127.0.0.1", 0); + + socket.setReuseAddr(true); + socket.bind(addr); + + auto bound_addr = reactor::Socket::getLocalAddr(socket.fd()); + + bool is_self = socket.isSelfConnected(); + std::cout << "Self connected: " << is_self << "\n"; + + std::cout << "✓ Self connection detection passed\n"; +} + +int main() +{ + std::cout << "=== Socket Tests ===\n"; + + test_socket_creation(); + test_socket_move(); + test_socket_bind_listen(); + test_socket_options(); + test_socket_connection(); + test_udp_socket(); + test_socket_shutdown(); + test_socket_error_handling(); + test_address_retrieval(); + test_self_connection_detection(); + + std::cout << "All socket tests passed! ✓\n"; + return 0; +} diff --git a/tests/test_tcp_server.cpp b/tests/test_tcp_server.cpp new file mode 100644 index 0000000..e5ce312 --- /dev/null +++ b/tests/test_tcp_server.cpp @@ -0,0 +1,406 @@ +#include "../lib/TcpServer.hpp" +#include "../lib/TcpConnection.hpp" +#include +#include +#include +#include +#include +#include +#include + +class TestClient +{ +private: + reactor::Socket socket_; + +public: + TestClient() : socket_(reactor::Socket::createTcp()) {} + + bool connect(const reactor::InetAddress& addr) + { + int result = socket_.connect(addr); + if (result == 0 || errno == EINPROGRESS) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + return true; + } + return false; + } + + bool send(const std::string& data) + { + ssize_t sent = socket_.write(data.data(), data.size()); + return sent == static_cast(data.size()); + } + + std::string receive(size_t max_size = 1024) + { + char buffer[1024]; + ssize_t received = socket_.read(buffer, std::min(max_size, sizeof(buffer))); + if (received > 0) { + return std::string(buffer, received); + } + return ""; + } + + void close() + { + socket_.shutdownWrite(); + } +}; + +void test_tcp_server_basic() +{ + std::cout << "Testing basic TCP server...\n"; + + reactor::EventLoop loop; + reactor::InetAddress listen_addr(0); + reactor::TcpServer server(&loop, listen_addr, "TestServer"); + + std::atomic server_started{false}; + std::atomic connection_received{false}; + std::atomic message_received{false}; + + server.setConnectionCallback([&](const reactor::TcpConnectionPtr& conn) { + if (conn->connected()) { + connection_received = true; + std::cout << "New connection: " << conn->name() << "\n"; + } else { + std::cout << "Connection closed: " << conn->name() << "\n"; + } + }); + + server.setMessageCallback([&](const reactor::TcpConnectionPtr& conn, reactor::Buffer& buffer) { + std::string message = buffer.readAll(); + std::cout << "Received: " << message << "\n"; + message_received = true; + conn->send("Echo: " + message); + }); + + server.start(); + + std::thread server_thread([&loop, &server_started]() { + server_started = true; + loop.loop(); + }); + + while (!server_started) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + TestClient client; + bool connected = client.connect(reactor::InetAddress("127.0.0.1", listen_addr.port())); + assert(connected); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + assert(connection_received); + + assert(client.send("Hello Server")); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + assert(message_received); + + std::string response = client.receive(); + assert(response == "Echo: Hello Server"); + + client.close(); + loop.quit(); + server_thread.join(); + + std::cout << "✓ Basic TCP server passed\n"; +} + +void test_multiple_connections() +{ + std::cout << "Testing multiple connections...\n"; + + reactor::EventLoop loop; + reactor::InetAddress listen_addr(0); + reactor::TcpServer server(&loop, listen_addr, "MultiServer"); + + std::atomic connection_count{0}; + std::atomic message_count{0}; + + server.setConnectionCallback([&](const reactor::TcpConnectionPtr& conn) { + if (conn->connected()) { + connection_count++; + } else { + connection_count--; + } + }); + + server.setMessageCallback([&](const reactor::TcpConnectionPtr& conn, reactor::Buffer& buffer) { + std::string message = buffer.readAll(); + message_count++; + conn->send("Response: " + message); + }); + + server.start(); + + std::thread server_thread([&loop]() { + loop.loop(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + constexpr int num_clients = 5; + std::vector> clients; + + for (int i = 0; i < num_clients; ++i) { + auto client = std::make_unique(); + bool connected = client->connect(reactor::InetAddress("127.0.0.1", listen_addr.port())); + assert(connected); + clients.push_back(std::move(client)); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + assert(connection_count == num_clients); + + for (int i = 0; i < num_clients; ++i) { + std::string message = "Message " + std::to_string(i); + assert(clients[i]->send(message)); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + assert(message_count == num_clients); + + for (auto& client : clients) { + client->close(); + } + clients.clear(); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + assert(connection_count == 0); + + loop.quit(); + server_thread.join(); + + std::cout << "✓ Multiple connections passed\n"; +} + +void test_server_with_thread_pool() +{ + std::cout << "Testing server with thread pool...\n"; + + reactor::EventLoop loop; + reactor::InetAddress listen_addr(0); + reactor::TcpServer server(&loop, listen_addr, "ThreadPoolServer"); + + server.setThreadNum(2); + + std::atomic message_count{0}; + std::vector thread_ids; + std::mutex thread_ids_mutex; + + server.setMessageCallback([&](const reactor::TcpConnectionPtr& conn, reactor::Buffer& buffer) { + { + std::lock_guard lock(thread_ids_mutex); + thread_ids.push_back(std::this_thread::get_id()); + } + + std::string message = buffer.readAll(); + message_count++; + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + conn->send("Processed: " + message); + }); + + server.start(); + + std::thread server_thread([&loop]() { + loop.loop(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + + constexpr int num_clients = 4; + std::vector client_threads; + + for (int i = 0; i < num_clients; ++i) { + client_threads.emplace_back([&listen_addr, i]() { + TestClient client; + bool connected = client.connect(reactor::InetAddress("127.0.0.1", listen_addr.port())); + assert(connected); + + std::string message = "Client" + std::to_string(i); + assert(client.send(message)); + + std::string response = client.receive(); + assert(response == "Processed: " + message); + + client.close(); + }); + } + + for (auto& thread : client_threads) { + thread.join(); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + assert(message_count == num_clients); + + { + std::lock_guard lock(thread_ids_mutex); + std::set unique_threads(thread_ids.begin(), thread_ids.end()); + assert(unique_threads.size() >= 2); + } + + loop.quit(); + server_thread.join(); + + std::cout << "✓ Server with thread pool passed\n"; +} + +void test_connection_lifecycle() +{ + std::cout << "Testing connection lifecycle...\n"; + + reactor::EventLoop loop; + reactor::InetAddress listen_addr(0); + reactor::TcpServer server(&loop, listen_addr, "LifecycleServer"); + + std::atomic connected{false}; + std::atomic disconnected{false}; + + server.setConnectionCallback([&](const reactor::TcpConnectionPtr& conn) { + if (conn->connected()) { + connected = true; + conn->send("Welcome"); + } else { + disconnected = true; + } + }); + + server.setMessageCallback([](const reactor::TcpConnectionPtr& conn, reactor::Buffer& buffer) { + buffer.readAll(); + conn->shutdown(); + }); + + server.start(); + + std::thread server_thread([&loop]() { + loop.loop(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + TestClient client; + bool conn_result = client.connect(reactor::InetAddress("127.0.0.1", listen_addr.port())); + assert(conn_result); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + assert(connected); + + std::string welcome = client.receive(); + assert(welcome == "Welcome"); + + assert(client.send("Goodbye")); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + assert(disconnected); + + loop.quit(); + server_thread.join(); + + std::cout << "✓ Connection lifecycle passed\n"; +} + +void test_large_message_handling() +{ + std::cout << "Testing large message handling...\n"; + + reactor::EventLoop loop; + reactor::InetAddress listen_addr(0); + reactor::TcpServer server(&loop, listen_addr, "LargeMessageServer"); + + std::atomic large_message_received{false}; + + server.setMessageCallback([&](const reactor::TcpConnectionPtr& conn, reactor::Buffer& buffer) { + std::string message = buffer.readAll(); + if (message.size() > 1000) { + large_message_received = true; + } + conn->send("Received " + std::to_string(message.size()) + " bytes"); + }); + + server.start(); + + std::thread server_thread([&loop]() { + loop.loop(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + TestClient client; + bool connected = client.connect(reactor::InetAddress("127.0.0.1", listen_addr.port())); + assert(connected); + + std::string large_message(5000, 'X'); + assert(client.send(large_message)); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + assert(large_message_received); + + std::string response = client.receive(); + assert(response == "Received 5000 bytes"); + + client.close(); + loop.quit(); + server_thread.join(); + + std::cout << "✓ Large message handling passed\n"; +} + +void test_server_stats() +{ + std::cout << "Testing server statistics...\n"; + + reactor::EventLoop loop; + reactor::InetAddress listen_addr(0); + reactor::TcpServer server(&loop, listen_addr, "StatsServer"); + + server.start(); + + std::thread server_thread([&loop]() { + loop.loop(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + assert(server.numConnections() == 0); + + TestClient client1, client2; + assert(client1.connect(reactor::InetAddress("127.0.0.1", listen_addr.port()))); + assert(client2.connect(reactor::InetAddress("127.0.0.1", listen_addr.port()))); + + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + assert(server.numConnections() == 2); + + auto connections = server.getConnections(); + assert(connections.size() == 2); + + client1.close(); + client2.close(); + + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + assert(server.numConnections() == 0); + + loop.quit(); + server_thread.join(); + + std::cout << "✓ Server statistics passed\n"; +} + +int main() +{ + std::cout << "=== TCP Server Tests ===\n"; + + test_tcp_server_basic(); + test_multiple_connections(); + test_server_with_thread_pool(); + test_connection_lifecycle(); + test_large_message_handling(); + test_server_stats(); + + std::cout << "All TCP server tests passed! ✓\n"; + return 0; +} diff --git a/tests/test_threading.cpp b/tests/test_threading.cpp new file mode 100644 index 0000000..a66b2dc --- /dev/null +++ b/tests/test_threading.cpp @@ -0,0 +1,314 @@ +#include "../lib/EventLoopThread.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +void test_event_loop_thread_basic() +{ + std::cout << "Testing basic EventLoopThread...\n"; + + reactor::EventLoopThread loop_thread("TestThread"); + auto loop = loop_thread.getLoop(); + + assert(loop != nullptr); + assert(loop_thread.name() == "TestThread"); + + std::atomic task_executed{false}; + loop->runInLoop([&task_executed]() { + task_executed = true; + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + assert(task_executed); + + std::cout << "✓ Basic EventLoopThread passed\n"; +} + +void test_event_loop_thread_timer() +{ + std::cout << "Testing EventLoopThread timer...\n"; + + reactor::EventLoopThread loop_thread("TimerThread"); + auto loop = loop_thread.getLoop(); + + std::atomic timer_count{0}; + auto timer_id = loop->runEvery(reactor::Duration(20), [&timer_count]() { + timer_count++; + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(85)); + loop->cancel(timer_id); + + int final_count = timer_count; + assert(final_count >= 3 && final_count <= 5); + + std::cout << "✓ EventLoopThread timer passed (count: " << final_count << ")\n"; +} + +void test_multiple_event_loop_threads() +{ + std::cout << "Testing multiple EventLoopThreads...\n"; + + constexpr int num_threads = 3; + std::vector> threads; + std::vector*> counters; + + for (int i = 0; i < num_threads; ++i) { + std::string name = "Thread-" + std::to_string(i); + threads.push_back(std::make_unique(name)); + counters.push_back(new std::atomic{0}); + } + + for (int i = 0; i < num_threads; ++i) { + auto loop = threads[i]->getLoop(); + auto counter = counters[i]; + + for (int j = 0; j < 10; ++j) { + loop->queueInLoop([counter]() { + (*counter)++; + }); + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + for (int i = 0; i < num_threads; ++i) { + assert(*counters[i] == 10); + delete counters[i]; + } + + std::cout << "✓ Multiple EventLoopThreads passed\n"; +} + +void test_event_loop_thread_pool_basic() +{ + std::cout << "Testing basic EventLoopThreadPool...\n"; + + reactor::EventLoopThreadPool pool(3, "PoolThread"); + + assert(pool.size() == 3); + assert(pool.getBaseName() == "PoolThread"); + + auto loops = pool.getAllLoops(); + assert(loops.size() == 3); + + for (auto loop : loops) { + assert(loop != nullptr); + } + + std::cout << "✓ Basic EventLoopThreadPool passed\n"; +} + +void test_thread_pool_round_robin() +{ + std::cout << "Testing thread pool round robin...\n"; + + reactor::EventLoopThreadPool pool(3, "RoundRobin"); + + std::vector selected_loops; + for (int i = 0; i < 9; ++i) { + selected_loops.push_back(pool.getNextLoop()); + } + + for (int i = 0; i < 3; ++i) { + assert(selected_loops[i] == selected_loops[i + 3]); + assert(selected_loops[i] == selected_loops[i + 6]); + } + + std::cout << "✓ Thread pool round robin passed\n"; +} + +void test_thread_pool_task_distribution() +{ + std::cout << "Testing thread pool task distribution...\n"; + + reactor::EventLoopThreadPool pool(3, "TaskDist"); + + std::vector*> counters(3); + for (int i = 0; i < 3; ++i) { + counters[i] = new std::atomic{0}; + } + + std::map loop_to_index; + auto loops = pool.getAllLoops(); + for (int i = 0; i < 3; ++i) { + loop_to_index[loops[i]] = i; + } + + constexpr int tasks_per_loop = 10; + for (int i = 0; i < 3 * tasks_per_loop; ++i) { + auto loop = pool.getNextLoop(); + int index = loop_to_index[loop]; + + loop->queueInLoop([counter = counters[index]]() { + (*counter)++; + }); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + for (int i = 0; i < 3; ++i) { + assert(*counters[i] == tasks_per_loop); + delete counters[i]; + } + + std::cout << "✓ Thread pool task distribution passed\n"; +} + +void test_empty_thread_pool() +{ + std::cout << "Testing empty thread pool...\n"; + + reactor::EventLoopThreadPool pool(0, "EmptyPool"); + + assert(pool.size() == 0); + assert(pool.getNextLoop() == nullptr); + assert(pool.getAllLoops().empty()); + + std::cout << "✓ Empty thread pool passed\n"; +} + +void test_thread_pool_concurrent_access() +{ + std::cout << "Testing thread pool concurrent access...\n"; + + reactor::EventLoopThreadPool pool(4, "ConcurrentAccess"); + + std::atomic total_tasks{0}; + std::vector client_threads; + + for (int t = 0; t < 8; ++t) { + client_threads.emplace_back([&pool, &total_tasks]() { + for (int i = 0; i < 25; ++i) { + auto loop = pool.getNextLoop(); + assert(loop != nullptr); + + loop->queueInLoop([&total_tasks]() { + total_tasks++; + }); + } + }); + } + + for (auto& thread : client_threads) { + thread.join(); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + assert(total_tasks == 8 * 25); + + std::cout << "✓ Thread pool concurrent access passed\n"; +} + +void test_thread_pool_with_timers() +{ + std::cout << "Testing thread pool with timers...\n"; + + reactor::EventLoopThreadPool pool(2, "TimerPool"); + + std::atomic timer_count{0}; + std::vector timer_ids; + + auto loops = pool.getAllLoops(); + for (auto loop : loops) { + auto timer_id = loop->runEvery(reactor::Duration(30), [&timer_count]() { + timer_count++; + }); + timer_ids.push_back(timer_id); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + for (int i = 0; i < loops.size(); ++i) { + loops[i]->cancel(timer_ids[i]); + } + + int final_count = timer_count; + assert(final_count >= 4 && final_count <= 8); + + std::cout << "✓ Thread pool with timers passed (count: " << final_count << ")\n"; +} + +void test_thread_synchronization() +{ + std::cout << "Testing thread synchronization...\n"; + + reactor::EventLoopThread loop_thread("SyncThread"); + auto loop = loop_thread.getLoop(); + + std::vector results; + std::mutex results_mutex; + + std::vector> futures; + + for (int i = 0; i < 10; ++i) { + std::promise promise; + auto future = promise.get_future(); + + loop->queueInLoop([i, &results, &results_mutex, promise = std::move(promise)]() mutable { + { + std::lock_guard lock(results_mutex); + results.push_back(i); + } + promise.set_value(); + }); + + futures.push_back(std::move(future)); + } + + for (auto& future : futures) { + future.wait(); + } + + assert(results.size() == 10); + + std::cout << "✓ Thread synchronization passed\n"; +} + +void test_thread_pool_destruction() +{ + std::cout << "Testing thread pool destruction...\n"; + + std::atomic destructor_count{0}; + + { + reactor::EventLoopThreadPool pool(2, "DestroyTest"); + + auto loops = pool.getAllLoops(); + for (auto loop : loops) { + loop->queueInLoop([&destructor_count]() { + destructor_count++; + }); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + assert(destructor_count == 2); + std::cout << "✓ Thread pool destruction passed\n"; +} + +int main() +{ + std::cout << "=== Threading Tests ===\n"; + + test_event_loop_thread_basic(); + test_event_loop_thread_timer(); + test_multiple_event_loop_threads(); + test_event_loop_thread_pool_basic(); + test_thread_pool_round_robin(); + test_thread_pool_task_distribution(); + test_empty_thread_pool(); + test_thread_pool_concurrent_access(); + test_thread_pool_with_timers(); + test_thread_synchronization(); + test_thread_pool_destruction(); + + std::cout << "All threading tests passed! ✓\n"; + return 0; +} diff --git a/tests/test_utilities.cpp b/tests/test_utilities.cpp new file mode 100644 index 0000000..3965093 --- /dev/null +++ b/tests/test_utilities.cpp @@ -0,0 +1,249 @@ +#include "../lib/Utilities.hpp" +#include +#include +#include +#include +#include +#include + +void test_lock_free_queue() +{ + std::cout << "Testing lock-free queue...\n"; + + reactor::LockFreeQueue queue; + assert(queue.empty()); + + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + assert(!queue.empty()); + + int val; + assert(queue.dequeue(val) && val == 1); + assert(queue.dequeue(val) && val == 2); + assert(queue.dequeue(val) && val == 3); + assert(queue.empty()); + assert(!queue.dequeue(val)); + + std::cout << "✓ Lock-free queue basic operations passed\n"; +} + +void test_lock_free_queue_concurrent() +{ + std::cout << "Testing lock-free queue concurrency...\n"; + + reactor::LockFreeQueue queue; + constexpr int num_items = 1000; + constexpr int num_producers = 4; + constexpr int num_consumers = 2; + + std::vector producers; + std::vector consumers; + std::atomic consumed_count{0}; + std::atomic stop_consumers{false}; + + for (int p = 0; p < num_producers; ++p) { + producers.emplace_back([&queue, p, num_items]() { + for (int i = 0; i < num_items; ++i) { + queue.enqueue(p * num_items + i); + } + }); + } + + for (int c = 0; c < num_consumers; ++c) { + consumers.emplace_back([&queue, &consumed_count, &stop_consumers]() { + int val; + while (!stop_consumers) { + if (queue.dequeue(val)) { + consumed_count++; + } else { + std::this_thread::yield(); + } + } + }); + } + + for (auto& p : producers) { + p.join(); + } + + while (consumed_count < num_producers * num_items) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + stop_consumers = true; + for (auto& c : consumers) { + c.join(); + } + + assert(consumed_count == num_producers * num_items); + std::cout << "✓ Lock-free queue concurrency passed\n"; +} + +void test_object_pool() +{ + std::cout << "Testing object pool...\n"; + + struct TestObject + { + int value = 42; + TestObject() { std::cout << "TestObject constructed\n"; } + ~TestObject() { std::cout << "TestObject destructed\n"; } + }; + + auto pool = std::make_shared>(); + + { + auto obj1 = pool->getObject(); + auto obj2 = pool->getObject(); + assert(obj1->value == 42); + assert(obj2->value == 42); + assert(obj1.get() != obj2.get()); + } + + { + auto obj3 = pool->getObject(); + assert(obj3->value == 42); + } + + std::cout << "✓ Object pool passed\n"; +} + +void test_logger() +{ + std::cout << "Testing logger...\n"; + + reactor::Logger::setLevel(reactor::LogLevel::DEBUG); + + reactor::Logger(reactor::LogLevel::DEBUG) << "Debug message"; + reactor::Logger(reactor::LogLevel::INFO) << "Info message with number: " << 42; + reactor::Logger(reactor::LogLevel::WARN) << "Warning message"; + reactor::Logger(reactor::LogLevel::ERROR) << "Error message"; + + reactor::Logger::setLevel(reactor::LogLevel::ERROR); + reactor::Logger(reactor::LogLevel::DEBUG) << "This should not appear"; + reactor::Logger(reactor::LogLevel::INFO) << "This should not appear"; + reactor::Logger(reactor::LogLevel::ERROR) << "This should appear"; + + reactor::Logger::setLevel(reactor::LogLevel::DEBUG); + + std::cout << "✓ Logger passed\n"; +} + +void test_concurrent_task_queue() +{ + std::cout << "Testing concurrent task queue...\n"; + + reactor::ConcurrentTaskQueue queue(2, "TestQueue"); + assert(queue.getName() == "TestQueue"); + + std::atomic counter{0}; + std::promise all_done; + auto future = all_done.get_future(); + + for (int i = 0; i < 10; ++i) { + queue.runTaskInQueue([&counter, i, &all_done]() { + counter++; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + if (counter == 10) { + all_done.set_value(); + } + }); + } + + future.wait(); + assert(counter == 10); + + std::cout << "✓ Concurrent task queue passed\n"; +} + +void test_sync_task() +{ + std::cout << "Testing sync task execution...\n"; + + reactor::ConcurrentTaskQueue queue(1, "SyncTest"); + bool task_executed = false; + + queue.syncTaskInQueue([&task_executed]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + task_executed = true; + }); + + assert(task_executed); + std::cout << "✓ Sync task execution passed\n"; +} + +void test_utility_functions() +{ + std::cout << "Testing utility functions...\n"; + + std::size_t seed = 0; + reactor::hashCombine(seed, 42); + reactor::hashCombine(seed, std::string("test")); + assert(seed != 0); + + std::string text = "hello,world,test,data"; + auto parts = reactor::splitString(text, ","); + assert(parts.size() == 4); + assert(parts[0] == "hello"); + assert(parts[1] == "world"); + assert(parts[2] == "test"); + assert(parts[3] == "data"); + + auto single = reactor::splitString("single", ","); + assert(single.size() == 1); + assert(single[0] == "single"); + + std::cout << "✓ Utility functions passed\n"; +} + +void test_network_utilities() +{ + std::cout << "Testing network utilities...\n"; + + uint64_t original = 0x123456789ABCDEF0ULL; + uint64_t network = reactor::hton64(original); + uint64_t back = reactor::ntoh64(network); + + assert(back == original); + + std::cout << "✓ Network utilities passed\n"; +} + +void test_non_copyable() +{ + std::cout << "Testing NonCopyable...\n"; + + class TestClass : public reactor::NonCopyable + { + public: + int value = 42; + TestClass() = default; + TestClass(TestClass&& other) noexcept : value(other.value) { other.value = 0; } + }; + + TestClass obj1; + TestClass obj2 = std::move(obj1); + assert(obj2.value == 42); + assert(obj1.value == 0); + + std::cout << "✓ NonCopyable passed\n"; +} + +int main() +{ + std::cout << "=== Utilities Tests ===\n"; + + test_lock_free_queue(); + test_lock_free_queue_concurrent(); + test_object_pool(); + test_logger(); + test_concurrent_task_queue(); + test_sync_task(); + test_utility_functions(); + test_network_utilities(); + test_non_copyable(); + + std::cout << "All utilities tests passed! ✓\n"; + return 0; +}