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_rx_engine.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/l2cap/fragmenter.h"
18 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
19 #include "pw_unit_test/framework.h"
20
21 namespace bt::l2cap::internal {
22 namespace {
23
24 using Engine = CreditBasedFlowControlRxEngine;
25
26 class CreditBasedFlowControlRxEngineTest : public ::testing::Test {
27 protected:
28 static constexpr hci_spec::ConnectionHandle kTestHandle = 0x0001;
29 static constexpr ChannelId kTestChannelId = 0x0001;
30
engine()31 Engine& engine() { return *engine_; }
failure_callback_count()32 size_t failure_callback_count() { return failure_callback_count_; }
ToPdu(const ByteBuffer & buffer)33 PDU ToPdu(const ByteBuffer& buffer) {
34 return Fragmenter(kTestHandle)
35 .BuildFrame(kTestChannelId, buffer, FrameCheckSequenceOption::kNoFcs);
36 }
37
ProcessPayload(const ByteBuffer & buffer)38 ByteBufferPtr ProcessPayload(const ByteBuffer& buffer) {
39 return engine().ProcessPdu(ToPdu(buffer));
40 }
41
42 private:
43 size_t failure_callback_count_ = 0;
44 std::unique_ptr<Engine> engine_ =
__anon923920900202null45 std::make_unique<Engine>([this] { ++failure_callback_count_; });
46 };
47
TEST_F(CreditBasedFlowControlRxEngineTest,SmallUnsegmentedSdu)48 TEST_F(CreditBasedFlowControlRxEngineTest, SmallUnsegmentedSdu) {
49 // clang-format off
50 const StaticByteBuffer payload(
51 // SDU size field (LE u16)
52 4, 0,
53 // Payload
54 't', 'e', 's', 't'
55 );
56 // clang-format on
57
58 const ByteBufferPtr sdu = ProcessPayload(payload);
59
60 ASSERT_TRUE(sdu);
61 EXPECT_TRUE(ContainersEqual(StaticByteBuffer('t', 'e', 's', 't'), *sdu));
62 EXPECT_EQ(0u, failure_callback_count());
63 }
64
TEST_F(CreditBasedFlowControlRxEngineTest,LargeUnsegmentedSdu)65 TEST_F(CreditBasedFlowControlRxEngineTest, LargeUnsegmentedSdu) {
66 // clang-format off
67 const StaticByteBuffer payload(
68 // SDU size field (LE u16)
69 58, 0,
70 // Payload
71 'L', 'o', 'r', 'e', 'm', ' ', 'i', 'p', 's', 'u', 'm', ' ', 'd', 'o', 'l',
72 'o', 'r', ' ', 's', 'i', 't', ' ', 'a', 'm', 'e', 't', ',', ' ', 'c', 'o',
73 'n', 's', 'e', 'c', 't', 'e', 't', 'u', 'r', ' ', 'a', 'd', 'i', 'p', 'i',
74 's', 'c', 'i', 'n', 'g', ' ', 'e', 'l', 'i', 't', '.', ' ', 'S'
75 );
76 // clang-format on
77
78 const ByteBufferPtr sdu = ProcessPayload(payload);
79 ASSERT_TRUE(sdu);
80 EXPECT_TRUE(ContainersEqual(payload.view(2), *sdu));
81 EXPECT_EQ(0u, failure_callback_count());
82 }
83
TEST_F(CreditBasedFlowControlRxEngineTest,SduSegmentedIntoManySmallPdus)84 TEST_F(CreditBasedFlowControlRxEngineTest, SduSegmentedIntoManySmallPdus) {
85 // clang-format off
86 EXPECT_FALSE(ProcessPayload(StaticByteBuffer(
87 // SDU size field (LE u16)
88 16, 0,
89 // First four bytes of payload
90 't', 'e', 's', 't'
91 )));
92 // clang-format on
93
94 EXPECT_FALSE(ProcessPayload(StaticByteBuffer('i', 'n', 'g', ' ')));
95 EXPECT_FALSE(ProcessPayload(StaticByteBuffer('f', 'o', 'r', ' ')));
96 const ByteBufferPtr sdu =
97 ProcessPayload(StaticByteBuffer('b', 'u', 'g', 's'));
98
99 // clang-format off
100 StaticByteBuffer expected(
101 't', 'e', 's', 't', 'i', 'n', 'g', ' ',
102 'f', 'o', 'r', ' ', 'b', 'u', 'g', 's'
103 );
104 // clang-format on
105
106 ASSERT_TRUE(sdu);
107 EXPECT_TRUE(ContainersEqual(expected, *sdu));
108 EXPECT_EQ(0u, failure_callback_count());
109 }
110
TEST_F(CreditBasedFlowControlRxEngineTest,FailSduSmallerThanPayload)111 TEST_F(CreditBasedFlowControlRxEngineTest, FailSduSmallerThanPayload) {
112 // clang-format off
113 const StaticByteBuffer payload(
114 // SDU size field (LE u16)
115 4, 0,
116 // Payload
117 'f', 'a', 'i', 'l', 'i', 'u', 'r', 'e'
118 );
119 // clang-format on
120
121 EXPECT_FALSE(ProcessPayload(payload));
122 EXPECT_EQ(1u, failure_callback_count());
123 }
124
TEST_F(CreditBasedFlowControlRxEngineTest,FailSduSmallerThanPayloadSegmented)125 TEST_F(CreditBasedFlowControlRxEngineTest, FailSduSmallerThanPayloadSegmented) {
126 // clang-format off
127 const StaticByteBuffer payload(
128 // SDU size field (LE u16)
129 5, 0,
130 // Payload
131 'f', 'a', 'i', 'l'
132 );
133 // clang-format on
134
135 EXPECT_FALSE(ProcessPayload(payload));
136 EXPECT_EQ(0u, failure_callback_count());
137 EXPECT_FALSE(ProcessPayload(StaticByteBuffer('i', 'u', 'r', 'e')));
138 EXPECT_EQ(1u, failure_callback_count());
139 }
140
TEST_F(CreditBasedFlowControlRxEngineTest,InitialFrameMissingSduSize)141 TEST_F(CreditBasedFlowControlRxEngineTest, InitialFrameMissingSduSize) {
142 const ByteBufferPtr sdu = ProcessPayload(StaticByteBuffer(0));
143 EXPECT_FALSE(sdu);
144 EXPECT_EQ(1u, failure_callback_count());
145 }
146
147 } // namespace
148 } // namespace bt::l2cap::internal
149