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_bluetooth_sapphire/internal/host/l2cap/credit_based_flow_control_tx_engine.h"
16 
17 #include <pw_async/fake_dispatcher_fixture.h>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
20 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_tx_channel.h"
21 #include "pw_bluetooth_sapphire/internal/host/l2cap/frame_headers.h"
22 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
23 #include "pw_unit_test/framework.h"
24 
25 namespace bt::l2cap::internal {
26 namespace {
27 
28 using Engine = CreditBasedFlowControlTxEngine;
29 
30 class CreditBasedFlowControlTxEngineTest : public ::testing::Test {
31  protected:
32   static constexpr ChannelId kTestChannelId = 170u;
33   static constexpr auto kTestMtu = 256u;
34   static constexpr auto kTestMps = 64u;
35   static constexpr auto kInitialCredits = 1u;
36 
SetUp()37   void SetUp() override {
38     channel_.HandleSendFrame(
39         [this](ByteBufferPtr pdu) { sent_frames().push_back(std::move(pdu)); });
40   }
41 
engine()42   Engine& engine() { return *engine_; }
sent_frames()43   std::vector<ByteBufferPtr>& sent_frames() { return sent_frames_; }
channel()44   FakeTxChannel& channel() { return channel_; }
45 
ProcessSdu(ByteBufferPtr sdu)46   void ProcessSdu(ByteBufferPtr sdu) {
47     channel().QueueSdu(std::move(sdu));
48     engine().NotifySduQueued();
49   }
50 
51  private:
52   std::unique_ptr<Engine> engine_ = std::make_unique<Engine>(
53       kTestChannelId,
54       kTestMtu,
55       channel_,
56       CreditBasedFlowControlMode::kLeCreditBasedFlowControl,
57       kTestMps,
58       kInitialCredits);
59 
60   std::vector<ByteBufferPtr> sent_frames_{};
61 
62   FakeTxChannel channel_{};
63 };
64 
TEST_F(CreditBasedFlowControlTxEngineTest,SendBasicPayload)65 TEST_F(CreditBasedFlowControlTxEngineTest, SendBasicPayload) {
66   StaticByteBuffer<4> basic{'t', 'e', 's', 't'};
67   ProcessSdu(std::make_unique<DynamicByteBuffer>(basic));
68 
69   ASSERT_EQ(sent_frames().size(), 1u);
70   auto& sent = sent_frames()[0];
71 
72   ASSERT_TRUE(sent);
73   EXPECT_EQ(sent->size(), 10u);
74   EXPECT_EQ(channel().queue_size(), 0u);
75 
76   // clang-format off: Formatter wants each value on a separate line.
77   StaticByteBuffer<10> expected{
78       // PDU size field (LE u16)
79       6, 0,
80       // Channel field (LE u16)
81       kTestChannelId, 0,
82       // SDU size field (LE u16)
83       4, 0,
84       // Payload
85       't', 'e', 's', 't',
86   };
87   // clang-format on
88 
89   EXPECT_TRUE(ContainersEqual(*sent, expected));
90 }
91 
TEST_F(CreditBasedFlowControlTxEngineTest,SendSegmentedPayload)92 TEST_F(CreditBasedFlowControlTxEngineTest, SendSegmentedPayload) {
93   StaticByteBuffer<72> segmented{
94       'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
95       'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f',
96       'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e',
97       'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd',
98       'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
99   };
100 
101   // Make sure credits are available to send the entire payload.
102   EXPECT_TRUE(engine().AddCredits(5));
103   ProcessSdu(std::make_unique<DynamicByteBuffer>(segmented));
104   EXPECT_EQ(channel().queue_size(), 0u);
105 
106   ASSERT_EQ(sent_frames().size(), 2u);
107   auto& sent_first = sent_frames()[0];
108   auto& sent_second = sent_frames()[1];
109 
110   ASSERT_TRUE(sent_first);
111   EXPECT_EQ(sent_first->size(), kTestMps);
112 
113   // clang-format off: Formatter wants each value on a separate line.
114   StaticByteBuffer<kTestMps> expected_first{
115       // PDU size field (LE u16)
116       (kTestMps - 4), 0,
117       // Channel field (LE u16)
118       kTestChannelId, 0,
119       // SDU size field (LE u16)
120       72, 0,
121       // Payload
122       'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
123       'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f',
124       'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e',
125       'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b',
126   };
127   // clang-format on
128 
129   ASSERT_TRUE(sent_second);
130   EXPECT_EQ(sent_second->size(), 18u);
131 
132   // clang-format off: Formatter wants each value on a separate line.
133   StaticByteBuffer<18> expected_second{
134       // PDU size field (LE u16)
135       14, 0,
136       // Channel field (LE u16)
137       kTestChannelId, 0,
138       // Payload
139       'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
140   };
141   // clang-format on
142 
143   EXPECT_TRUE(ContainersEqual(*sent_first, expected_first));
144   EXPECT_TRUE(ContainersEqual(*sent_second, expected_second));
145 }
146 
TEST_F(CreditBasedFlowControlTxEngineTest,NoSendWithoutCreditsBasic)147 TEST_F(CreditBasedFlowControlTxEngineTest, NoSendWithoutCreditsBasic) {
148   StaticByteBuffer<5> first{'f', 'i', 'r', 's', 't'};
149   StaticByteBuffer<6> second{'s', 'e', 'c', 'o', 'n', 'd'};
150 
151   ProcessSdu(std::make_unique<DynamicByteBuffer>(first));
152 
153   ASSERT_EQ(sent_frames().size(), 1u);
154   auto& sent_first = sent_frames()[0];
155 
156   ASSERT_TRUE(sent_first);
157   EXPECT_EQ(sent_first->size(), 11u);
158 
159   // clang-format off: Formatter wants each value on a separate line.
160   StaticByteBuffer<11> expected_first{
161       // PDU size field (LE u16)
162       7, 0,
163       // Channel field (LE u16)
164       kTestChannelId, 0,
165       // SDU size field (LE u16)
166       5, 0,
167       // Payload
168       'f', 'i', 'r', 's', 't',
169   };
170   // clang-format on
171 
172   EXPECT_TRUE(ContainersEqual(*sent_first, expected_first));
173   EXPECT_EQ(engine().credits(), 0);
174   EXPECT_EQ(engine().segments_count(), 0u);
175   EXPECT_EQ(channel().queue_size(), 0u);
176 
177   ProcessSdu(std::make_unique<DynamicByteBuffer>(second));
178 
179   // Ensure the second send did not occur yet, as credits are exhausted.
180   EXPECT_EQ(engine().credits(), 0);
181   EXPECT_EQ(engine().segments_count(), 0u);
182   EXPECT_EQ(sent_frames().size(), 1u);
183   EXPECT_EQ(channel().queue_size(), 1u);
184 
185   engine().AddCredits(1);
186 
187   // Now confirm the send did occur.
188   EXPECT_EQ(engine().credits(), 0);
189   EXPECT_EQ(engine().segments_count(), 0u);
190   EXPECT_EQ(channel().queue_size(), 0u);
191   ASSERT_EQ(sent_frames().size(), 2u);
192   auto& sent_second = sent_frames()[1];
193   ASSERT_TRUE(sent_second);
194   EXPECT_EQ(sent_second->size(), 12u);
195 
196   // clang-format off: Formatter wants each value on a separate line.
197   StaticByteBuffer<12> expected_second{
198       // PDU size field (LE u16)
199       8, 0,
200       // Channel field (LE u16)
201       170, 0,
202       // SDU size field (LE u16)
203       6, 0,
204       // Payload
205       's', 'e', 'c', 'o', 'n', 'd',
206   };
207   // clang-format on
208 
209   EXPECT_TRUE(ContainersEqual(*sent_second, expected_second));
210 }
211 
TEST_F(CreditBasedFlowControlTxEngineTest,NoSendWithoutCreditsSegmented)212 TEST_F(CreditBasedFlowControlTxEngineTest, NoSendWithoutCreditsSegmented) {
213   StaticByteBuffer<150> segmented{
214       'L', 'o', 'r', 'e', 'm', ' ', 'i', 'p', 's', 'u', 'm', ' ', 'd', 'o',
215       'l', 'o', 'r', ' ', 's', 'i', 't', ' ', 'a', 'm', 'e', 't', ',', ' ',
216       'c', 'o', 'n', 's', 'e', 'c', 't', 'e', 't', 'u', 'r', ' ', 'a', 'd',
217       'i', 'p', 'i', 's', 'c', 'i', 'n', 'g', ' ', 'e', 'l', 'i', 't', '.',
218       ' ', 'S', 'e', 'd', ' ', 'e', 't', ' ', 'v', 'e', 'h', 'i', 'c', 'u',
219       'l', 'a', ' ', 'e', 'n', 'i', 'm', '.', ' ', 'U', 't', ' ', 's', 'i',
220       't', ' ', 'a', 'm', 'e', 't', ' ', 'm', 'a', 'g', 'n', 'a', ' ', 'm',
221       'a', 'u', 'r', 'i', 's', '.', ' ', 'U', 't', ' ', 's', 'e', 'd', ' ',
222       't', 'u', 'r', 'p', 'i', 's', ' ', 'n', 'i', 'b', 'h', '.', ' ', 'V',
223       'e', 's', 't', 'i', 'b', 'u', 'l', 'u', 'm', ' ', 's', 'e', 'd', ' ',
224       't', 'o', 'r', 't', 'o', 'r', ' ', 'i', 'd', '.'};
225 
226   ProcessSdu(std::make_unique<DynamicByteBuffer>(segmented));
227   EXPECT_EQ(engine().credits(), 0);
228   EXPECT_EQ(engine().segments_count(), 2u);
229   EXPECT_EQ(channel().queue_size(), 0u);
230 
231   ASSERT_EQ(sent_frames().size(), 1u);
232   auto& sent_first = sent_frames()[0];
233   ASSERT_TRUE(sent_first);
234   EXPECT_EQ(sent_first->size(), kTestMps);
235 
236   // clang-format off: Formatter wants each value on a separate line.
237   StaticByteBuffer<kTestMps> expected_first{
238       // PDU size field (LE u16)
239       (kTestMps - 4), 0,
240       // Channel field (LE u16)
241       170, 0,
242       // SDU size field (LE u16)
243       150, 0,
244       // Payload
245       'L', 'o', 'r', 'e', 'm', ' ', 'i', 'p', 's', 'u', 'm', ' ', 'd', 'o', 'l',
246       'o', 'r', ' ', 's', 'i', 't', ' ', 'a', 'm', 'e', 't', ',', ' ', 'c', 'o',
247       'n', 's', 'e', 'c', 't', 'e', 't', 'u', 'r', ' ', 'a', 'd', 'i', 'p', 'i',
248       's', 'c', 'i', 'n', 'g', ' ', 'e', 'l', 'i', 't', '.', ' ', 'S',
249   };
250   // clang-format on
251 
252   EXPECT_TRUE(ContainersEqual(*sent_first, expected_first));
253 
254   engine().AddCredits(1);
255 
256   ASSERT_EQ(sent_frames().size(), 2u);
257   auto& sent_second = sent_frames()[1];
258   ASSERT_TRUE(sent_second);
259   EXPECT_EQ(sent_second->size(), kTestMps);
260   EXPECT_EQ(engine().credits(), 0);
261   EXPECT_EQ(engine().segments_count(), 1u);
262 
263   // clang-format off: Formatter wants each value on a separate line.
264   StaticByteBuffer<kTestMps> expected_second{
265       // PDU size field (LE u16)
266       (kTestMps - 4), 0,
267       // Channel field (LE u16)
268       170, 0,
269       // Payload
270       'e', 'd', ' ', 'e', 't', ' ', 'v', 'e', 'h', 'i', 'c', 'u', 'l', 'a', ' ',
271       'e', 'n', 'i', 'm', '.', ' ', 'U', 't', ' ', 's', 'i', 't', ' ', 'a', 'm',
272       'e', 't', ' ', 'm', 'a', 'g', 'n', 'a', ' ', 'm', 'a', 'u', 'r', 'i', 's',
273       '.', ' ', 'U', 't', ' ', 's', 'e', 'd', ' ', 't', 'u', 'r', 'p', 'i', 's',
274   };
275   // clang-format on
276 
277   EXPECT_TRUE(ContainersEqual(*sent_second, expected_second));
278 
279   engine().AddCredits(10);
280 
281   ASSERT_EQ(sent_frames().size(), 3u);
282   auto& sent_third = sent_frames()[2];
283   ASSERT_TRUE(sent_third);
284   EXPECT_EQ(sent_third->size(), 36u);
285   EXPECT_EQ(engine().credits(), 9);
286   EXPECT_EQ(engine().segments_count(), 0u);
287 
288   // clang-format off: Formatter wants each value on a separate line.
289   StaticByteBuffer<36> expected_third{
290       // PDU size field (LE u16)
291       32, 0,
292       // Channel field (LE u16)
293       170, 0,
294       // Payload
295       ' ', 'n', 'i', 'b', 'h', '.', ' ', 'V', 'e', 's', 't', 'i', 'b', 'u', 'l',
296       'u', 'm', ' ', 's', 'e', 'd', ' ', 't', 'o', 'r', 't', 'o', 'r', ' ', 'i',
297       'd', '.'
298   };
299   // clang-format on
300 
301   EXPECT_TRUE(ContainersEqual(*sent_third, expected_third));
302 }
303 
TEST_F(CreditBasedFlowControlTxEngineTest,DoesNotAcceptSduWhilePdusQueued)304 TEST_F(CreditBasedFlowControlTxEngineTest, DoesNotAcceptSduWhilePdusQueued) {
305   StaticByteBuffer<150> segmented{
306       'L', 'o', 'r', 'e', 'm', ' ', 'i', 'p', 's', 'u', 'm', ' ', 'd', 'o',
307       'l', 'o', 'r', ' ', 's', 'i', 't', ' ', 'a', 'm', 'e', 't', ',', ' ',
308       'c', 'o', 'n', 's', 'e', 'c', 't', 'e', 't', 'u', 'r', ' ', 'a', 'd',
309       'i', 'p', 'i', 's', 'c', 'i', 'n', 'g', ' ', 'e', 'l', 'i', 't', '.',
310       ' ', 'S', 'e', 'd', ' ', 'e', 't', ' ', 'v', 'e', 'h', 'i', 'c', 'u',
311       'l', 'a', ' ', 'e', 'n', 'i', 'm', '.', ' ', 'U', 't', ' ', 's', 'i',
312       't', ' ', 'a', 'm', 'e', 't', ' ', 'm', 'a', 'g', 'n', 'a', ' ', 'm',
313       'a', 'u', 'r', 'i', 's', '.', ' ', 'U', 't', ' ', 's', 'e', 'd', ' ',
314       't', 'u', 'r', 'p', 'i', 's', ' ', 'n', 'i', 'b', 'h', '.', ' ', 'V',
315       'e', 's', 't', 'i', 'b', 'u', 'l', 'u', 'm', ' ', 's', 'e', 'd', ' ',
316       't', 'o', 'r', 't', 'o', 'r', ' ', 'i', 'd', '.'};
317 
318   ProcessSdu(std::make_unique<DynamicByteBuffer>(segmented));
319   EXPECT_EQ(engine().credits(), 0);
320   EXPECT_EQ(engine().segments_count(), 2u);
321   EXPECT_EQ(channel().queue_size(), 0u);
322 
323   ASSERT_EQ(sent_frames().size(), 1u);
324   auto& sent_first = sent_frames()[0];
325   ASSERT_TRUE(sent_first);
326   EXPECT_EQ(sent_first->size(), kTestMps);
327 
328   // clang-format off: Formatter wants each value on a separate line.
329   StaticByteBuffer<kTestMps> expected_first{
330       // PDU size field (LE u16)
331       (kTestMps - 4), 0,
332       // Channel field (LE u16)
333       170, 0,
334       // SDU size field (LE u16)
335       150, 0,
336       // Payload
337       'L', 'o', 'r', 'e', 'm', ' ', 'i', 'p', 's', 'u', 'm', ' ', 'd', 'o', 'l',
338       'o', 'r', ' ', 's', 'i', 't', ' ', 'a', 'm', 'e', 't', ',', ' ', 'c', 'o',
339       'n', 's', 'e', 'c', 't', 'e', 't', 'u', 'r', ' ', 'a', 'd', 'i', 'p', 'i',
340       's', 'c', 'i', 'n', 'g', ' ', 'e', 'l', 'i', 't', '.', ' ', 'S',
341   };
342   // clang-format on
343 
344   EXPECT_TRUE(ContainersEqual(*sent_first, expected_first));
345 
346   StaticByteBuffer<8> next_sdu{'n', 'e', 'x', 't', '_', 's', 'd', 'u'};
347   ProcessSdu(std::make_unique<DynamicByteBuffer>(next_sdu));
348 
349   EXPECT_EQ(sent_frames().size(), 1u);
350   EXPECT_EQ(engine().credits(), 0);
351   EXPECT_EQ(engine().segments_count(), 2u);
352   EXPECT_EQ(channel().queue_size(), 1u);
353 
354   engine().AddCredits(3);
355 
356   ASSERT_EQ(sent_frames().size(), 4u);
357   auto& sent_second = sent_frames()[1];
358   auto& sent_third = sent_frames()[2];
359   auto& sent_fourth = sent_frames()[3];
360 
361   ASSERT_TRUE(sent_second);
362   EXPECT_EQ(sent_second->size(), kTestMps);
363 
364   ASSERT_TRUE(sent_third);
365   EXPECT_EQ(sent_third->size(), 36u);
366 
367   ASSERT_TRUE(sent_fourth);
368   EXPECT_EQ(sent_fourth->size(), 14u);
369 
370   EXPECT_EQ(engine().credits(), 0);
371   EXPECT_EQ(engine().segments_count(), 0u);
372   EXPECT_EQ(channel().queue_size(), 0u);
373 
374   // clang-format off: Formatter wants each value on a separate line.
375   StaticByteBuffer<kTestMps> expected_second{
376       // PDU size field (LE u16)
377       (kTestMps - 4), 0,
378       // Channel field (LE u16)
379       170, 0,
380       // Payload
381       'e', 'd', ' ', 'e', 't', ' ', 'v', 'e', 'h', 'i', 'c', 'u', 'l', 'a', ' ',
382       'e', 'n', 'i', 'm', '.', ' ', 'U', 't', ' ', 's', 'i', 't', ' ', 'a', 'm',
383       'e', 't', ' ', 'm', 'a', 'g', 'n', 'a', ' ', 'm', 'a', 'u', 'r', 'i', 's',
384       '.', ' ', 'U', 't', ' ', 's', 'e', 'd', ' ', 't', 'u', 'r', 'p', 'i', 's',
385   };
386   // clang-format on
387 
388   EXPECT_TRUE(ContainersEqual(*sent_second, expected_second));
389 
390   // clang-format off: Formatter wants each value on a separate line.
391   StaticByteBuffer<36> expected_third{
392       // PDU size field (LE u16)
393       32, 0,
394       // Channel field (LE u16)
395       170, 0,
396       // Payload
397       ' ', 'n', 'i', 'b', 'h', '.', ' ', 'V', 'e', 's', 't', 'i', 'b', 'u', 'l',
398       'u', 'm', ' ', 's', 'e', 'd', ' ', 't', 'o', 'r', 't', 'o', 'r', ' ', 'i',
399       'd', '.'
400   };
401   // clang-format on
402 
403   EXPECT_TRUE(ContainersEqual(*sent_third, expected_third));
404 
405   // clang-format off: Formatter wants each value on a separate line.
406   StaticByteBuffer<14> expected_fourth{
407       // PDU size field (LE u16)
408       10, 0,
409       // Channel field (LE u16)
410       170, 0,
411       // SDU size field (LE u16)
412       8, 0,
413       // Payload
414       'n', 'e', 'x', 't', '_', 's', 'd', 'u',
415   };
416   // clang-format on
417 
418   EXPECT_TRUE(ContainersEqual(*sent_fourth, expected_fourth));
419 }
420 
TEST_F(CreditBasedFlowControlTxEngineTest,DoesNotAcceptOversizedSdu)421 TEST_F(CreditBasedFlowControlTxEngineTest, DoesNotAcceptOversizedSdu) {
422   StaticByteBuffer<kTestMtu + 1> oversized{};
423   ProcessSdu(std::make_unique<DynamicByteBuffer>(oversized));
424 
425   EXPECT_EQ(engine().credits(), 1);
426   EXPECT_EQ(engine().segments_count(), 0u);
427   EXPECT_EQ(channel().queue_size(), 0u);
428   ASSERT_EQ(sent_frames().size(), 0u);
429 }
430 
TEST_F(CreditBasedFlowControlTxEngineTest,AddCreditsOverCap)431 TEST_F(CreditBasedFlowControlTxEngineTest, AddCreditsOverCap) {
432   EXPECT_FALSE(engine().AddCredits(65535));
433   EXPECT_EQ(engine().credits(), 1u);
434   EXPECT_TRUE(engine().AddCredits(3000));
435   EXPECT_EQ(engine().credits(), 3001u);
436   EXPECT_TRUE(engine().AddCredits(50000));
437   EXPECT_EQ(engine().credits(), 53001u);
438   EXPECT_FALSE(engine().AddCredits(12535));
439   EXPECT_EQ(engine().credits(), 53001u);
440   EXPECT_FALSE(engine().AddCredits(65535));
441   EXPECT_EQ(engine().credits(), 53001u);
442   EXPECT_TRUE(engine().AddCredits(12534));
443   EXPECT_EQ(engine().credits(), 65535u);
444   EXPECT_FALSE(engine().AddCredits(1));
445   EXPECT_EQ(engine().credits(), 65535u);
446   EXPECT_FALSE(engine().AddCredits(42));
447   EXPECT_EQ(engine().credits(), 65535u);
448   EXPECT_FALSE(engine().AddCredits(99));
449   EXPECT_EQ(engine().credits(), 65535u);
450   EXPECT_FALSE(engine().AddCredits(32768));
451   EXPECT_EQ(engine().credits(), 65535u);
452   EXPECT_FALSE(engine().AddCredits(32767));
453   EXPECT_EQ(engine().credits(), 65535u);
454   EXPECT_FALSE(engine().AddCredits(65535));
455   EXPECT_EQ(engine().credits(), 65535u);
456 }
457 
458 }  // namespace
459 }  // namespace bt::l2cap::internal
460