xref: /aosp_15_r20/external/pigweed/pw_hdlc/encoder_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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_hdlc/encoder.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <algorithm>
18*61c4878aSAndroid Build Coastguard Worker #include <array>
19*61c4878aSAndroid Build Coastguard Worker #include <cstddef>
20*61c4878aSAndroid Build Coastguard Worker #include <cstring>
21*61c4878aSAndroid Build Coastguard Worker 
22*61c4878aSAndroid Build Coastguard Worker #include "pw_bytes/array.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_hdlc/encoded_size.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_hdlc/internal/protocol.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_stream/memory_stream.h"
26*61c4878aSAndroid Build Coastguard Worker #include "pw_unit_test/framework.h"
27*61c4878aSAndroid Build Coastguard Worker 
28*61c4878aSAndroid Build Coastguard Worker using std::byte;
29*61c4878aSAndroid Build Coastguard Worker 
30*61c4878aSAndroid Build Coastguard Worker namespace pw::hdlc {
31*61c4878aSAndroid Build Coastguard Worker namespace {
32*61c4878aSAndroid Build Coastguard Worker 
33*61c4878aSAndroid Build Coastguard Worker constexpr uint8_t kAddress = 0x7B;  // 123
34*61c4878aSAndroid Build Coastguard Worker constexpr uint8_t kEncodedAddress = (kAddress << 1) | 1;
35*61c4878aSAndroid Build Coastguard Worker 
36*61c4878aSAndroid Build Coastguard Worker #define EXPECT_ENCODER_WROTE(...)                                           \
37*61c4878aSAndroid Build Coastguard Worker   do {                                                                      \
38*61c4878aSAndroid Build Coastguard Worker     constexpr auto expected_data = (__VA_ARGS__);                           \
39*61c4878aSAndroid Build Coastguard Worker     EXPECT_EQ(writer_.bytes_written(), expected_data.size());               \
40*61c4878aSAndroid Build Coastguard Worker     EXPECT_EQ(                                                              \
41*61c4878aSAndroid Build Coastguard Worker         std::memcmp(                                                        \
42*61c4878aSAndroid Build Coastguard Worker             writer_.data(), expected_data.data(), writer_.bytes_written()), \
43*61c4878aSAndroid Build Coastguard Worker         0);                                                                 \
44*61c4878aSAndroid Build Coastguard Worker   } while (0)
45*61c4878aSAndroid Build Coastguard Worker 
46*61c4878aSAndroid Build Coastguard Worker class WriteUnnumberedFrame : public ::testing::Test {
47*61c4878aSAndroid Build Coastguard Worker  protected:
WriteUnnumberedFrame()48*61c4878aSAndroid Build Coastguard Worker   WriteUnnumberedFrame() : buffer_{}, writer_(buffer_) {}
49*61c4878aSAndroid Build Coastguard Worker 
50*61c4878aSAndroid Build Coastguard Worker   // Allocate a buffer that will fit any 7-byte payload.
51*61c4878aSAndroid Build Coastguard Worker   std::array<byte, MaxEncodedFrameSize(7)> buffer_;
52*61c4878aSAndroid Build Coastguard Worker   stream::MemoryWriter writer_;
53*61c4878aSAndroid Build Coastguard Worker };
54*61c4878aSAndroid Build Coastguard Worker 
55*61c4878aSAndroid Build Coastguard Worker constexpr byte kUnnumberedControl = byte{0x3};
56*61c4878aSAndroid Build Coastguard Worker 
TEST_F(WriteUnnumberedFrame,EmptyPayload)57*61c4878aSAndroid Build Coastguard Worker TEST_F(WriteUnnumberedFrame, EmptyPayload) {
58*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, span<byte>(), writer_));
59*61c4878aSAndroid Build Coastguard Worker   EXPECT_ENCODER_WROTE(bytes::Concat(
60*61c4878aSAndroid Build Coastguard Worker       kFlag, kEncodedAddress, kUnnumberedControl, uint32_t{0x832d343f}, kFlag));
61*61c4878aSAndroid Build Coastguard Worker }
62*61c4878aSAndroid Build Coastguard Worker 
TEST_F(WriteUnnumberedFrame,OneBytePayload)63*61c4878aSAndroid Build Coastguard Worker TEST_F(WriteUnnumberedFrame, OneBytePayload) {
64*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::String("A"), writer_));
65*61c4878aSAndroid Build Coastguard Worker   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
66*61c4878aSAndroid Build Coastguard Worker                                      kEncodedAddress,
67*61c4878aSAndroid Build Coastguard Worker                                      kUnnumberedControl,
68*61c4878aSAndroid Build Coastguard Worker                                      'A',
69*61c4878aSAndroid Build Coastguard Worker                                      uint32_t{0x653c9e82},
70*61c4878aSAndroid Build Coastguard Worker                                      kFlag));
71*61c4878aSAndroid Build Coastguard Worker }
72*61c4878aSAndroid Build Coastguard Worker 
TEST_F(WriteUnnumberedFrame,OneBytePayload_Escape0x7d)73*61c4878aSAndroid Build Coastguard Worker TEST_F(WriteUnnumberedFrame, OneBytePayload_Escape0x7d) {
74*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::Array<0x7d>(), writer_));
75*61c4878aSAndroid Build Coastguard Worker   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
76*61c4878aSAndroid Build Coastguard Worker                                      kEncodedAddress,
77*61c4878aSAndroid Build Coastguard Worker                                      kUnnumberedControl,
78*61c4878aSAndroid Build Coastguard Worker                                      kEscape,
79*61c4878aSAndroid Build Coastguard Worker                                      byte{0x7d} ^ byte{0x20},
80*61c4878aSAndroid Build Coastguard Worker                                      uint32_t{0x4a53e205},
81*61c4878aSAndroid Build Coastguard Worker                                      kFlag));
82*61c4878aSAndroid Build Coastguard Worker }
83*61c4878aSAndroid Build Coastguard Worker 
TEST_F(WriteUnnumberedFrame,OneBytePayload_Escape0x7E)84*61c4878aSAndroid Build Coastguard Worker TEST_F(WriteUnnumberedFrame, OneBytePayload_Escape0x7E) {
85*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::Array<0x7e>(), writer_));
86*61c4878aSAndroid Build Coastguard Worker   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
87*61c4878aSAndroid Build Coastguard Worker                                      kEncodedAddress,
88*61c4878aSAndroid Build Coastguard Worker                                      kUnnumberedControl,
89*61c4878aSAndroid Build Coastguard Worker                                      kEscape,
90*61c4878aSAndroid Build Coastguard Worker                                      byte{0x7e} ^ byte{0x20},
91*61c4878aSAndroid Build Coastguard Worker                                      uint32_t{0xd35ab3bf},
92*61c4878aSAndroid Build Coastguard Worker                                      kFlag));
93*61c4878aSAndroid Build Coastguard Worker }
94*61c4878aSAndroid Build Coastguard Worker 
TEST_F(WriteUnnumberedFrame,AddressNeedsEscaping)95*61c4878aSAndroid Build Coastguard Worker TEST_F(WriteUnnumberedFrame, AddressNeedsEscaping) {
96*61c4878aSAndroid Build Coastguard Worker   // Becomes 0x7d when encoded.
97*61c4878aSAndroid Build Coastguard Worker   constexpr uint8_t kEscapeRequiredAddress = 0x7d >> 1;
98*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(),
99*61c4878aSAndroid Build Coastguard Worker             WriteUIFrame(kEscapeRequiredAddress, bytes::String("A"), writer_));
100*61c4878aSAndroid Build Coastguard Worker   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
101*61c4878aSAndroid Build Coastguard Worker                                      kEscape,
102*61c4878aSAndroid Build Coastguard Worker                                      byte{0x5d},
103*61c4878aSAndroid Build Coastguard Worker                                      kUnnumberedControl,
104*61c4878aSAndroid Build Coastguard Worker                                      'A',
105*61c4878aSAndroid Build Coastguard Worker                                      uint32_t{0x899E00D4},
106*61c4878aSAndroid Build Coastguard Worker                                      kFlag));
107*61c4878aSAndroid Build Coastguard Worker }
108*61c4878aSAndroid Build Coastguard Worker 
TEST_F(WriteUnnumberedFrame,Crc32NeedsEscaping)109*61c4878aSAndroid Build Coastguard Worker TEST_F(WriteUnnumberedFrame, Crc32NeedsEscaping) {
110*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::String("aa"), writer_));
111*61c4878aSAndroid Build Coastguard Worker 
112*61c4878aSAndroid Build Coastguard Worker   // The CRC-32 of {kEncodedAddress, kUnnumberedControl, "aa"} is 0x7ee04473, so
113*61c4878aSAndroid Build Coastguard Worker   // the 0x7e must be escaped.
114*61c4878aSAndroid Build Coastguard Worker   constexpr auto expected_crc32 = bytes::Array<0x73, 0x44, 0xe0, 0x7d, 0x5e>();
115*61c4878aSAndroid Build Coastguard Worker   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
116*61c4878aSAndroid Build Coastguard Worker                                      kEncodedAddress,
117*61c4878aSAndroid Build Coastguard Worker                                      kUnnumberedControl,
118*61c4878aSAndroid Build Coastguard Worker                                      bytes::String("aa"),
119*61c4878aSAndroid Build Coastguard Worker                                      expected_crc32,
120*61c4878aSAndroid Build Coastguard Worker                                      kFlag));
121*61c4878aSAndroid Build Coastguard Worker }
122*61c4878aSAndroid Build Coastguard Worker 
TEST_F(WriteUnnumberedFrame,MultiplePayloads)123*61c4878aSAndroid Build Coastguard Worker TEST_F(WriteUnnumberedFrame, MultiplePayloads) {
124*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::String("ABC"), writer_));
125*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::String("DEF"), writer_));
126*61c4878aSAndroid Build Coastguard Worker   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
127*61c4878aSAndroid Build Coastguard Worker                                      kEncodedAddress,
128*61c4878aSAndroid Build Coastguard Worker                                      kUnnumberedControl,
129*61c4878aSAndroid Build Coastguard Worker                                      bytes::String("ABC"),
130*61c4878aSAndroid Build Coastguard Worker                                      uint32_t{0x72410ee4},
131*61c4878aSAndroid Build Coastguard Worker                                      kFlag,
132*61c4878aSAndroid Build Coastguard Worker                                      kFlag,
133*61c4878aSAndroid Build Coastguard Worker                                      kEncodedAddress,
134*61c4878aSAndroid Build Coastguard Worker                                      kUnnumberedControl,
135*61c4878aSAndroid Build Coastguard Worker                                      bytes::String("DEF"),
136*61c4878aSAndroid Build Coastguard Worker                                      uint32_t{0x4ba1ae47},
137*61c4878aSAndroid Build Coastguard Worker                                      kFlag));
138*61c4878aSAndroid Build Coastguard Worker }
139*61c4878aSAndroid Build Coastguard Worker 
TEST_F(WriteUnnumberedFrame,PayloadWithNoEscapes)140*61c4878aSAndroid Build Coastguard Worker TEST_F(WriteUnnumberedFrame, PayloadWithNoEscapes) {
141*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(
142*61c4878aSAndroid Build Coastguard Worker       OkStatus(),
143*61c4878aSAndroid Build Coastguard Worker       WriteUIFrame(kAddress, bytes::String("1995 toyota corolla"), writer_));
144*61c4878aSAndroid Build Coastguard Worker 
145*61c4878aSAndroid Build Coastguard Worker   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
146*61c4878aSAndroid Build Coastguard Worker                                      kEncodedAddress,
147*61c4878aSAndroid Build Coastguard Worker                                      kUnnumberedControl,
148*61c4878aSAndroid Build Coastguard Worker                                      bytes::String("1995 toyota corolla"),
149*61c4878aSAndroid Build Coastguard Worker                                      uint32_t{0x53ee911c},
150*61c4878aSAndroid Build Coastguard Worker                                      kFlag));
151*61c4878aSAndroid Build Coastguard Worker }
152*61c4878aSAndroid Build Coastguard Worker 
TEST_F(WriteUnnumberedFrame,MultibyteAddress)153*61c4878aSAndroid Build Coastguard Worker TEST_F(WriteUnnumberedFrame, MultibyteAddress) {
154*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), WriteUIFrame(0x3fff, bytes::String("abc"), writer_));
155*61c4878aSAndroid Build Coastguard Worker 
156*61c4878aSAndroid Build Coastguard Worker   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
157*61c4878aSAndroid Build Coastguard Worker                                      bytes::String("\xfe\xff"),
158*61c4878aSAndroid Build Coastguard Worker                                      kUnnumberedControl,
159*61c4878aSAndroid Build Coastguard Worker                                      bytes::String("abc"),
160*61c4878aSAndroid Build Coastguard Worker                                      uint32_t{0x8cee2978},
161*61c4878aSAndroid Build Coastguard Worker                                      kFlag));
162*61c4878aSAndroid Build Coastguard Worker }
163*61c4878aSAndroid Build Coastguard Worker 
TEST_F(WriteUnnumberedFrame,PayloadWithMultipleEscapes)164*61c4878aSAndroid Build Coastguard Worker TEST_F(WriteUnnumberedFrame, PayloadWithMultipleEscapes) {
165*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(
166*61c4878aSAndroid Build Coastguard Worker       OkStatus(),
167*61c4878aSAndroid Build Coastguard Worker       WriteUIFrame(kAddress,
168*61c4878aSAndroid Build Coastguard Worker                    bytes::Array<0x7E, 0x7B, 0x61, 0x62, 0x63, 0x7D, 0x7E>(),
169*61c4878aSAndroid Build Coastguard Worker                    writer_));
170*61c4878aSAndroid Build Coastguard Worker   EXPECT_ENCODER_WROTE(bytes::Concat(
171*61c4878aSAndroid Build Coastguard Worker       kFlag,
172*61c4878aSAndroid Build Coastguard Worker       kEncodedAddress,
173*61c4878aSAndroid Build Coastguard Worker       kUnnumberedControl,
174*61c4878aSAndroid Build Coastguard Worker       bytes::
175*61c4878aSAndroid Build Coastguard Worker           Array<0x7D, 0x5E, 0x7B, 0x61, 0x62, 0x63, 0x7D, 0x5D, 0x7D, 0x5E>(),
176*61c4878aSAndroid Build Coastguard Worker       uint32_t{0x1563a4e6},
177*61c4878aSAndroid Build Coastguard Worker       kFlag));
178*61c4878aSAndroid Build Coastguard Worker }
179*61c4878aSAndroid Build Coastguard Worker 
TEST_F(WriteUnnumberedFrame,PayloadTooLarge_WritesNothing)180*61c4878aSAndroid Build Coastguard Worker TEST_F(WriteUnnumberedFrame, PayloadTooLarge_WritesNothing) {
181*61c4878aSAndroid Build Coastguard Worker   constexpr auto data = bytes::Initialized<sizeof(buffer_)>(0x7e);
182*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), WriteUIFrame(kAddress, data, writer_));
183*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(0u, writer_.bytes_written());
184*61c4878aSAndroid Build Coastguard Worker }
185*61c4878aSAndroid Build Coastguard Worker 
186*61c4878aSAndroid Build Coastguard Worker class ErrorWriter : public stream::NonSeekableWriter {
187*61c4878aSAndroid Build Coastguard Worker  private:
DoWrite(ConstByteSpan)188*61c4878aSAndroid Build Coastguard Worker   Status DoWrite(ConstByteSpan) override { return Status::Unimplemented(); }
189*61c4878aSAndroid Build Coastguard Worker };
190*61c4878aSAndroid Build Coastguard Worker 
TEST(WriteUIFrame,WriterError)191*61c4878aSAndroid Build Coastguard Worker TEST(WriteUIFrame, WriterError) {
192*61c4878aSAndroid Build Coastguard Worker   ErrorWriter writer;
193*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::Unimplemented(),
194*61c4878aSAndroid Build Coastguard Worker             WriteUIFrame(kAddress, bytes::Array<0x01>(), writer));
195*61c4878aSAndroid Build Coastguard Worker }
196*61c4878aSAndroid Build Coastguard Worker 
197*61c4878aSAndroid Build Coastguard Worker }  // namespace
198*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::hdlc
199