1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_hdlc/encoder.h"
16 
17 #include <algorithm>
18 #include <array>
19 #include <cstddef>
20 
21 #include "gtest/gtest.h"
22 #include "pw_bytes/array.h"
23 #include "pw_hdlc/internal/encoder.h"
24 #include "pw_hdlc/internal/protocol.h"
25 #include "pw_stream/memory_stream.h"
26 
27 using std::byte;
28 
29 namespace pw::hdlc {
30 namespace {
31 
32 constexpr uint8_t kAddress = 0x7B;  // 123
33 constexpr uint8_t kEncodedAddress = (kAddress << 1) | 1;
34 
35 #define EXPECT_ENCODER_WROTE(...)                                           \
36   do {                                                                      \
37     constexpr auto expected_data = (__VA_ARGS__);                           \
38     EXPECT_EQ(writer_.bytes_written(), expected_data.size());               \
39     EXPECT_EQ(                                                              \
40         std::memcmp(                                                        \
41             writer_.data(), expected_data.data(), writer_.bytes_written()), \
42         0);                                                                 \
43   } while (0)
44 
45 class WriteUnnumberedFrame : public ::testing::Test {
46  protected:
WriteUnnumberedFrame()47   WriteUnnumberedFrame() : writer_(buffer_) {}
48 
49   stream::MemoryWriter writer_;
50   std::array<byte, 32> buffer_;
51 };
52 
53 constexpr byte kUnnumberedControl = byte{0x3};
54 
TEST_F(WriteUnnumberedFrame,EmptyPayload)55 TEST_F(WriteUnnumberedFrame, EmptyPayload) {
56   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, std::span<byte>(), writer_));
57   EXPECT_ENCODER_WROTE(bytes::Concat(
58       kFlag, kEncodedAddress, kUnnumberedControl, uint32_t{0x832d343f}, kFlag));
59 }
60 
TEST_F(WriteUnnumberedFrame,OneBytePayload)61 TEST_F(WriteUnnumberedFrame, OneBytePayload) {
62   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::String("A"), writer_));
63   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
64                                      kEncodedAddress,
65                                      kUnnumberedControl,
66                                      'A',
67                                      uint32_t{0x653c9e82},
68                                      kFlag));
69 }
70 
TEST_F(WriteUnnumberedFrame,OneBytePayload_Escape0x7d)71 TEST_F(WriteUnnumberedFrame, OneBytePayload_Escape0x7d) {
72   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::Array<0x7d>(), writer_));
73   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
74                                      kEncodedAddress,
75                                      kUnnumberedControl,
76                                      kEscape,
77                                      byte{0x7d} ^ byte{0x20},
78                                      uint32_t{0x4a53e205},
79                                      kFlag));
80 }
81 
TEST_F(WriteUnnumberedFrame,OneBytePayload_Escape0x7E)82 TEST_F(WriteUnnumberedFrame, OneBytePayload_Escape0x7E) {
83   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::Array<0x7e>(), writer_));
84   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
85                                      kEncodedAddress,
86                                      kUnnumberedControl,
87                                      kEscape,
88                                      byte{0x7e} ^ byte{0x20},
89                                      uint32_t{0xd35ab3bf},
90                                      kFlag));
91 }
92 
TEST_F(WriteUnnumberedFrame,AddressNeedsEscaping)93 TEST_F(WriteUnnumberedFrame, AddressNeedsEscaping) {
94   // Becomes 0x7d when encoded.
95   constexpr uint8_t kEscapeRequiredAddress = 0x7d >> 1;
96   ASSERT_EQ(OkStatus(),
97             WriteUIFrame(kEscapeRequiredAddress, bytes::String("A"), writer_));
98   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
99                                      kEscape,
100                                      byte{0x5d},
101                                      kUnnumberedControl,
102                                      'A',
103                                      uint32_t{0x899E00D4},
104                                      kFlag));
105 }
106 
TEST_F(WriteUnnumberedFrame,Crc32NeedsEscaping)107 TEST_F(WriteUnnumberedFrame, Crc32NeedsEscaping) {
108   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::String("aa"), writer_));
109 
110   // The CRC-32 of {kEncodedAddress, kUnnumberedControl, "aa"} is 0x7ee04473, so
111   // the 0x7e must be escaped.
112   constexpr auto expected_crc32 = bytes::Array<0x73, 0x44, 0xe0, 0x7d, 0x5e>();
113   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
114                                      kEncodedAddress,
115                                      kUnnumberedControl,
116                                      bytes::String("aa"),
117                                      expected_crc32,
118                                      kFlag));
119 }
120 
TEST_F(WriteUnnumberedFrame,MultiplePayloads)121 TEST_F(WriteUnnumberedFrame, MultiplePayloads) {
122   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::String("ABC"), writer_));
123   ASSERT_EQ(OkStatus(), WriteUIFrame(kAddress, bytes::String("DEF"), writer_));
124   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
125                                      kEncodedAddress,
126                                      kUnnumberedControl,
127                                      bytes::String("ABC"),
128                                      uint32_t{0x72410ee4},
129                                      kFlag,
130                                      kFlag,
131                                      kEncodedAddress,
132                                      kUnnumberedControl,
133                                      bytes::String("DEF"),
134                                      uint32_t{0x4ba1ae47},
135                                      kFlag));
136 }
137 
TEST_F(WriteUnnumberedFrame,PayloadWithNoEscapes)138 TEST_F(WriteUnnumberedFrame, PayloadWithNoEscapes) {
139   ASSERT_EQ(
140       OkStatus(),
141       WriteUIFrame(kAddress, bytes::String("1995 toyota corolla"), writer_));
142 
143   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
144                                      kEncodedAddress,
145                                      kUnnumberedControl,
146                                      bytes::String("1995 toyota corolla"),
147                                      uint32_t{0x53ee911c},
148                                      kFlag));
149 }
150 
TEST_F(WriteUnnumberedFrame,MultibyteAddress)151 TEST_F(WriteUnnumberedFrame, MultibyteAddress) {
152   ASSERT_EQ(OkStatus(), WriteUIFrame(0x3fff, bytes::String("abc"), writer_));
153 
154   EXPECT_ENCODER_WROTE(bytes::Concat(kFlag,
155                                      bytes::String("\xfe\xff"),
156                                      kUnnumberedControl,
157                                      bytes::String("abc"),
158                                      uint32_t{0x8cee2978},
159                                      kFlag));
160 }
161 
TEST_F(WriteUnnumberedFrame,PayloadWithMultipleEscapes)162 TEST_F(WriteUnnumberedFrame, PayloadWithMultipleEscapes) {
163   ASSERT_EQ(
164       OkStatus(),
165       WriteUIFrame(kAddress,
166                    bytes::Array<0x7E, 0x7B, 0x61, 0x62, 0x63, 0x7D, 0x7E>(),
167                    writer_));
168   EXPECT_ENCODER_WROTE(bytes::Concat(
169       kFlag,
170       kEncodedAddress,
171       kUnnumberedControl,
172       bytes::
173           Array<0x7D, 0x5E, 0x7B, 0x61, 0x62, 0x63, 0x7D, 0x5D, 0x7D, 0x5E>(),
174       uint32_t{0x1563a4e6},
175       kFlag));
176 }
177 
TEST_F(WriteUnnumberedFrame,PayloadTooLarge_WritesNothing)178 TEST_F(WriteUnnumberedFrame, PayloadTooLarge_WritesNothing) {
179   constexpr auto data = bytes::Initialized<sizeof(buffer_)>(0x7e);
180   EXPECT_EQ(Status::ResourceExhausted(), WriteUIFrame(kAddress, data, writer_));
181   EXPECT_EQ(0u, writer_.bytes_written());
182 }
183 
184 class ErrorWriter : public stream::Writer {
185  private:
DoWrite(ConstByteSpan)186   Status DoWrite(ConstByteSpan) override { return Status::Unimplemented(); }
187 };
188 
TEST(WriteUnnumberedFrame,WriterError)189 TEST(WriteUnnumberedFrame, WriterError) {
190   ErrorWriter writer;
191   EXPECT_EQ(Status::Unimplemented(),
192             WriteUIFrame(kAddress, bytes::Array<0x01>(), writer));
193 }
194 
195 }  // namespace
196 
197 namespace internal {
198 namespace {
199 
200 constexpr uint8_t kEscapeAddress = 0x7d;
201 
TEST(Encoder,MaxEncodedSize_EmptyPayload)202 TEST(Encoder, MaxEncodedSize_EmptyPayload) {
203   EXPECT_EQ(11u, Encoder::MaxEncodedSize(kAddress, {}));
204   EXPECT_EQ(11u, Encoder::MaxEncodedSize(kEscapeAddress, {}));
205 }
206 
TEST(Encoder,MaxEncodedSize_PayloadWithoutEscapes)207 TEST(Encoder, MaxEncodedSize_PayloadWithoutEscapes) {
208   constexpr auto data = bytes::Array<0x00, 0x01, 0x02, 0x03>();
209   EXPECT_EQ(15u, Encoder::MaxEncodedSize(kAddress, data));
210   EXPECT_EQ(15u, Encoder::MaxEncodedSize(kEscapeAddress, data));
211 }
212 
TEST(Encoder,MaxEncodedSize_PayloadWithOneEscape)213 TEST(Encoder, MaxEncodedSize_PayloadWithOneEscape) {
214   constexpr auto data = bytes::Array<0x00, 0x01, 0x7e, 0x03>();
215   EXPECT_EQ(16u, Encoder::MaxEncodedSize(kAddress, data));
216   EXPECT_EQ(16u, Encoder::MaxEncodedSize(kEscapeAddress, data));
217 }
218 
TEST(Encoder,MaxEncodedSize_PayloadWithAllEscapes)219 TEST(Encoder, MaxEncodedSize_PayloadWithAllEscapes) {
220   constexpr auto data = bytes::Initialized<8>(0x7e);
221   EXPECT_EQ(27u, Encoder::MaxEncodedSize(kAddress, data));
222   EXPECT_EQ(27u, Encoder::MaxEncodedSize(kEscapeAddress, data));
223 }
224 
225 }  // namespace
226 }  // namespace internal
227 }  // namespace pw::hdlc
228