blob: ccd8bddf62f6c79f0518fd14f7d45ed5434cfed5 [file]
// Copyright 2026 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_rpc2/internal/packet.h"
#include <algorithm>
#include <array>
#include <cstddef>
#include <string_view>
#include <utility>
#include "pw_allocator/testing.h"
#include "pw_assert/check.h"
#include "pw_bytes/array.h"
#include "pw_bytes/span.h"
#include "pw_enum/to_string.h"
#include "pw_enum/traits.h"
#include "pw_rpc2/internal/protocol_status.h"
#include "pw_status/status.h"
#include "pw_unit_test/framework.h"
namespace {
namespace internal = ::pw::rpc2::internal;
std::string_view AsString(pw::ConstByteSpan bytes) {
return std::string_view(reinterpret_cast<const char*>(bytes.data()),
bytes.size());
}
TEST(PacketTest, EncodeDecodeRequest) {
pw::allocator::test::AllocatorForTest<256> allocator;
constexpr std::string_view kPayload = "hello request";
size_t total_size = sizeof(internal::RequestWireFormat) + kPayload.size();
auto buf = pw::Buf::Allocate(allocator, total_size);
auto packet =
internal::OutboundPacket::Request(0x12345678, 0xabcdef01, 0x23456789);
pw::ConstByteSpan payload_bytes = pw::as_bytes(pw::span(kPayload));
std::copy(payload_bytes.begin(),
payload_bytes.end(),
buf.data() + packet.payload_offset());
auto encode_result = packet.Encode(std::move(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
pw::Buf encoded_buf = std::move(encode_result.value());
EXPECT_EQ(encoded_buf.size(), total_size);
auto decode_result =
internal::InboundPacket::Decode(pw::ConstBuf(std::move(encoded_buf)));
ASSERT_EQ(decode_result.status(), pw::OkStatus());
internal::InboundPacket decoded = std::move(decode_result.value());
EXPECT_EQ(decoded.type(), internal::PacketType::kRequest);
EXPECT_EQ(decoded.payload_offset(), sizeof(internal::RequestWireFormat));
EXPECT_EQ(decoded.call_id(), 0x12345678u);
EXPECT_EQ(decoded.service_id(), 0xabcdef01u);
EXPECT_EQ(decoded.method_id(), 0x23456789u);
EXPECT_EQ(decoded.payload().size(), kPayload.size());
EXPECT_EQ(AsString(decoded.payload()), kPayload);
pw::ConstBuf payload_buf = std::move(decoded).TakePayload();
EXPECT_EQ(payload_buf.size(), kPayload.size());
EXPECT_EQ(AsString(payload_buf), kPayload);
}
TEST(PacketTest, EncodeDecodeMessage) {
pw::allocator::test::AllocatorForTest<256> allocator;
constexpr std::string_view kPayload = "hello msg";
size_t total_size = sizeof(internal::MessageWireFormat) + kPayload.size();
for (auto [sender, expected_type] :
{std::pair{internal::EndpointRole::kClient,
internal::PacketType::kClientMessage},
std::pair{internal::EndpointRole::kServer,
internal::PacketType::kServerMessage}}) {
auto buf = pw::Buf::Allocate(allocator, total_size);
auto packet = internal::OutboundPacket::Message(sender, 0x12345678);
pw::ConstByteSpan payload_bytes = pw::as_bytes(pw::span(kPayload));
std::copy(payload_bytes.begin(),
payload_bytes.end(),
buf.data() + packet.payload_offset());
auto encode_result = packet.Encode(std::move(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
pw::Buf encoded_buf = std::move(encode_result.value());
EXPECT_EQ(encoded_buf.size(), total_size);
auto decode_result =
internal::InboundPacket::Decode(pw::ConstBuf(std::move(encoded_buf)));
ASSERT_EQ(decode_result.status(), pw::OkStatus());
internal::InboundPacket decoded = std::move(decode_result.value());
EXPECT_EQ(decoded.type(), expected_type);
EXPECT_EQ(decoded.payload_offset(), sizeof(internal::MessageWireFormat));
EXPECT_EQ(decoded.call_id(), 0x12345678u);
EXPECT_EQ(decoded.payload().size(), kPayload.size());
EXPECT_EQ(AsString(decoded.payload()), kPayload);
pw::ConstBuf payload_buf = std::move(decoded).TakePayload();
EXPECT_EQ(payload_buf.size(), kPayload.size());
EXPECT_EQ(AsString(payload_buf), kPayload);
}
}
TEST(PacketTest, EncodeDecodeStreamEnd) {
pw::allocator::test::AllocatorForTest<256> allocator;
size_t total_size = sizeof(internal::StreamEndWireFormat);
for (auto [sender, expected_type] :
{std::pair{internal::EndpointRole::kClient,
internal::PacketType::kClientStreamEnd},
std::pair{internal::EndpointRole::kServer,
internal::PacketType::kServerStreamEnd}}) {
auto buf = pw::Buf::Allocate(allocator, total_size);
auto encode_result = internal::OutboundPacket::StreamEnd(sender, 0x12345678)
.Encode(std::move(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
pw::Buf encoded_buf = std::move(encode_result.value());
EXPECT_EQ(encoded_buf.size(), total_size);
auto decode_result =
internal::InboundPacket::Decode(pw::ConstBuf(std::move(encoded_buf)));
ASSERT_EQ(decode_result.status(), pw::OkStatus());
internal::InboundPacket decoded = std::move(decode_result.value());
EXPECT_EQ(decoded.type(), expected_type);
EXPECT_EQ(decoded.call_id(), 0x12345678u);
}
}
TEST(PacketTest, EncodeDecodeError) {
pw::allocator::test::AllocatorForTest<256> allocator;
size_t total_size = sizeof(internal::ErrorWireFormat);
{
auto buf = pw::Buf::Allocate(allocator, total_size);
auto encode_result = internal::OutboundPacket::Error(
0x12345678, internal::ClientError::kCancelled)
.Encode(std::move(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
pw::Buf encoded_buf = std::move(encode_result.value());
EXPECT_EQ(encoded_buf.size(), total_size);
auto decode_result =
internal::InboundPacket::Decode(pw::ConstBuf(std::move(encoded_buf)));
ASSERT_EQ(decode_result.status(), pw::OkStatus());
internal::InboundPacket decoded = std::move(decode_result.value());
EXPECT_EQ(decoded.type(), internal::PacketType::kClientError);
EXPECT_TRUE(internal::IsError(decoded.type()));
EXPECT_EQ(decoded.call_id(), 0x12345678u);
EXPECT_EQ(decoded.client_error(), internal::ClientError::kCancelled);
}
{
auto buf = pw::Buf::Allocate(allocator, total_size);
auto encode_result = internal::OutboundPacket::Error(
0x12345678, internal::ServerError::kUnknownMethod)
.Encode(std::move(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
pw::Buf encoded_buf = std::move(encode_result.value());
EXPECT_EQ(encoded_buf.size(), total_size);
auto decode_result =
internal::InboundPacket::Decode(pw::ConstBuf(std::move(encoded_buf)));
ASSERT_EQ(decode_result.status(), pw::OkStatus());
internal::InboundPacket decoded = std::move(decode_result.value());
EXPECT_EQ(decoded.type(), internal::PacketType::kServerError);
EXPECT_TRUE(internal::IsError(decoded.type()));
EXPECT_EQ(decoded.call_id(), 0x12345678u);
EXPECT_EQ(decoded.server_error(), internal::ServerError::kUnknownMethod);
}
}
TEST(PacketTest, DecodeBufferTooShortForHeader) {
pw::allocator::test::AllocatorForTest<256> allocator;
auto buf = pw::Buf::Allocate(allocator, sizeof(internal::PacketHeader) - 1);
EXPECT_EQ(
internal::InboundPacket::Decode(pw::ConstBuf(std::move(buf))).status(),
pw::Status::DataLoss());
}
TEST(PacketTest, DecodeBufferTooShortForType) {
pw::allocator::test::AllocatorForTest<256> allocator;
// Long enough for the common header, but not for a request header.
auto buf = pw::Buf::Allocate(allocator, sizeof(internal::PacketHeader));
ASSERT_EQ(internal::OutboundPacket::Request(1, 2, 3)
.EncodeHeader(pw::ByteSpan(buf))
.status(),
pw::Status::ResourceExhausted());
// Write just the type byte by hand, since the header does not fit.
buf[offsetof(internal::PacketHeader, type)] =
static_cast<std::byte>(internal::PacketType::kRequest);
EXPECT_EQ(
internal::InboundPacket::Decode(pw::ConstBuf(std::move(buf))).status(),
pw::Status::DataLoss());
}
TEST(PacketTest, DecodeUnrecognizedType) {
pw::allocator::test::AllocatorForTest<256> allocator;
for (std::byte invalid_type : {std::byte{0x00},
std::byte{0x01},
std::byte{0x0a},
std::byte{0x7f},
std::byte{0xff}}) {
auto buf = pw::Buf::Allocate(allocator, 32);
buf[offsetof(internal::PacketHeader, type)] = invalid_type;
EXPECT_EQ(
internal::InboundPacket::Decode(pw::ConstBuf(std::move(buf))).status(),
pw::Status::InvalidArgument());
}
}
TEST(PacketTest, EncodeDecodeHandshakePacket) {
pw::allocator::test::AllocatorForTest<256> allocator;
size_t total_size = internal::HandshakePacket::kWireSizeBytes;
EXPECT_EQ(total_size, 8u);
auto buf = pw::Buf::Allocate(allocator, total_size);
internal::HandshakePacket packet(internal::HandshakePacket::Type::kSyn);
auto encode_result = packet.Encode(std::move(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
pw::Buf encoded_buf = std::move(encode_result.value());
EXPECT_EQ(encoded_buf.size(), total_size);
auto decode_result = internal::HandshakePacket::Decode(encoded_buf);
ASSERT_EQ(decode_result.status(), pw::OkStatus());
internal::HandshakePacket decoded = decode_result.value();
EXPECT_EQ(decoded.version(), 1u);
EXPECT_EQ(decoded.type(), internal::HandshakePacket::Type::kSyn);
}
TEST(PacketTest, DecodeZeroLengthPayload) {
pw::allocator::test::AllocatorForTest<256> allocator;
size_t total_size = sizeof(internal::RequestWireFormat);
auto buf = pw::Buf::Allocate(allocator, total_size);
auto encode_result =
internal::OutboundPacket::Request(0x12345678, 0x100, 0x200)
.Encode(std::move(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
pw::Buf encoded_buf = std::move(encode_result.value());
EXPECT_EQ(encoded_buf.size(), total_size);
auto decode_result =
internal::InboundPacket::Decode(pw::ConstBuf(std::move(encoded_buf)));
ASSERT_EQ(decode_result.status(), pw::OkStatus());
internal::InboundPacket decoded = std::move(decode_result.value());
EXPECT_EQ(decoded.type(), internal::PacketType::kRequest);
EXPECT_EQ(decoded.payload().size(), 0u);
pw::ConstBuf payload_buf = std::move(decoded).TakePayload();
EXPECT_TRUE(payload_buf.empty());
}
TEST(PacketTest, DecodeCorruptHandshakePacket) {
pw::allocator::test::AllocatorForTest<256> allocator;
auto buf = pw::Buf::Allocate(allocator, 4);
EXPECT_EQ(internal::HandshakePacket::Decode(buf).status(),
pw::Status::DataLoss());
}
TEST(PacketTest, EncodeDecodeResponse) {
pw::allocator::test::AllocatorForTest<256> allocator;
constexpr std::string_view kPayload = "hello response";
size_t total_size = sizeof(internal::ResponseWireFormat) + kPayload.size();
auto buf = pw::Buf::Allocate(allocator, total_size);
auto packet = internal::OutboundPacket::Response(0x12345678);
pw::ConstByteSpan payload_bytes = pw::as_bytes(pw::span(kPayload));
std::copy(payload_bytes.begin(),
payload_bytes.end(),
buf.data() + packet.payload_offset());
auto encode_result = packet.Encode(std::move(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
pw::Buf encoded_buf = std::move(encode_result.value());
EXPECT_EQ(encoded_buf.size(), total_size);
auto decode_result =
internal::InboundPacket::Decode(pw::ConstBuf(std::move(encoded_buf)));
ASSERT_EQ(decode_result.status(), pw::OkStatus());
internal::InboundPacket decoded = std::move(decode_result.value());
EXPECT_EQ(decoded.type(), internal::PacketType::kResponse);
EXPECT_EQ(decoded.payload_offset(), sizeof(internal::ResponseWireFormat));
EXPECT_EQ(decoded.call_id(), 0x12345678u);
EXPECT_EQ(decoded.payload().size(), kPayload.size());
EXPECT_EQ(AsString(decoded.payload()), kPayload);
pw::ConstBuf payload_buf = std::move(decoded).TakePayload();
EXPECT_EQ(payload_buf.size(), kPayload.size());
EXPECT_EQ(AsString(payload_buf), kPayload);
}
TEST(PacketTest, EncodeStreamEndTruncatesExtraBuffer) {
pw::allocator::test::AllocatorForTest<256> allocator;
auto buf = pw::Buf::Allocate(allocator, 64);
auto encode_result = internal::OutboundPacket::ServerStreamEnd(0x12345678)
.Encode(std::move(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
EXPECT_EQ(encode_result->size(), sizeof(internal::StreamEndWireFormat));
}
TEST(PacketTest, EncodeErrorTruncatesExtraBuffer) {
pw::allocator::test::AllocatorForTest<256> allocator;
auto buf = pw::Buf::Allocate(allocator, 64);
auto encode_result = internal::OutboundPacket::Error(
0x12345678, internal::ServerError::kUnknownMethod)
.Encode(std::move(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
EXPECT_EQ(encode_result->size(), sizeof(internal::ErrorWireFormat));
}
TEST(PacketTest, EncodeHandshakeTruncatesExtraBuffer) {
pw::allocator::test::AllocatorForTest<256> allocator;
auto buf = pw::Buf::Allocate(allocator, 64);
auto encode_result =
internal::HandshakePacket(internal::HandshakePacket::Type::kSyn)
.Encode(std::move(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
EXPECT_EQ(encode_result->size(), internal::HandshakePacket::kWireSizeBytes);
auto decode_result = internal::HandshakePacket::Decode(*encode_result);
ASSERT_EQ(decode_result.status(), pw::OkStatus());
EXPECT_EQ(decode_result->type(), internal::HandshakePacket::Type::kSyn);
}
TEST(PacketTest, EncodeRejectsBufferSmallerThanHeader) {
pw::allocator::test::AllocatorForTest<256> allocator;
auto buf =
pw::Buf::Allocate(allocator, sizeof(internal::RequestWireFormat) - 1);
EXPECT_EQ(internal::OutboundPacket::Request(1, 2, 3)
.Encode(std::move(buf))
.status(),
pw::Status::ResourceExhausted());
}
TEST(PacketTest, EncodeRejectsEmptyBuffer) {
EXPECT_EQ(
internal::OutboundPacket::ClientMessage(1).Encode(pw::Buf()).status(),
pw::Status::FailedPrecondition());
}
TEST(PacketTest, ServerErrorToStatusAndToString) {
using internal::ServerError;
EXPECT_EQ(ToStatus(ServerError::kUnknown), pw::Status::Unknown());
EXPECT_EQ(ToStatus(ServerError::kInternal), pw::Status::Internal());
EXPECT_EQ(ToStatus(ServerError::kCancelled), pw::Status::Cancelled());
EXPECT_EQ(ToStatus(ServerError::kReceivedPacketForClient),
pw::Status::Unimplemented());
EXPECT_EQ(ToStatus(ServerError::kDroppedWithoutResponse),
pw::Status::Cancelled());
EXPECT_EQ(ToStatus(ServerError::kServiceUnregistered),
pw::Status::Cancelled());
EXPECT_EQ(ToStatus(ServerError::kUnknownService), pw::Status::NotFound());
EXPECT_EQ(ToStatus(ServerError::kUnknownMethod), pw::Status::NotFound());
EXPECT_EQ(ToStatus(ServerError::kInvalidRequestPayload),
pw::Status::DataLoss());
EXPECT_EQ(ToStatus(ServerError::kFailedToAllocateCall),
pw::Status::ResourceExhausted());
EXPECT_EQ(ToStatus(ServerError::kFailedToAllocateCallResourcesWhileRunning),
pw::Status::ResourceExhausted());
EXPECT_EQ(ToStatus(ServerError::kOk), pw::Status::Internal());
EXPECT_EQ(ToStatus(static_cast<ServerError>(0xff)), pw::Status::Unknown());
EXPECT_STREQ(pw::EnumToString(ServerError::kOk), "OK");
EXPECT_STREQ(pw::EnumToString(ServerError::kUnknown), "UNKNOWN");
EXPECT_STREQ(pw::EnumToString(ServerError::kInternal), "INTERNAL");
EXPECT_STREQ(pw::EnumToString(ServerError::kCancelled), "CANCELLED");
EXPECT_STREQ(pw::EnumToString(ServerError::kReceivedPacketForClient),
"RECEIVED_PACKET_FOR_CLIENT");
EXPECT_STREQ(pw::EnumToString(ServerError::kDroppedWithoutResponse),
"DROPPED_WITHOUT_RESPONSE");
EXPECT_STREQ(pw::EnumToString(ServerError::kServiceUnregistered),
"SERVICE_UNREGISTERED");
EXPECT_STREQ(pw::EnumToString(ServerError::kUnknownService),
"UNKNOWN_SERVICE");
EXPECT_STREQ(pw::EnumToString(ServerError::kUnknownMethod), "UNKNOWN_METHOD");
EXPECT_STREQ(pw::EnumToString(ServerError::kInvalidRequestPayload),
"INVALID_REQUEST_PAYLOAD");
EXPECT_STREQ(pw::EnumToString(ServerError::kFailedToAllocateCall),
"FAILED_TO_ALLOCATE_CALL");
EXPECT_STREQ(
pw::EnumToString(ServerError::kFailedToAllocateCallResourcesWhileRunning),
"FAILED_TO_ALLOCATE_CALL_RESOURCES_WHILE_RUNNING");
}
TEST(PacketTest, ClientErrorToStatusAndToString) {
using internal::ClientError;
EXPECT_EQ(ToStatus(ClientError::kUnknown), pw::Status::Unknown());
EXPECT_EQ(ToStatus(ClientError::kInternal), pw::Status::Internal());
EXPECT_EQ(ToStatus(ClientError::kCancelled), pw::Status::Cancelled());
EXPECT_EQ(ToStatus(ClientError::kReceivedPacketForServer),
pw::Status::Unimplemented());
EXPECT_EQ(ToStatus(ClientError::kOk), pw::Status::Internal());
EXPECT_EQ(ToStatus(static_cast<ClientError>(0xff)), pw::Status::Unknown());
EXPECT_STREQ(pw::EnumToString(ClientError::kOk), "OK");
EXPECT_STREQ(pw::EnumToString(ClientError::kUnknown), "UNKNOWN");
EXPECT_STREQ(pw::EnumToString(ClientError::kInternal), "INTERNAL");
EXPECT_STREQ(pw::EnumToString(ClientError::kCancelled), "CANCELLED");
EXPECT_STREQ(pw::EnumToString(ClientError::kReceivedPacketForServer),
"RECEIVED_PACKET_FOR_SERVER");
}
// Codes with equivalent meanings share the same value.
static_assert(static_cast<uint8_t>(internal::ServerError::kOk) ==
static_cast<uint8_t>(internal::ClientError::kOk));
static_assert(static_cast<uint8_t>(internal::ServerError::kUnknown) ==
static_cast<uint8_t>(internal::ClientError::kUnknown));
static_assert(static_cast<uint8_t>(internal::ServerError::kInternal) ==
static_cast<uint8_t>(internal::ClientError::kInternal));
static_assert(static_cast<uint8_t>(internal::ServerError::kCancelled) ==
static_cast<uint8_t>(internal::ClientError::kCancelled));
static_assert(
static_cast<uint8_t>(internal::ServerError::kReceivedPacketForClient) ==
static_cast<uint8_t>(internal::ClientError::kReceivedPacketForServer));
static_assert(
internal::PacketSizeWithoutPayload(internal::PacketType::kRequest) == 13u);
static_assert(
internal::PacketSizeWithoutPayload(internal::PacketType::kResponse) == 5u);
static_assert(internal::PacketSizeWithoutPayload(
internal::PacketType::kClientMessage) == 5u);
static_assert(internal::PacketSizeWithoutPayload(
internal::PacketType::kServerMessage) == 5u);
static_assert(internal::PacketSizeWithoutPayload(
internal::PacketType::kClientStreamEnd) == 5u);
static_assert(internal::PacketSizeWithoutPayload(
internal::PacketType::kServerStreamEnd) == 5u);
static_assert(internal::PacketSizeWithoutPayload(
internal::PacketType::kClientError) == 7u);
static_assert(internal::PacketSizeWithoutPayload(
internal::PacketType::kServerError) == 7u);
static_assert(internal::OutboundPacket::Request(1, 2, 3).payload_offset() ==
13u);
static_assert(internal::OutboundPacket::Response(1).payload_offset() == 5u);
static_assert(internal::OutboundPacket::ClientMessage(1).payload_offset() ==
5u);
static_assert(internal::OutboundPacket::ServerMessage(1).payload_offset() ==
5u);
static_assert(internal::OutboundPacket::ClientStreamEnd(1).payload_offset() ==
5u);
static_assert(internal::OutboundPacket::ServerStreamEnd(1).payload_offset() ==
5u);
static_assert(internal::OutboundPacket::Error(1,
internal::ClientError::kCancelled)
.payload_offset() == 7u);
static_assert(internal::OutboundPacket::Error(1,
internal::ServerError::kCancelled)
.payload_offset() == 7u);
static_assert(internal::IsError(internal::PacketType::kClientError));
static_assert(internal::IsError(internal::PacketType::kServerError));
static_assert(!internal::IsError(internal::PacketType::kClientStreamEnd));
static_assert(!internal::IsError(internal::PacketType::kServerStreamEnd));
static_assert(!internal::IsError(static_cast<internal::PacketType>(0x0a)));
static_assert(!internal::IsError(static_cast<internal::PacketType>(0x0b)));
// True if `type` is addressed to `to` and not to the other endpoint.
constexpr bool OnlyFor(internal::PacketType type, internal::EndpointRole to) {
const auto other = to == internal::EndpointRole::kServer
? internal::EndpointRole::kClient
: internal::EndpointRole::kServer;
return internal::IsPacketFor(type, to) && !internal::IsPacketFor(type, other);
}
// Every valid type is addressed to exactly one endpoint.
static_assert(OnlyFor(internal::PacketType::kRequest,
internal::EndpointRole::kServer));
static_assert(OnlyFor(internal::PacketType::kResponse,
internal::EndpointRole::kClient));
static_assert(OnlyFor(internal::PacketType::kClientMessage,
internal::EndpointRole::kServer));
static_assert(OnlyFor(internal::PacketType::kServerMessage,
internal::EndpointRole::kClient));
static_assert(OnlyFor(internal::PacketType::kClientStreamEnd,
internal::EndpointRole::kServer));
static_assert(OnlyFor(internal::PacketType::kServerStreamEnd,
internal::EndpointRole::kClient));
static_assert(OnlyFor(internal::PacketType::kClientError,
internal::EndpointRole::kServer));
static_assert(OnlyFor(internal::PacketType::kServerError,
internal::EndpointRole::kClient));
// Unrecognized type bytes are rejected even when their direction bit matches
// the destination.
static_assert(!internal::IsPacketFor(static_cast<internal::PacketType>(0x00),
internal::EndpointRole::kServer));
static_assert(!internal::IsPacketFor(static_cast<internal::PacketType>(0x01),
internal::EndpointRole::kClient));
static_assert(!internal::IsPacketFor(static_cast<internal::PacketType>(0x0a),
internal::EndpointRole::kServer));
static_assert(!internal::IsPacketFor(static_cast<internal::PacketType>(0x0b),
internal::EndpointRole::kClient));
static_assert(!internal::IsPacketFor(static_cast<internal::PacketType>(0xff),
internal::EndpointRole::kClient));
TEST(PacketTest, EncodedMessageHeaderLayout) {
std::array<std::byte, sizeof(internal::MessageWireFormat)> buffer = {};
auto result =
internal::OutboundPacket::ClientMessage(0x12345678).EncodeHeader(buffer);
ASSERT_EQ(result.status(), pw::OkStatus());
EXPECT_EQ(*result, buffer.size());
// call_id is little endian and comes first, followed by the type byte (0x04).
constexpr auto expected = pw::bytes::Array<0x78, 0x56, 0x34, 0x12, 0x04>();
EXPECT_EQ(buffer, expected);
}
TEST(PacketTest, EncodedRequestHeaderLayout) {
std::array<std::byte, sizeof(internal::RequestWireFormat)> buffer = {};
auto result =
internal::OutboundPacket::Request(0x12345678, 0xabcdef01, 0x23456789)
.EncodeHeader(buffer);
ASSERT_EQ(result.status(), pw::OkStatus());
EXPECT_EQ(*result, buffer.size());
constexpr auto expected = pw::bytes::Array<0x78,
0x56,
0x34,
0x12,
0x02,
0x01,
0xef,
0xcd,
0xab,
0x89,
0x67,
0x45,
0x23>();
EXPECT_EQ(buffer, expected);
}
TEST(PacketTest, EncodedErrorHeaderLayout) {
std::array<std::byte, sizeof(internal::ErrorWireFormat)> buffer = {};
auto result = internal::OutboundPacket::Error(
0x12345678, internal::ServerError::kUnknownMethod)
.EncodeHeader(buffer);
ASSERT_EQ(result.status(), pw::OkStatus());
EXPECT_EQ(*result, buffer.size());
// 4-byte call_id (LE), 1-byte type (0x09), 2-byte error (LE).
constexpr auto expected =
pw::bytes::Array<0x78,
0x56,
0x34,
0x12,
0x09,
static_cast<uint8_t>(
internal::ServerError::kUnknownMethod),
0x00>();
EXPECT_EQ(buffer, expected);
auto decoded = internal::InboundPacket::Decode(pw::ConstBuf::Unowned(buffer));
ASSERT_EQ(decoded.status(), pw::OkStatus());
EXPECT_EQ(decoded->server_error(), internal::ServerError::kUnknownMethod);
}
TEST(PacketTest, DecodeErrorZeroMapsToInternal) {
// A wire error of 0 (kOk) is never valid in an error packet.
auto buffer = pw::bytes::Array<0x78, 0x56, 0x34, 0x12, 0x09, 0x00, 0x00>();
auto decoded = internal::InboundPacket::Decode(pw::ConstBuf::Unowned(buffer));
ASSERT_EQ(decoded.status(), pw::OkStatus());
EXPECT_EQ(decoded->server_error(), internal::ServerError::kInternal);
EXPECT_EQ(internal::ToStatus(decoded->server_error()),
pw::Status::Internal());
buffer[4] = static_cast<std::byte>(internal::PacketType::kClientError);
decoded = internal::InboundPacket::Decode(pw::ConstBuf::Unowned(buffer));
ASSERT_EQ(decoded.status(), pw::OkStatus());
EXPECT_EQ(decoded->client_error(), internal::ClientError::kInternal);
EXPECT_EQ(internal::ToStatus(decoded->client_error()),
pw::Status::Internal());
}
TEST(PacketTest, DecodeErrorOutOfRangeMapsToUnknown) {
auto buffer = pw::bytes::Array<0x78, 0x56, 0x34, 0x12, 0x09, 0x00, 0x01>();
// 0x0100 is beyond the largest known code.
auto decoded = internal::InboundPacket::Decode(pw::ConstBuf::Unowned(buffer));
ASSERT_EQ(decoded.status(), pw::OkStatus());
EXPECT_EQ(decoded->server_error(), internal::ServerError::kUnknown);
EXPECT_EQ(internal::ToStatus(decoded->server_error()), pw::Status::Unknown());
buffer[4] = static_cast<std::byte>(internal::PacketType::kClientError);
decoded = internal::InboundPacket::Decode(pw::ConstBuf::Unowned(buffer));
ASSERT_EQ(decoded.status(), pw::OkStatus());
EXPECT_EQ(decoded->client_error(), internal::ClientError::kUnknown);
EXPECT_EQ(internal::ToStatus(decoded->client_error()), pw::Status::Unknown());
// One past the maximum.
buffer[4] = static_cast<std::byte>(internal::PacketType::kServerError);
buffer[5] = static_cast<std::byte>(
static_cast<uint8_t>(pw::EnumTraits<internal::ServerError>::kMax) + 1);
buffer[6] = std::byte{0x00};
decoded = internal::InboundPacket::Decode(pw::ConstBuf::Unowned(buffer));
ASSERT_EQ(decoded.status(), pw::OkStatus());
EXPECT_EQ(decoded->server_error(), internal::ServerError::kUnknown);
buffer[4] = static_cast<std::byte>(internal::PacketType::kClientError);
buffer[5] = static_cast<std::byte>(
static_cast<uint8_t>(pw::EnumTraits<internal::ClientError>::kMax) + 1);
decoded = internal::InboundPacket::Decode(pw::ConstBuf::Unowned(buffer));
ASSERT_EQ(decoded.status(), pw::OkStatus());
EXPECT_EQ(decoded->client_error(), internal::ClientError::kUnknown);
}
TEST(PacketTest, EncodeHeaderReportsTotalSizeWithoutWritingPayloadLength) {
constexpr auto buffer_init = [](size_t i) {
return static_cast<std::byte>(i + 1);
};
auto buffer = pw::bytes::Initialized<64>(buffer_init);
auto result = internal::OutboundPacket::Response(1).EncodeHeader(buffer, 10);
ASSERT_EQ(result.status(), pw::OkStatus());
EXPECT_EQ(*result, sizeof(internal::ResponseWireFormat) + 10u);
// Nothing past the header was modified.
for (size_t i = sizeof(internal::ResponseWireFormat); i < buffer.size();
++i) {
EXPECT_EQ(buffer[i], buffer_init(i));
}
}
TEST(PacketTest, EncodeHeaderRejectsBufferSmallerThanHeaderPlusPayload) {
std::array<std::byte, sizeof(internal::MessageWireFormat) + 4> buffer = {};
EXPECT_EQ(internal::OutboundPacket::ClientMessage(1)
.EncodeHeader(buffer, 5)
.status(),
pw::Status::ResourceExhausted());
EXPECT_EQ(internal::OutboundPacket::ClientMessage(1)
.EncodeHeader(buffer, 4)
.status(),
pw::OkStatus());
}
TEST(PacketTest, DecodedPayloadIsEverythingAfterTheHeader) {
pw::allocator::test::AllocatorForTest<256> allocator;
// The payload length is not on the wire, so all trailing bytes belong to the
// payload regardless of what was passed to EncodeHeader.
constexpr size_t kTrailingBytes = 7;
auto buf = pw::Buf::Allocate(
allocator, sizeof(internal::MessageWireFormat) + kTrailingBytes);
auto encode_result = internal::OutboundPacket::ClientMessage(42).EncodeHeader(
pw::ByteSpan(buf));
ASSERT_EQ(encode_result.status(), pw::OkStatus());
ASSERT_EQ(*encode_result, sizeof(internal::MessageWireFormat));
auto decode_result =
internal::InboundPacket::Decode(pw::ConstBuf(std::move(buf)));
ASSERT_EQ(decode_result.status(), pw::OkStatus());
internal::InboundPacket decoded = std::move(decode_result.value());
EXPECT_EQ(decoded.type(), internal::PacketType::kClientMessage);
EXPECT_EQ(decoded.call_id(), 42u);
EXPECT_EQ(decoded.payload().size(), kTrailingBytes);
}
} // namespace