#1 - quicr module

This commit is contained in:
Martin Slachta
2026-07-22 17:34:44 +02:00
parent a04f0dc262
commit e6dd954ded
149 changed files with 3997 additions and 2126 deletions
@@ -0,0 +1,24 @@
project(tw_message_protocol_tests)
file(GLOB FILES
./*.cpp
)
add_executable(${PROJECT_NAME} ${FILES})
# tw::quicr is listed explicitly because CMake does not propagate the object
# files of an OBJECT library through another OBJECT library.
target_link_libraries(${PROJECT_NAME}
PRIVATE
tw::message_protocol
tw::quicr
Catch2::Catch2WithMain
tl::expected
)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
catch_discover_tests(${PROJECT_NAME})
@@ -0,0 +1,73 @@
#include "message_protocol/MessageDispatcher.hpp"
#include <catch2/catch_test_macros.hpp>
#include <array>
#include <string>
using namespace tw::msg;
namespace {
std::span<const std::byte> as_bytes(const std::array<std::byte, 2>& body) {
return { body.data(), body.size() };
}
}
TEST_CASE("Dispatch reaches the handler bound to the address", "[message_dispatcher]") {
MessageDispatcher dispatcher;
PeerId seen_peer = 0;
size_t seen_size = 0;
dispatcher.set_handler(7, [&](PeerId peer, std::span<const std::byte> body) {
seen_peer = peer;
seen_size = body.size();
});
std::array<std::byte, 2> body{};
REQUIRE(dispatcher.dispatch(99, 7, as_bytes(body)));
REQUIRE(seen_peer == 99);
REQUIRE(seen_size == 2);
}
TEST_CASE("Dispatch to an unbound address reports failure", "[message_dispatcher]") {
MessageDispatcher dispatcher;
std::array<std::byte, 2> body{};
REQUIRE_FALSE(dispatcher.dispatch(1, 7, as_bytes(body)));
}
TEST_CASE("Addresses are routed independently", "[message_dispatcher]") {
MessageDispatcher dispatcher;
std::string called;
dispatcher.set_handler(1, [&](PeerId, std::span<const std::byte>) { called = "first"; });
dispatcher.set_handler(2, [&](PeerId, std::span<const std::byte>) { called = "second"; });
std::array<std::byte, 2> body{};
dispatcher.dispatch(1, 2, as_bytes(body));
REQUIRE(called == "second");
dispatcher.dispatch(1, 1, as_bytes(body));
REQUIRE(called == "first");
}
TEST_CASE("Rebinding an address replaces the handler", "[message_dispatcher]") {
MessageDispatcher dispatcher;
std::string called;
dispatcher.set_handler(7, [&](PeerId, std::span<const std::byte>) { called = "first"; });
dispatcher.set_handler(7, [&](PeerId, std::span<const std::byte>) { called = "second"; });
std::array<std::byte, 2> body{};
dispatcher.dispatch(1, 7, as_bytes(body));
REQUIRE(called == "second");
}
@@ -0,0 +1,45 @@
#include "message_protocol/MessageHeader.hpp"
#include <catch2/catch_test_macros.hpp>
#include <array>
using namespace tw::msg;
TEST_CASE("Header survives a round trip", "[message_header]") {
std::array<std::byte, MessageHeader::SIZE> buffer{};
MessageHeader{ 42, 7 }.encode(buffer);
auto decoded = MessageHeader::decode(buffer);
REQUIRE(decoded.has_value());
REQUIRE(decoded->type == 42);
REQUIRE(decoded->seq == 7);
}
TEST_CASE("Header defaults to expecting no reply", "[message_header]") {
std::array<std::byte, MessageHeader::SIZE> buffer{};
MessageHeader{ 3 }.encode(buffer);
auto decoded = MessageHeader::decode(buffer);
REQUIRE(decoded.has_value());
REQUIRE(decoded->seq == MessageHeader::SEQ_NONE);
}
TEST_CASE("Decoding a message shorter than a header fails", "[message_header]") {
std::array<std::byte, MessageHeader::SIZE - 1> buffer{};
REQUIRE_FALSE(MessageHeader::decode(buffer).has_value());
}
TEST_CASE("Encoding only writes the header", "[message_header]") {
std::array<std::byte, MessageHeader::SIZE + 4> buffer{};
buffer[MessageHeader::SIZE] = std::byte{ 0xAB };
MessageHeader{ 1, 2 }.encode(buffer);
REQUIRE(buffer[MessageHeader::SIZE] == std::byte{ 0xAB });
}