1 // Copyright 2024 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/router.h"
16
17 #include "pw_allocator/testing.h"
18 #include "pw_async2/pend_func_task.h"
19 #include "pw_bytes/suffix.h"
20 #include "pw_channel/forwarding_channel.h"
21 #include "pw_channel/loopback_channel.h"
22 #include "pw_containers/inline_queue.h"
23 #include "pw_containers/vector.h"
24 #include "pw_multibuf/simple_allocator.h"
25
26 namespace pw::hdlc {
27 namespace {
28
29 using ::pw::allocator::test::AllocatorForTest;
30 using ::pw::async2::Context;
31 using ::pw::async2::Dispatcher;
32 using ::pw::async2::PendFuncTask;
33 using ::pw::async2::Pending;
34 using ::pw::async2::Poll;
35 using ::pw::async2::Ready;
36 using ::pw::async2::Task;
37 using ::pw::async2::Waker;
38 using ::pw::operator""_b;
39 using ::pw::channel::DatagramReader;
40 using ::pw::channel::DatagramWriter;
41 using ::pw::channel::ForwardingByteChannelPair;
42 using ::pw::channel::ForwardingDatagramChannelPair;
43 using ::pw::channel::LoopbackByteChannel;
44 using ::pw::multibuf::MultiBuf;
45 using ::pw::multibuf::MultiBufAllocator;
46 using ::pw::multibuf::SimpleAllocator;
47
48 class SimpleAllocatorForTest {
49 public:
SimpleAllocatorForTest()50 SimpleAllocatorForTest() : simple_allocator_(data_area_, meta_alloc_) {}
operator *()51 MultiBufAllocator& operator*() { return simple_allocator_; }
operator ->()52 MultiBufAllocator* operator->() { return &simple_allocator_; }
53
54 private:
55 static constexpr size_t kArbitraryDataSize = 256;
56 static constexpr size_t kArbitraryMetaSize = 2048;
57 std::array<std::byte, kArbitraryDataSize> data_area_;
58 AllocatorForTest<kArbitraryMetaSize> meta_alloc_;
59 SimpleAllocator simple_allocator_;
60 };
61
62 class SendDatagrams : public Task {
63 public:
SendDatagrams(pw::InlineQueue<MultiBuf> & to_send,DatagramWriter & channel)64 SendDatagrams(pw::InlineQueue<MultiBuf>& to_send, DatagramWriter& channel)
65 : to_send_(to_send), channel_(channel) {}
66
67 private:
DoPend(Context & cx)68 Poll<> DoPend(Context& cx) final {
69 while (!to_send_.empty()) {
70 if (channel_.PendReadyToWrite(cx).IsPending()) {
71 return Pending();
72 }
73 PW_TEST_EXPECT_OK(channel_.StageWrite(std::move(to_send_.front())));
74 to_send_.pop();
75 }
76 return Ready();
77 }
78
79 pw::InlineQueue<MultiBuf>& to_send_;
80 DatagramWriter& channel_;
81 };
82
83 static constexpr size_t kMaxReceiveDatagrams = 16;
84 class ReceiveDatagramsUntilClosed : public Task {
85 public:
ReceiveDatagramsUntilClosed(DatagramReader & channel)86 ReceiveDatagramsUntilClosed(DatagramReader& channel) : channel_(channel) {}
87
88 pw::Vector<MultiBuf, kMaxReceiveDatagrams> received;
89
90 private:
DoPend(Context & cx)91 Poll<> DoPend(Context& cx) final {
92 while (true) {
93 Poll<Result<MultiBuf>> result = channel_.PendRead(cx);
94 if (result.IsPending()) {
95 return Pending();
96 }
97 if (!result->ok()) {
98 EXPECT_EQ(result->status(), pw::Status::FailedPrecondition());
99 return Ready();
100 }
101 received.push_back(std::move(**result));
102 }
103 // Unreachable.
104 return Ready();
105 }
106
107 DatagramReader& channel_;
108 };
109
110 template <typename ActualIterable, typename ExpectedIterable>
ExpectElementsEqual(const ActualIterable & actual,const ExpectedIterable & expected)111 void ExpectElementsEqual(const ActualIterable& actual,
112 const ExpectedIterable& expected) {
113 auto actual_iter = actual.begin();
114 auto expected_iter = expected.begin();
115 for (; expected_iter != expected.end(); ++actual_iter, ++expected_iter) {
116 ASSERT_NE(actual_iter, actual.end());
117 EXPECT_EQ(*actual_iter, *expected_iter);
118 }
119 }
120
121 template <typename ActualIterable, typename T>
ExpectElementsEqual(const ActualIterable & actual,std::initializer_list<T> expected)122 void ExpectElementsEqual(const ActualIterable& actual,
123 std::initializer_list<T> expected) {
124 ExpectElementsEqual<ActualIterable, std::initializer_list<T>>(actual,
125 expected);
126 }
127
128 // TODO: b/331285977 - Fuzz test this function.
ExpectSendAndReceive(std::initializer_list<std::initializer_list<std::byte>> data)129 void ExpectSendAndReceive(
130 std::initializer_list<std::initializer_list<std::byte>> data) {
131 SimpleAllocatorForTest alloc;
132
133 LoopbackByteChannel io_loopback(*alloc);
134 ForwardingDatagramChannelPair outgoing_pair(*alloc, *alloc);
135 ForwardingDatagramChannelPair incoming_pair(*alloc, *alloc);
136
137 static constexpr size_t kMaxSendDatagrams = 16;
138 ASSERT_LE(data.size(), kMaxSendDatagrams);
139
140 pw::InlineQueue<MultiBuf, kMaxSendDatagrams> datagrams_to_send;
141 for (size_t i = 0; i < data.size(); i++) {
142 std::optional<MultiBuf> buf = alloc->Allocate(std::data(data)[i].size());
143 ASSERT_TRUE(buf.has_value());
144 std::copy(
145 std::data(data)[i].begin(), std::data(data)[i].end(), buf->begin());
146 datagrams_to_send.push(std::move(*buf));
147 }
148
149 static constexpr uint64_t kAddress = 27;
150 static constexpr uint64_t kArbitraryAddressOne = 13802183;
151 static constexpr uint64_t kArbitraryAddressTwo = 4284900;
152 static constexpr size_t kDecodeBufferSize = 256;
153
154 std::array<std::byte, kDecodeBufferSize> decode_buffer;
155 Router router(io_loopback.channel(), decode_buffer);
156 PendFuncTask router_task([&router](Context& cx) { return router.Pend(cx); });
157
158 SendDatagrams send_task(datagrams_to_send, outgoing_pair.first());
159 ReceiveDatagramsUntilClosed recv_task(incoming_pair.first());
160
161 EXPECT_EQ(
162 router.AddChannel(outgoing_pair.second(), kArbitraryAddressOne, kAddress),
163 OkStatus());
164 EXPECT_EQ(
165 router.AddChannel(incoming_pair.second(), kAddress, kArbitraryAddressTwo),
166 OkStatus());
167
168 Dispatcher dispatcher;
169 dispatcher.Post(router_task);
170 dispatcher.Post(send_task);
171 dispatcher.Post(recv_task);
172
173 EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
174 ASSERT_EQ(recv_task.received.size(), data.size());
175 for (size_t i = 0; i < data.size(); i++) {
176 ExpectElementsEqual(recv_task.received[i], std::data(data)[i]);
177 }
178 }
179
TEST(Router,SendsAndReceivesSingleDatagram)180 TEST(Router, SendsAndReceivesSingleDatagram) {
181 ExpectSendAndReceive({{2_b, 4_b, 6_b, 0_b, 1_b}});
182 }
183
TEST(Router,SendsAndReceivesMultipleDatagrams)184 TEST(Router, SendsAndReceivesMultipleDatagrams) {
185 ExpectSendAndReceive({
186 {1_b, 3_b, 5_b},
187 {2_b, 4_b, 6_b, 7_b},
188 });
189 }
190
TEST(Router,SendsAndReceivesReservedBytes)191 TEST(Router, SendsAndReceivesReservedBytes) {
192 ExpectSendAndReceive({
193 // Control octets.
194 {0x7D_b},
195 {0x7E_b},
196 {0x7D_b, 0x7E_b},
197 {0x7D_b, 0x5E_b},
198 // XON / XOFF
199 {0x13_b},
200 {0x11_b},
201 });
202 }
203
TEST(Router,PendOnClosedIoChannelReturnsReady)204 TEST(Router, PendOnClosedIoChannelReturnsReady) {
205 static constexpr size_t kDecodeBufferSize = 256;
206
207 SimpleAllocatorForTest alloc;
208
209 ForwardingByteChannelPair byte_pair(*alloc, *alloc);
210 std::array<std::byte, kDecodeBufferSize> decode_buffer;
211 Router router(byte_pair.first(), decode_buffer);
212
213 ForwardingDatagramChannelPair datagram_pair(*alloc, *alloc);
214 ReceiveDatagramsUntilClosed recv_task(datagram_pair.first());
215 EXPECT_EQ(router.AddChannel(datagram_pair.second(),
216 /*arbitrary incoming address*/ 5017,
217 /*arbitrary outgoing address*/ 2019),
218 OkStatus());
219
220 PendFuncTask router_task([&router](Context& cx) { return router.Pend(cx); });
221
222 Dispatcher dispatcher;
223 dispatcher.Post(router_task);
224 dispatcher.Post(recv_task);
225
226 EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
227
228 // Close the underlying byte channel.
229 Waker null_waker;
230 Context null_cx(dispatcher, null_waker);
231 EXPECT_EQ(byte_pair.second().PendClose(null_cx), Ready(OkStatus()));
232
233 // Both the router and the receive task should complete.
234 EXPECT_EQ(dispatcher.RunUntilStalled(), Ready());
235 }
236
237 } // namespace
238 } // namespace pw::hdlc
239