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