• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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