From 01090e8e44d8d64bb5839f8d842bdd3028447608 Mon Sep 17 00:00:00 2001 From: cirakg Date: Tue, 3 Nov 2026 01:08:49 +0100 Subject: tests: cover room ProxySocket receives and the LDN join handshake Add unit tests for the room LAN fixes in this branch. The socket tests check datagram truncation, TCP remainders and peeks, timeouts, wakeups on close and on a new packet, and stream behavior with the default protocol. The LDN tests check that a join waits for the host's reply, rejects bad or duplicate joins, ignores late replies and uses fresh identities. Assisted-by: Sol (LDN join tests) Assisted-by: Claude Opus 5.5 (room socket tests) --- src/tests/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/tests/CMakeLists.txt') diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 1eb1040e34..a31c073765 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -19,6 +19,8 @@ add_executable(tests common/undefined_fix.cpp common/unique_function.cpp core/core_timing.cpp + core/ldn.cpp + core/internal_network/socket_proxy.cpp core/nup_writer.cpp core/priority_queue.cpp core/internal_network/network.cpp -- cgit v1.2.3 From d8d16713b891245fd786b27d375f68d695c76d0e Mon Sep 17 00:00:00 2001 From: cirakg Date: Tue, 3 Nov 2026 01:39:55 +0100 Subject: tests: cover room packet matching and size-checked ZSTD decompression Assisted-by: Claude Opus 5.5 (investigation and implementation) --- src/tests/CMakeLists.txt | 1 + src/tests/common/zstd_compression.cpp | 59 ++++++++++++++++++++++++ src/tests/core/internal_network/socket_proxy.cpp | 37 +++++++++++++-- 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 src/tests/common/zstd_compression.cpp (limited to 'src/tests/CMakeLists.txt') diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index a31c073765..6bf14f4cbd 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(tests common/slot_vector.cpp common/undefined_fix.cpp common/unique_function.cpp + common/zstd_compression.cpp core/core_timing.cpp core/ldn.cpp core/internal_network/socket_proxy.cpp diff --git a/src/tests/common/zstd_compression.cpp b/src/tests/common/zstd_compression.cpp new file mode 100644 index 0000000000..6996f2e1ac --- /dev/null +++ b/src/tests/common/zstd_compression.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include + +#include "common/common_types.h" +#include "common/zstd_compression.h" + +namespace { + +std::vector Compress(const std::vector& data) { + return Common::Compression::CompressDataZSTDDefault(data.data(), data.size()); +} + +} // namespace + +TEST_CASE("ZSTD frames within the size limit decompress", "[common][zstd]") { + const std::vector data(100, 0x5a); + const auto compressed = Compress(data); + + const auto result = Common::Compression::TryDecompressDataZSTD(compressed, data.size()); + REQUIRE(result.has_value()); + REQUIRE(*result == data); + REQUIRE(Common::Compression::DecompressDataZSTD(compressed) == data); + + const auto empty = Common::Compression::TryDecompressDataZSTD(Compress({}), 0); + REQUIRE(empty.has_value()); + REQUIRE(empty->empty()); +} + +TEST_CASE("ZSTD frames over the size limit are rejected", "[common][zstd]") { + const std::vector data(100, 0x5a); + const auto compressed = Compress(data); + REQUIRE_FALSE(Common::Compression::TryDecompressDataZSTD(compressed, data.size() - 1)); +} + +TEST_CASE("Invalid ZSTD frames are rejected without throwing", "[common][zstd]") { + const std::array one_byte{0x01}; + const std::array bad_magic{0x01, 0x02, 0x03, 0x04, 0x05}; + REQUIRE_FALSE(Common::Compression::TryDecompressDataZSTD(one_byte, 0x10000)); + REQUIRE_FALSE(Common::Compression::TryDecompressDataZSTD(bad_magic, 0x10000)); + REQUIRE(Common::Compression::DecompressDataZSTD(one_byte).empty()); + + // The header declares the size, but the data after it is cut off + std::vector truncated = Compress(std::vector(100, 0x5a)); + truncated.pop_back(); + REQUIRE_FALSE(Common::Compression::TryDecompressDataZSTD(truncated, 0x10000)); +} + +TEST_CASE("ZSTD frames without a declared size are rejected", "[common][zstd]") { + // Magic number, a frame header without the content size, then one raw 3-byte last block + const std::array frame{0x28, 0xb5, 0x2f, 0xfd, 0x00, 0x00, + 0x19, 0x00, 0x00, 'a', 'b', 'c'}; + REQUIRE_FALSE(Common::Compression::TryDecompressDataZSTD(frame, 0x10000)); + REQUIRE(Common::Compression::DecompressDataZSTD(frame).empty()); +} diff --git a/src/tests/core/internal_network/socket_proxy.cpp b/src/tests/core/internal_network/socket_proxy.cpp index 0ba5f2aa2b..137de5b421 100644 --- a/src/tests/core/internal_network/socket_proxy.cpp +++ b/src/tests/core/internal_network/socket_proxy.cpp @@ -21,9 +21,7 @@ void BindSocket(Network::ProxySocket& socket, Network::Protocol protocol) { .portno = 35000}) == Network::Errno::SUCCESS); } -template -void QueuePacket(Network::ProxySocket& socket, Network::Protocol protocol, - const std::array& payload) { +Network::ProxyPacket MakePacket(Network::Protocol protocol) { Network::ProxyPacket packet{}; packet.local_endpoint = {.family = Network::Domain::INET, .ip = {192, 168, 1, 2}, @@ -32,6 +30,13 @@ void QueuePacket(Network::ProxySocket& socket, Network::Protocol protocol, .ip = {192, 168, 1, 1}, .portno = 35000}; packet.protocol = protocol; + return packet; +} + +template +void QueuePacket(Network::ProxySocket& socket, Network::Protocol protocol, + const std::array& payload) { + Network::ProxyPacket packet = MakePacket(protocol); packet.data.assign(payload.begin(), payload.end()); socket.HandleProxyPacket(packet); } @@ -228,3 +233,29 @@ TEST_CASE("One room packet wakes every blocked peek reader", "[core][sockets]") REQUIRE(finished[i] - queued < std::chrono::milliseconds{1000}); } } + +TEST_CASE("Room sockets accept only packets for their port and protocol", "[core][sockets]") { + Network::ProxySocket socket; + BindSocket(socket, Network::Protocol::UDP); + + REQUIRE(socket.AcceptsProxyPacket(MakePacket(Network::Protocol::UDP))); + REQUIRE_FALSE(socket.AcceptsProxyPacket(MakePacket(Network::Protocol::TCP))); + + Network::ProxyPacket other_port = MakePacket(Network::Protocol::UDP); + other_port.remote_endpoint.portno = 35001; + REQUIRE_FALSE(socket.AcceptsProxyPacket(other_port)); + + Network::ProxyPacket broadcast = MakePacket(Network::Protocol::UDP); + broadcast.broadcast = true; + REQUIRE_FALSE(socket.AcceptsProxyPacket(broadcast)); + REQUIRE(socket.SetBroadcast(true) == Network::Errno::SUCCESS); + REQUIRE(socket.AcceptsProxyPacket(broadcast)); + + REQUIRE(socket.Close() == Network::Errno::SUCCESS); + REQUIRE_FALSE(socket.AcceptsProxyPacket(MakePacket(Network::Protocol::UDP))); +} + +TEST_CASE("Native sockets accept no room packets", "[core][sockets]") { + Network::Socket socket; + REQUIRE_FALSE(socket.AcceptsProxyPacket(MakePacket(Network::Protocol::UDP))); +} -- cgit v1.2.3