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 EXPECT_EQ(channel_.Write(std::move(to_send_.front())).status(),
74 pw::OkStatus());
75 to_send_.pop();
76 }
77 return Ready();
78 }
79
80 pw::InlineQueue<MultiBuf>& to_send_;
81 DatagramWriter& channel_;
82 };
83
84 static constexpr size_t kMaxReceiveDatagrams = 16;
85 class ReceiveDatagramsUntilClosed : public Task {
86 public:
ReceiveDatagramsUntilClosed(DatagramReader & channel)87 ReceiveDatagramsUntilClosed(DatagramReader& channel) : channel_(channel) {}
88
89 pw::Vector<MultiBuf, kMaxReceiveDatagrams> received;
90
91 private:
DoPend(Context & cx)92 Poll<> DoPend(Context& cx) final {
93 while (true) {
94 Poll<Result<MultiBuf>> result = channel_.PendRead(cx);
95 if (result.IsPending()) {
96 return Pending();
97 }
98 if (!result->ok()) {
99 EXPECT_EQ(result->status(), pw::Status::FailedPrecondition());
100 return Ready();
101 }
102 received.push_back(std::move(**result));
103 }
104 // Unreachable.
105 return Ready();
106 }
107
108 DatagramReader& channel_;
109 };
110
111 template <typename ActualIterable, typename ExpectedIterable>
ExpectElementsEqual(const ActualIterable & actual,const ExpectedIterable & expected)112 void ExpectElementsEqual(const ActualIterable& actual,
113 const ExpectedIterable& expected) {
114 auto actual_iter = actual.begin();
115 auto expected_iter = expected.begin();
116 for (; expected_iter != expected.end(); ++actual_iter, ++expected_iter) {
117 ASSERT_NE(actual_iter, actual.end());
118 EXPECT_EQ(*actual_iter, *expected_iter);
119 }
120 }
121
122 template <typename ActualIterable, typename T>
ExpectElementsEqual(const ActualIterable & actual,std::initializer_list<T> expected)123 void ExpectElementsEqual(const ActualIterable& actual,
124 std::initializer_list<T> expected) {
125 ExpectElementsEqual<ActualIterable, std::initializer_list<T>>(actual,
126 expected);
127 }
128
129 // TODO: b/331285977 - Fuzz test this function.
ExpectSendAndReceive(std::initializer_list<std::initializer_list<std::byte>> data)130 void ExpectSendAndReceive(
131 std::initializer_list<std::initializer_list<std::byte>> data) {
132 SimpleAllocatorForTest alloc;
133
134 LoopbackByteChannel io_loopback(*alloc);
135 ForwardingDatagramChannelPair outgoing_pair(*alloc);
136 ForwardingDatagramChannelPair incoming_pair(*alloc);
137
138 static constexpr size_t kMaxSendDatagrams = 16;
139 ASSERT_LE(data.size(), kMaxSendDatagrams);
140
141 pw::InlineQueue<MultiBuf, kMaxSendDatagrams> datagrams_to_send;
142 for (size_t i = 0; i < data.size(); i++) {
143 std::optional<MultiBuf> buf = alloc->Allocate(std::data(data)[i].size());
144 ASSERT_TRUE(buf.has_value());
145 std::copy(
146 std::data(data)[i].begin(), std::data(data)[i].end(), buf->begin());
147 datagrams_to_send.push(std::move(*buf));
148 }
149
150 static constexpr uint64_t kAddress = 27;
151 static constexpr uint64_t kArbitraryAddressOne = 13802183;
152 static constexpr uint64_t kArbitraryAddressTwo = 4284900;
153 static constexpr size_t kDecodeBufferSize = 256;
154
155 std::array<std::byte, kDecodeBufferSize> decode_buffer;
156 Router router(io_loopback, decode_buffer);
157 PendFuncTask router_task([&router](Context& cx) { return router.Pend(cx); });
158
159 SendDatagrams send_task(datagrams_to_send, outgoing_pair.first());
160 ReceiveDatagramsUntilClosed recv_task(incoming_pair.first());
161
162 EXPECT_EQ(
163 router.AddChannel(outgoing_pair.second(), kArbitraryAddressOne, kAddress),
164 OkStatus());
165 EXPECT_EQ(
166 router.AddChannel(incoming_pair.second(), kAddress, kArbitraryAddressTwo),
167 OkStatus());
168
169 Dispatcher dispatcher;
170 dispatcher.Post(router_task);
171 dispatcher.Post(send_task);
172 dispatcher.Post(recv_task);
173
174 EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
175 ASSERT_EQ(recv_task.received.size(), data.size());
176 for (size_t i = 0; i < data.size(); i++) {
177 ExpectElementsEqual(recv_task.received[i], std::data(data)[i]);
178 }
179 }
180
TEST(Router,SendsAndReceivesSingleDatagram)181 TEST(Router, SendsAndReceivesSingleDatagram) {
182 ExpectSendAndReceive({{2_b, 4_b, 6_b, 0_b, 1_b}});
183 }
184
TEST(Router,SendsAndReceivesMultipleDatagrams)185 TEST(Router, SendsAndReceivesMultipleDatagrams) {
186 ExpectSendAndReceive({
187 {1_b, 3_b, 5_b},
188 {2_b, 4_b, 6_b, 7_b},
189 });
190 }
191
TEST(Router,SendsAndReceivesReservedBytes)192 TEST(Router, SendsAndReceivesReservedBytes) {
193 ExpectSendAndReceive({
194 // Control octets.
195 {0x7D_b},
196 {0x7E_b},
197 {0x7D_b, 0x7E_b},
198 {0x7D_b, 0x5E_b},
199 // XON / XOFF
200 {0x13_b},
201 {0x11_b},
202 });
203 }
204
TEST(Router,PendOnClosedIoChannelReturnsReady)205 TEST(Router, PendOnClosedIoChannelReturnsReady) {
206 static constexpr size_t kDecodeBufferSize = 256;
207
208 SimpleAllocatorForTest alloc;
209
210 ForwardingByteChannelPair byte_pair(*alloc);
211 std::array<std::byte, kDecodeBufferSize> decode_buffer;
212 Router router(byte_pair.first(), decode_buffer);
213
214 ForwardingDatagramChannelPair datagram_pair(*alloc);
215 ReceiveDatagramsUntilClosed recv_task(datagram_pair.first());
216 EXPECT_EQ(router.AddChannel(datagram_pair.second(),
217 /*arbitrary incoming address*/ 5017,
218 /*arbitrary outgoing address*/ 2019),
219 OkStatus());
220
221 PendFuncTask router_task([&router](Context& cx) { return router.Pend(cx); });
222
223 Dispatcher dispatcher;
224 dispatcher.Post(router_task);
225 dispatcher.Post(recv_task);
226
227 EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
228
229 // Close the underlying byte channel.
230 Waker null_waker;
231 Context null_cx(dispatcher, null_waker);
232 EXPECT_EQ(byte_pair.second().PendClose(null_cx), Ready(OkStatus()));
233
234 // Both the router and the receive task should complete.
235 EXPECT_EQ(dispatcher.RunUntilStalled(), Ready());
236 }
237
238 } // namespace
239 } // namespace pw::hdlc
240