xref: /aosp_15_r20/external/pigweed/pw_hdlc/router_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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