blob: 2fa8b01a2052cf0dad23f769f619c175553e401d [file] [log] [blame]
// 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/hdlc_framing.h"
#include <algorithm>
#include <array>
#include "pw_bytes/span.h"
#include "pw_status/status.h"
#include "pw_unit_test/framework.h"
namespace pw::rpc {
namespace {
TEST(HdlcRpc, EncodeThenDecode) {
constexpr size_t kMaxPacketSize = 256;
constexpr size_t kPacketSize = 100;
constexpr size_t kMaxFrameSize = 20;
// Expecting six frames due to HDLC overhead.
constexpr size_t kNumFramesExpected = 6;
HdlcRpcPacketEncoder<kMaxPacketSize> encoder;
std::array<std::byte, kPacketSize> packet{};
struct EncodeState {
size_t num_frames = 0;
size_t offset = 0;
std::array<std::byte, 2 * kMaxPacketSize> encoded{};
} state;
std::fill(packet.begin(), packet.end(), std::byte{0x42});
ASSERT_EQ(encoder.Encode(packet,
kMaxFrameSize,
[&state](RpcFrame& frame) {
state.num_frames++;
EXPECT_TRUE(frame.header.empty());
std::copy(frame.payload.begin(),
frame.payload.end(),
state.encoded.begin() + state.offset);
state.offset += frame.payload.size();
return OkStatus();
}),
OkStatus());
EXPECT_EQ(state.num_frames, kNumFramesExpected);
std::array<std::byte, kMaxPacketSize> decoded{};
HdlcRpcPacketDecoder<kMaxPacketSize> decoder;
ASSERT_EQ(decoder.Decode(state.encoded,
[&decoded](ConstByteSpan packet_to_decode) {
std::copy(packet_to_decode.begin(),
packet_to_decode.end(),
decoded.begin());
}),
OkStatus());
EXPECT_TRUE(std::equal(packet.begin(), packet.end(), decoded.begin()));
}
TEST(HdlcRpc, PacketTooLong) {
constexpr size_t kMaxPacketSize = 256;
constexpr size_t kMaxFrameSize = 100;
std::array<std::byte, kMaxPacketSize + 1> packet{};
HdlcRpcPacketEncoder<kMaxPacketSize> encoder;
EXPECT_EQ(encoder.Encode(
packet, kMaxFrameSize, [](RpcFrame&) { return OkStatus(); }),
Status::FailedPrecondition());
}
TEST(HdlcRpcFrame, MaxFrameSizeIsZero) {
constexpr size_t kMaxPacketSize = 256;
constexpr size_t kMaxFrameSize = 0;
std::array<std::byte, kMaxPacketSize> packet{};
HdlcRpcPacketEncoder<kMaxPacketSize> encoder;
EXPECT_EQ(encoder.Encode(
packet, kMaxFrameSize, [](RpcFrame&) { return OkStatus(); }),
Status::FailedPrecondition());
}
TEST(HdlcRpcFrame, MaxSizeHdlcPayload) {
constexpr size_t kMaxPacketSize = 256;
constexpr size_t kPacketSize = 256;
constexpr size_t kMaxFrameSize = 20;
constexpr auto kHdlcEscapeByte = std::byte{0x7e};
std::array<std::byte, kPacketSize> packet{};
std::fill(packet.begin(), packet.end(), kHdlcEscapeByte);
struct EncodeState {
size_t offset = 0;
std::array<std::byte, 2 * kMaxPacketSize + kHdlcProtocolOverheadBytes>
encoded{};
} state;
HdlcRpcPacketEncoder<kMaxPacketSize> encoder;
ASSERT_EQ(encoder.Encode(packet,
kMaxFrameSize,
[&state](RpcFrame& frame) {
EXPECT_TRUE(frame.header.empty());
std::copy(frame.payload.begin(),
frame.payload.end(),
state.encoded.begin() + state.offset);
state.offset += frame.payload.size();
return OkStatus();
}),
OkStatus());
std::array<std::byte, kMaxPacketSize> decoded{};
HdlcRpcPacketDecoder<kMaxPacketSize> decoder;
ASSERT_EQ(decoder.Decode(state.encoded,
[&decoded](ConstByteSpan packet_to_decode) {
std::copy(packet_to_decode.begin(),
packet_to_decode.end(),
decoded.begin());
}),
OkStatus());
EXPECT_TRUE(std::equal(packet.begin(), packet.end(), decoded.begin()));
}
TEST(HdlcRpc, CallbackErrorPropagation) {
constexpr size_t kMaxPacketSize = 256;
constexpr size_t kPacketSize = 256;
constexpr size_t kMaxFrameSize = 20;
std::array<std::byte, kPacketSize> packet{};
std::fill(packet.begin(), packet.end(), std::byte{0x42});
HdlcRpcPacketEncoder<kMaxPacketSize> encoder;
EXPECT_EQ(
encoder.Encode(packet,
kMaxFrameSize,
[](RpcFrame&) { return Status::PermissionDenied(); }),
Status::PermissionDenied());
}
TEST(HdlcRpcFrame, OneByteAtTimeDecoding) {
constexpr size_t kMaxPacketSize = 256;
constexpr size_t kPacketSize = 100;
constexpr size_t kMaxFrameSize = 8;
HdlcRpcPacketEncoder<kMaxPacketSize> encoder;
std::array<std::byte, kPacketSize> packet{};
std::vector<std::byte> encoded;
std::fill(packet.begin(), packet.end(), std::byte{0x42});
ASSERT_EQ(encoder.Encode(packet,
kMaxFrameSize,
[&encoded](RpcFrame& frame) {
std::copy(frame.payload.begin(),
frame.payload.end(),
std::back_inserter(encoded));
return OkStatus();
}),
OkStatus());
std::array<std::byte, kMaxPacketSize> decoded{};
HdlcRpcPacketDecoder<kMaxPacketSize> decoder;
for (std::byte b : encoded) {
auto buffer_span = span(&b, 1);
ASSERT_EQ(decoder.Decode(buffer_span,
[&decoded](ConstByteSpan packet_to_decode) {
std::copy(packet_to_decode.begin(),
packet_to_decode.end(),
decoded.begin());
}),
OkStatus());
}
EXPECT_TRUE(std::equal(packet.begin(), packet.end(), decoded.begin()));
}
} // namespace
} // namespace pw::rpc