1*61c4878aSAndroid Build Coastguard Worker // Copyright 2020 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/internal/packet.h"
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard Worker #include "pw_bytes/array.h"
18*61c4878aSAndroid Build Coastguard Worker #include "pw_fuzzer/fuzztest.h"
19*61c4878aSAndroid Build Coastguard Worker #include "pw_protobuf/wire_format.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_unit_test/framework.h"
21*61c4878aSAndroid Build Coastguard Worker
22*61c4878aSAndroid Build Coastguard Worker namespace pw::rpc::internal {
23*61c4878aSAndroid Build Coastguard Worker namespace {
24*61c4878aSAndroid Build Coastguard Worker
25*61c4878aSAndroid Build Coastguard Worker using protobuf::FieldKey;
26*61c4878aSAndroid Build Coastguard Worker using ::pw::rpc::internal::pwpb::PacketType;
27*61c4878aSAndroid Build Coastguard Worker using std::byte;
28*61c4878aSAndroid Build Coastguard Worker using namespace fuzzer;
29*61c4878aSAndroid Build Coastguard Worker
30*61c4878aSAndroid Build Coastguard Worker constexpr auto kPayload = bytes::Array<0x82, 0x02, 0xff, 0xff>();
31*61c4878aSAndroid Build Coastguard Worker
32*61c4878aSAndroid Build Coastguard Worker constexpr auto kEncoded = bytes::Array<
33*61c4878aSAndroid Build Coastguard Worker // Payload
34*61c4878aSAndroid Build Coastguard Worker uint32_t(FieldKey(5, protobuf::WireType::kDelimited)),
35*61c4878aSAndroid Build Coastguard Worker 0x04,
36*61c4878aSAndroid Build Coastguard Worker 0x82,
37*61c4878aSAndroid Build Coastguard Worker 0x02,
38*61c4878aSAndroid Build Coastguard Worker 0xff,
39*61c4878aSAndroid Build Coastguard Worker 0xff,
40*61c4878aSAndroid Build Coastguard Worker
41*61c4878aSAndroid Build Coastguard Worker // Packet type
42*61c4878aSAndroid Build Coastguard Worker uint32_t(FieldKey(1, protobuf::WireType::kVarint)),
43*61c4878aSAndroid Build Coastguard Worker 1, // RESPONSE
44*61c4878aSAndroid Build Coastguard Worker
45*61c4878aSAndroid Build Coastguard Worker // Channel ID
46*61c4878aSAndroid Build Coastguard Worker uint32_t(FieldKey(2, protobuf::WireType::kVarint)),
47*61c4878aSAndroid Build Coastguard Worker 1,
48*61c4878aSAndroid Build Coastguard Worker
49*61c4878aSAndroid Build Coastguard Worker // Service ID
50*61c4878aSAndroid Build Coastguard Worker uint32_t(FieldKey(3, protobuf::WireType::kFixed32)),
51*61c4878aSAndroid Build Coastguard Worker 42,
52*61c4878aSAndroid Build Coastguard Worker 0,
53*61c4878aSAndroid Build Coastguard Worker 0,
54*61c4878aSAndroid Build Coastguard Worker 0,
55*61c4878aSAndroid Build Coastguard Worker
56*61c4878aSAndroid Build Coastguard Worker // Method ID
57*61c4878aSAndroid Build Coastguard Worker uint32_t(FieldKey(4, protobuf::WireType::kFixed32)),
58*61c4878aSAndroid Build Coastguard Worker 100,
59*61c4878aSAndroid Build Coastguard Worker 0,
60*61c4878aSAndroid Build Coastguard Worker 0,
61*61c4878aSAndroid Build Coastguard Worker 0,
62*61c4878aSAndroid Build Coastguard Worker
63*61c4878aSAndroid Build Coastguard Worker // Status (not encoded if it is zero)
64*61c4878aSAndroid Build Coastguard Worker // FieldKey(6, protobuf::WireType::kVarint),
65*61c4878aSAndroid Build Coastguard Worker // 0x00
66*61c4878aSAndroid Build Coastguard Worker
67*61c4878aSAndroid Build Coastguard Worker // Call ID
68*61c4878aSAndroid Build Coastguard Worker uint32_t(FieldKey(7, protobuf::WireType::kVarint)),
69*61c4878aSAndroid Build Coastguard Worker 7>();
70*61c4878aSAndroid Build Coastguard Worker
71*61c4878aSAndroid Build Coastguard Worker // Test that a default-constructed packet sets its members to the default
72*61c4878aSAndroid Build Coastguard Worker // protobuf values.
73*61c4878aSAndroid Build Coastguard Worker static_assert(Packet().type() == PacketType{});
74*61c4878aSAndroid Build Coastguard Worker static_assert(Packet().channel_id() == 0);
75*61c4878aSAndroid Build Coastguard Worker static_assert(Packet().service_id() == 0);
76*61c4878aSAndroid Build Coastguard Worker static_assert(Packet().method_id() == 0);
77*61c4878aSAndroid Build Coastguard Worker static_assert(Packet().status() == static_cast<Status::Code>(0));
78*61c4878aSAndroid Build Coastguard Worker static_assert(Packet().payload().empty());
79*61c4878aSAndroid Build Coastguard Worker
TEST(Packet,Encode)80*61c4878aSAndroid Build Coastguard Worker TEST(Packet, Encode) {
81*61c4878aSAndroid Build Coastguard Worker byte buffer[64];
82*61c4878aSAndroid Build Coastguard Worker
83*61c4878aSAndroid Build Coastguard Worker Packet packet(PacketType::RESPONSE, 1, 42, 100, 7, kPayload);
84*61c4878aSAndroid Build Coastguard Worker
85*61c4878aSAndroid Build Coastguard Worker auto result = packet.Encode(buffer);
86*61c4878aSAndroid Build Coastguard Worker ASSERT_EQ(OkStatus(), result.status());
87*61c4878aSAndroid Build Coastguard Worker ASSERT_EQ(kEncoded.size(), result.value().size());
88*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(std::memcmp(kEncoded.data(), buffer, kEncoded.size()), 0);
89*61c4878aSAndroid Build Coastguard Worker }
90*61c4878aSAndroid Build Coastguard Worker
TEST(Packet,Encode_BufferTooSmall)91*61c4878aSAndroid Build Coastguard Worker TEST(Packet, Encode_BufferTooSmall) {
92*61c4878aSAndroid Build Coastguard Worker byte buffer[2];
93*61c4878aSAndroid Build Coastguard Worker
94*61c4878aSAndroid Build Coastguard Worker Packet packet(PacketType::RESPONSE, 1, 42, 100, 12, kPayload);
95*61c4878aSAndroid Build Coastguard Worker
96*61c4878aSAndroid Build Coastguard Worker auto result = packet.Encode(buffer);
97*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(Status::ResourceExhausted(), result.status());
98*61c4878aSAndroid Build Coastguard Worker }
99*61c4878aSAndroid Build Coastguard Worker
TEST(Packet,Decode_ValidPacket)100*61c4878aSAndroid Build Coastguard Worker TEST(Packet, Decode_ValidPacket) {
101*61c4878aSAndroid Build Coastguard Worker auto result = Packet::FromBuffer(kEncoded);
102*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(result.ok());
103*61c4878aSAndroid Build Coastguard Worker
104*61c4878aSAndroid Build Coastguard Worker auto& packet = result.value();
105*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(PacketType::RESPONSE, packet.type());
106*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(1u, packet.channel_id());
107*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(42u, packet.service_id());
108*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(100u, packet.method_id());
109*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(7u, packet.call_id());
110*61c4878aSAndroid Build Coastguard Worker ASSERT_EQ(sizeof(kPayload), packet.payload().size());
111*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(
112*61c4878aSAndroid Build Coastguard Worker 0,
113*61c4878aSAndroid Build Coastguard Worker std::memcmp(packet.payload().data(), kPayload.data(), kPayload.size()));
114*61c4878aSAndroid Build Coastguard Worker }
115*61c4878aSAndroid Build Coastguard Worker
TEST(Packet,Decode_InvalidPacket)116*61c4878aSAndroid Build Coastguard Worker TEST(Packet, Decode_InvalidPacket) {
117*61c4878aSAndroid Build Coastguard Worker byte bad_data[] = {byte{0xFF}, byte{0x00}, byte{0x00}, byte{0xFF}};
118*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(Status::DataLoss(), Packet::FromBuffer(bad_data).status());
119*61c4878aSAndroid Build Coastguard Worker }
120*61c4878aSAndroid Build Coastguard Worker
EncodeDecode(uint32_t channel_id,uint32_t service_id,uint32_t method_id,uint32_t call_id,ConstByteSpan payload,Status status)121*61c4878aSAndroid Build Coastguard Worker void EncodeDecode(uint32_t channel_id,
122*61c4878aSAndroid Build Coastguard Worker uint32_t service_id,
123*61c4878aSAndroid Build Coastguard Worker uint32_t method_id,
124*61c4878aSAndroid Build Coastguard Worker uint32_t call_id,
125*61c4878aSAndroid Build Coastguard Worker ConstByteSpan payload,
126*61c4878aSAndroid Build Coastguard Worker Status status) {
127*61c4878aSAndroid Build Coastguard Worker Packet packet;
128*61c4878aSAndroid Build Coastguard Worker packet.set_channel_id(channel_id);
129*61c4878aSAndroid Build Coastguard Worker packet.set_service_id(service_id);
130*61c4878aSAndroid Build Coastguard Worker packet.set_method_id(method_id);
131*61c4878aSAndroid Build Coastguard Worker packet.set_call_id(call_id);
132*61c4878aSAndroid Build Coastguard Worker packet.set_payload(payload);
133*61c4878aSAndroid Build Coastguard Worker packet.set_status(status);
134*61c4878aSAndroid Build Coastguard Worker
135*61c4878aSAndroid Build Coastguard Worker byte buffer[128];
136*61c4878aSAndroid Build Coastguard Worker Result result = packet.Encode(buffer);
137*61c4878aSAndroid Build Coastguard Worker ASSERT_EQ(result.status(), OkStatus());
138*61c4878aSAndroid Build Coastguard Worker
139*61c4878aSAndroid Build Coastguard Worker span<byte> packet_data(buffer, result.value().size());
140*61c4878aSAndroid Build Coastguard Worker auto decode_result = Packet::FromBuffer(packet_data);
141*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(decode_result.ok());
142*61c4878aSAndroid Build Coastguard Worker
143*61c4878aSAndroid Build Coastguard Worker auto& decoded = decode_result.value();
144*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(decoded.type(), packet.type());
145*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(decoded.channel_id(), packet.channel_id());
146*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(decoded.service_id(), packet.service_id());
147*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(decoded.method_id(), packet.method_id());
148*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(decoded.call_id(), packet.call_id());
149*61c4878aSAndroid Build Coastguard Worker ASSERT_EQ(decoded.payload().size(), packet.payload().size());
150*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(std::memcmp(decoded.payload().data(),
151*61c4878aSAndroid Build Coastguard Worker packet.payload().data(),
152*61c4878aSAndroid Build Coastguard Worker packet.payload().size()),
153*61c4878aSAndroid Build Coastguard Worker 0);
154*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(decoded.status(), status);
155*61c4878aSAndroid Build Coastguard Worker }
156*61c4878aSAndroid Build Coastguard Worker
TEST(Packet,EncodeDecodeFixed)157*61c4878aSAndroid Build Coastguard Worker TEST(Packet, EncodeDecodeFixed) {
158*61c4878aSAndroid Build Coastguard Worker constexpr byte payload[]{byte(0x00), byte(0x01), byte(0x02), byte(0x03)};
159*61c4878aSAndroid Build Coastguard Worker EncodeDecode(12, 0xdeadbeef, 0x03a82921, 33, payload, Status::Unavailable());
160*61c4878aSAndroid Build Coastguard Worker }
161*61c4878aSAndroid Build Coastguard Worker
162*61c4878aSAndroid Build Coastguard Worker FUZZ_TEST(Packet, EncodeDecode)
163*61c4878aSAndroid Build Coastguard Worker .WithDomains(NonZero<uint32_t>(),
164*61c4878aSAndroid Build Coastguard Worker NonZero<uint32_t>(),
165*61c4878aSAndroid Build Coastguard Worker NonZero<uint32_t>(),
166*61c4878aSAndroid Build Coastguard Worker NonZero<uint32_t>(),
167*61c4878aSAndroid Build Coastguard Worker VectorOf<100>(Arbitrary<byte>()),
168*61c4878aSAndroid Build Coastguard Worker Arbitrary<Status>());
169*61c4878aSAndroid Build Coastguard Worker
170*61c4878aSAndroid Build Coastguard Worker constexpr size_t kReservedSize = 2 /* type */ + 2 /* channel */ +
171*61c4878aSAndroid Build Coastguard Worker 5 /* service */ + 5 /* method */ +
172*61c4878aSAndroid Build Coastguard Worker 2 /* payload key */ + 2 /* status */;
173*61c4878aSAndroid Build Coastguard Worker
TEST(Packet,PayloadUsableSpace_ExactFit)174*61c4878aSAndroid Build Coastguard Worker TEST(Packet, PayloadUsableSpace_ExactFit) {
175*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(
176*61c4878aSAndroid Build Coastguard Worker kReservedSize,
177*61c4878aSAndroid Build Coastguard Worker Packet(PacketType::RESPONSE, 1, 42, 100, 28282).MinEncodedSizeBytes());
178*61c4878aSAndroid Build Coastguard Worker }
179*61c4878aSAndroid Build Coastguard Worker
TEST(Packet,PayloadUsableSpace_LargerVarints)180*61c4878aSAndroid Build Coastguard Worker TEST(Packet, PayloadUsableSpace_LargerVarints) {
181*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(kReservedSize + 2 /* channel */, // service and method are Fixed32
182*61c4878aSAndroid Build Coastguard Worker Packet(PacketType::RESPONSE, 17000, 200, 200, 28282)
183*61c4878aSAndroid Build Coastguard Worker .MinEncodedSizeBytes());
184*61c4878aSAndroid Build Coastguard Worker }
185*61c4878aSAndroid Build Coastguard Worker
186*61c4878aSAndroid Build Coastguard Worker } // namespace
187*61c4878aSAndroid Build Coastguard Worker } // namespace pw::rpc::internal
188