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