// Copyright 2023 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_rpc_transport/simple_framing.h" #include #include #include #include "pw_bytes/span.h" #include "pw_log/log.h" #include "pw_status/status.h" #include "pw_unit_test/framework.h" namespace pw::rpc { namespace { constexpr size_t kMaxPacketSize = 256; struct TestParams { size_t packet_size = 0; size_t max_frame_size = 0; }; constexpr std::array kTestCases = { // Packet fits in one frame. TestParams{.packet_size = 5, .max_frame_size = 100}, // Typical parameters for RPC packet and mailbox frame size. TestParams{.packet_size = 100, .max_frame_size = 128}, // Smallest packet. TestParams{.packet_size = 1, .max_frame_size = 16}, // Small packet, small frame. TestParams{.packet_size = 16, .max_frame_size = 5}, // Odd-sized packet, small frame. TestParams{.packet_size = 77, .max_frame_size = 16}, // Frame size and packet size off by one. TestParams{.packet_size = 11, .max_frame_size = 10}, // Almost at the limit. TestParams{.packet_size = kMaxPacketSize - 1, .max_frame_size = kMaxPacketSize - 2}, // At the limit. TestParams{.packet_size = kMaxPacketSize, .max_frame_size = kMaxPacketSize}}; void MakePacket(ByteSpan dst_buffer) { static uint32_t rg_seed = 0x123; unsigned char c = 0; for (auto& i : dst_buffer) { i = std::byte{c++}; } std::mt19937 rg(rg_seed++); std::shuffle(dst_buffer.begin(), dst_buffer.end(), rg); } void CopyFrame(RpcFrame frame, std::vector& dst) { std::copy(frame.header.begin(), frame.header.end(), std::back_inserter(dst)); std::copy( frame.payload.begin(), frame.payload.end(), std::back_inserter(dst)); } TEST(SimpleRpcFrameEncodeDecodeTest, EncodeThenDecode) { for (auto test_case : kTestCases) { const size_t packet_size = test_case.packet_size; const size_t max_frame_size = test_case.max_frame_size; PW_LOG_INFO("EncodeThenDecode: packet_size = %d, max_frame_size = %d", static_cast(packet_size), static_cast(max_frame_size)); std::vector src(packet_size); MakePacket(src); std::vector encoded; std::vector decoded; SimpleRpcPacketEncoder encoder; ASSERT_EQ(encoder.Encode(src, max_frame_size, [&encoded](RpcFrame& frame) { CopyFrame(frame, encoded); return OkStatus(); }), OkStatus()); SimpleRpcPacketDecoder decoder; ASSERT_EQ(decoder.Decode(encoded, [&decoded](ConstByteSpan packet) { std::copy(packet.begin(), packet.end(), std::back_inserter(decoded)); }), OkStatus()); EXPECT_TRUE(std::equal(src.begin(), src.end(), decoded.begin())); } } TEST(SimpleRpcFrameEncodeDecodeTest, OneByteAtTimeDecoding) { for (auto test_case : kTestCases) { const size_t packet_size = test_case.packet_size; const size_t max_frame_size = test_case.max_frame_size; PW_LOG_INFO("EncodeThenDecode: packet_size = %d, max_frame_size = %d", static_cast(packet_size), static_cast(max_frame_size)); std::vector src(packet_size); MakePacket(src); std::vector encoded; std::vector decoded; SimpleRpcPacketEncoder encoder; ASSERT_EQ(encoder.Encode(src, max_frame_size, [&encoded](RpcFrame& frame) { CopyFrame(frame, encoded); return OkStatus(); }), OkStatus()); SimpleRpcPacketDecoder decoder; for (std::byte b : encoded) { auto buffer_span = span(&b, 1); ASSERT_EQ(decoder.Decode(buffer_span, [&decoded](ConstByteSpan packet) { std::copy(packet.begin(), packet.end(), std::back_inserter(decoded)); }), OkStatus()); } EXPECT_TRUE(std::equal(src.begin(), src.end(), decoded.begin())); } } TEST(SimpleRpcFrameTest, MissingFirstFrame) { // Sends two packets, the first packet is missing its first frame. The decoder // ignores the remaining frames of the first packet but still picks up the // second packet. constexpr size_t kPacketSize = 77; constexpr size_t kMaxFrameSize = 16; std::vector src1(kPacketSize); MakePacket(src1); std::vector src2(kPacketSize); MakePacket(src2); std::vector decoded; SimpleRpcPacketEncoder encoder; struct EncodeState { size_t frame_counter = 0; std::vector encoded; } state; ASSERT_EQ(encoder.Encode(src1, kMaxFrameSize, [&state](RpcFrame& frame) { state.frame_counter++; if (state.frame_counter > 1) { // Skip the first frame. CopyFrame(frame, state.encoded); } return OkStatus(); }), OkStatus()); ASSERT_EQ(encoder.Encode(src2, kMaxFrameSize, [&state](RpcFrame& frame) { CopyFrame(frame, state.encoded); return OkStatus(); }), OkStatus()); SimpleRpcPacketDecoder decoder; ASSERT_EQ(decoder.Decode(state.encoded, [&decoded](ConstByteSpan packet) { std::copy(packet.begin(), packet.end(), std::back_inserter(decoded)); }), OkStatus()); EXPECT_TRUE(std::equal(src2.begin(), src2.end(), decoded.begin())); } TEST(SimpleRpcFrameTest, MissingInternalFrame) { // Sends two packets, the first packet is missing its second frame. The // decoder ignores the remaining frames of the first packet and the second // packet as well but eventually stumbles upon the frame header in the third // packet and processes that packet. constexpr size_t kPacketSize = 77; constexpr size_t kMaxFrameSize = 16; std::vector src1(kPacketSize); MakePacket(src1); std::vector src2(kPacketSize); MakePacket(src2); std::vector src3(kPacketSize); MakePacket(src3); std::vector decoded; SimpleRpcPacketEncoder encoder; struct EncodeState { size_t frame_counter = 0; std::vector encoded; } encode_state; ASSERT_EQ(encoder.Encode(src1, kMaxFrameSize, [&encode_state](RpcFrame& frame) { encode_state.frame_counter++; if (encode_state.frame_counter != 2) { // Skip the second frame. CopyFrame(frame, encode_state.encoded); } return OkStatus(); }), OkStatus()); ASSERT_EQ(encoder.Encode(src2, kMaxFrameSize, [&encode_state](RpcFrame& frame) { CopyFrame(frame, encode_state.encoded); return OkStatus(); }), OkStatus()); ASSERT_EQ(encoder.Encode(src3, kMaxFrameSize, [&encode_state](RpcFrame& frame) { CopyFrame(frame, encode_state.encoded); return OkStatus(); }), OkStatus()); SimpleRpcPacketDecoder decoder; // First packet is decoded but it doesn't have correct bytes, as one of its // frames has never been received. Second packet is not received because its // header has been consumed by the first packet. By that point the decoder // knows that something is wrong and tries to recover as soon as it receives // bytes that look as the valid header. So we eventually receive the third // packet and it is correct. struct DecodeState { std::vector decoded1; std::vector decoded2; size_t packet_counter = 0; } decode_state; ASSERT_EQ( decoder.Decode(encode_state.encoded, [&decode_state](ConstByteSpan packet) { decode_state.packet_counter++; if (decode_state.packet_counter == 1) { std::copy(packet.begin(), packet.end(), std::back_inserter(decode_state.decoded1)); } if (decode_state.packet_counter == 2) { std::copy(packet.begin(), packet.end(), std::back_inserter(decode_state.decoded2)); } }), OkStatus()); EXPECT_EQ(decode_state.packet_counter, 2ul); EXPECT_EQ(decode_state.decoded1.size(), src1.size()); EXPECT_FALSE( std::equal(src1.begin(), src1.end(), decode_state.decoded1.begin())); EXPECT_TRUE( std::equal(src3.begin(), src3.end(), decode_state.decoded2.begin())); } TEST(SimpleRpcPacketEncoder, PacketTooBig) { SimpleRpcPacketEncoder encoder; constexpr size_t kMaxFrameSize = 100; std::array src{}; EXPECT_EQ( encoder.Encode(src, kMaxFrameSize, [](RpcFrame&) { return OkStatus(); }), Status::FailedPrecondition()); } TEST(SimpleRpcPacketEncoder, MaxFrameSizeTooSmall) { SimpleRpcPacketEncoder encoder; std::array src{}; EXPECT_EQ(encoder.Encode( src, encoder.kHeaderSize, [](RpcFrame&) { return OkStatus(); }), Status::FailedPrecondition()); EXPECT_EQ( encoder.Encode( src, encoder.kHeaderSize + 1, [](RpcFrame&) { return OkStatus(); }), OkStatus()); } TEST(SimpleRpcFrameTest, EncoderBufferLargerThanDecoderBuffer) { constexpr size_t kLargePacketSize = 150; constexpr size_t kSmallPacketSize = 120; constexpr size_t kMaxFrameSize = 16; // Decoder isn't able to receive the whole packet because it needs to be // buffered but the internal buffer is too small; the packet is thus // discarded. The second packet is received without issues as it's small // enough to fit in the decoder buffer. constexpr size_t kEncoderMaxPacketSize = 256; constexpr size_t kDecoderMaxPacketSize = 128; std::vector src1(kLargePacketSize); MakePacket(src1); std::vector src2(kSmallPacketSize); MakePacket(src1); std::vector encoded; std::vector decoded; SimpleRpcPacketEncoder encoder; ASSERT_EQ(encoder.Encode(src1, kMaxFrameSize, [&encoded](RpcFrame& frame) { CopyFrame(frame, encoded); return OkStatus(); }), OkStatus()); ASSERT_EQ(encoder.Encode(src2, kMaxFrameSize, [&encoded](RpcFrame& frame) { CopyFrame(frame, encoded); return OkStatus(); }), OkStatus()); SimpleRpcPacketDecoder decoder; // We have to decode piecemeal here because otherwise the decoder can just // pluck the packet from `encoded` without internally buffering it. for (std::byte b : encoded) { auto buffer_span = span(&b, 1); ASSERT_EQ(decoder.Decode(buffer_span, [&decoded](ConstByteSpan packet) { std::copy(packet.begin(), packet.end(), std::back_inserter(decoded)); }), OkStatus()); } EXPECT_TRUE(std::equal(src2.begin(), src2.end(), decoded.begin())); } } // namespace } // namespace pw::rpc