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