1 // Copyright 2023 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/low_energy_command_handler.h"
16
17 #include <pw_async/fake_dispatcher_fixture.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_signaling_channel.h"
20 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
21
22 namespace bt::l2cap::internal {
23
24 class LowEnergyCommandHandlerTest
25 : public pw::async::test::FakeDispatcherFixture {
26 public:
27 LowEnergyCommandHandlerTest() = default;
28 ~LowEnergyCommandHandlerTest() override = default;
29 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyCommandHandlerTest);
30
31 protected:
32 // TestLoopFixture overrides
SetUp()33 void SetUp() override {
34 signaling_channel_ =
35 std::make_unique<testing::FakeSignalingChannel>(dispatcher());
36 command_handler_ = std::make_unique<LowEnergyCommandHandler>(
37 fake_sig(),
38 fit::bind_member<&LowEnergyCommandHandlerTest::OnRequestFail>(this));
39 request_fail_callback_ = nullptr;
40 failed_requests_ = 0;
41 }
42
TearDown()43 void TearDown() override {
44 request_fail_callback_ = nullptr;
45 signaling_channel_ = nullptr;
46 command_handler_ = nullptr;
47 }
48
fake_sig() const49 testing::FakeSignalingChannel* fake_sig() const {
50 return signaling_channel_.get();
51 }
cmd_handler() const52 LowEnergyCommandHandler* cmd_handler() const {
53 return command_handler_.get();
54 }
failed_requests() const55 size_t failed_requests() const { return failed_requests_; }
56
set_request_fail_callback(fit::closure request_fail_callback)57 void set_request_fail_callback(fit::closure request_fail_callback) {
58 BT_ASSERT(!request_fail_callback_);
59 request_fail_callback_ = std::move(request_fail_callback);
60 }
61
62 private:
OnRequestFail()63 void OnRequestFail() {
64 failed_requests_++;
65 if (request_fail_callback_) {
66 request_fail_callback_();
67 }
68 }
69
70 std::unique_ptr<testing::FakeSignalingChannel> signaling_channel_;
71 std::unique_ptr<LowEnergyCommandHandler> command_handler_;
72 fit::closure request_fail_callback_;
73 size_t failed_requests_;
74 };
75
TEST_F(LowEnergyCommandHandlerTest,OutboundConnParamUpdateReqRspOk)76 TEST_F(LowEnergyCommandHandlerTest, OutboundConnParamUpdateReqRspOk) {
77 constexpr uint16_t kIntervalMin = 0;
78 constexpr uint16_t kIntervalMax = 1;
79 constexpr uint16_t kPeripheralLatency = 2;
80 constexpr uint16_t kTimeoutMult = 3;
81 StaticByteBuffer param_update_req(
82 // Interval Min
83 LowerBits(kIntervalMin),
84 UpperBits(kIntervalMin),
85 // Interval Max
86 LowerBits(kIntervalMax),
87 UpperBits(kIntervalMax),
88 // Peripheral Latency
89 LowerBits(kPeripheralLatency),
90 UpperBits(kPeripheralLatency),
91 // Timeout Multiplier
92 LowerBits(kTimeoutMult),
93 UpperBits(kTimeoutMult));
94
95 StaticByteBuffer param_update_rsp(
96 LowerBits(
97 static_cast<uint16_t>(ConnectionParameterUpdateResult::kRejected)),
98 UpperBits(
99 static_cast<uint16_t>(ConnectionParameterUpdateResult::kRejected)));
100
101 bool cb_called = false;
102 LowEnergyCommandHandler::ConnectionParameterUpdateResponseCallback rsp_cb =
103 [&](const LowEnergyCommandHandler::ConnectionParameterUpdateResponse&
104 rsp) {
105 cb_called = true;
106 EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status());
107 EXPECT_EQ(ConnectionParameterUpdateResult::kRejected, rsp.result());
108 };
109
110 EXPECT_OUTBOUND_REQ(
111 *fake_sig(),
112 kConnectionParameterUpdateRequest,
113 param_update_req.view(),
114 {SignalingChannel::Status::kSuccess, param_update_rsp.view()});
115
116 EXPECT_TRUE(
117 cmd_handler()->SendConnectionParameterUpdateRequest(kIntervalMin,
118 kIntervalMax,
119 kPeripheralLatency,
120 kTimeoutMult,
121 std::move(rsp_cb)));
122 RunUntilIdle();
123 EXPECT_TRUE(cb_called);
124 }
125
TEST_F(LowEnergyCommandHandlerTest,InboundConnParamsUpdateReqRspOk)126 TEST_F(LowEnergyCommandHandlerTest, InboundConnParamsUpdateReqRspOk) {
127 constexpr uint16_t kIntervalMin = 0;
128 constexpr uint16_t kIntervalMax = 1;
129 constexpr uint16_t kPeripheralLatency = 2;
130 constexpr uint16_t kTimeoutMult = 3;
131 StaticByteBuffer param_update_req(
132 // Interval Min
133 LowerBits(kIntervalMin),
134 UpperBits(kIntervalMin),
135 // Interval Max
136 LowerBits(kIntervalMax),
137 UpperBits(kIntervalMax),
138 // Peripheral Latency
139 LowerBits(kPeripheralLatency),
140 UpperBits(kPeripheralLatency),
141 // Timeout Multiplier
142 LowerBits(kTimeoutMult),
143 UpperBits(kTimeoutMult));
144
145 auto param_update_rsp = StaticByteBuffer(
146 LowerBits(
147 static_cast<uint16_t>(ConnectionParameterUpdateResult::kRejected)),
148 UpperBits(
149 static_cast<uint16_t>(ConnectionParameterUpdateResult::kRejected)));
150
151 LowEnergyCommandHandler::ConnectionParameterUpdateRequestCallback cb =
152 [&](uint16_t interval_min,
153 uint16_t interval_max,
154 uint16_t peripheral_latency,
155 uint16_t timeout_multiplier,
156 LowEnergyCommandHandler::ConnectionParameterUpdateResponder*
157 responder) {
158 EXPECT_EQ(kIntervalMin, interval_min);
159 EXPECT_EQ(kIntervalMax, interval_max);
160 EXPECT_EQ(kPeripheralLatency, peripheral_latency);
161 EXPECT_EQ(kTimeoutMult, timeout_multiplier);
162 responder->Send(ConnectionParameterUpdateResult::kRejected);
163 };
164
165 cmd_handler()->ServeConnectionParameterUpdateRequest(std::move(cb));
166
167 RETURN_IF_FATAL(fake_sig()->ReceiveExpect(
168 kConnectionParameterUpdateRequest, param_update_req, param_update_rsp));
169 }
170
TEST_F(LowEnergyCommandHandlerTest,InboundConnParamsUpdateReqNotEnoughBytes)171 TEST_F(LowEnergyCommandHandlerTest, InboundConnParamsUpdateReqNotEnoughBytes) {
172 constexpr uint16_t kIntervalMin = 0;
173
174 // Request is missing interval max, peripheral latency, and timeout multiplier
175 // fields.
176 auto param_update_req = StaticByteBuffer(
177 // Interval Min
178 LowerBits(kIntervalMin),
179 UpperBits(kIntervalMin));
180
181 bool cb_called = false;
182 auto cb = [&](uint16_t interval_min,
183 uint16_t interval_max,
184 uint16_t peripheral_latency,
185 uint16_t timeout_multiplier,
186 auto responder) { cb_called = true; };
187
188 cmd_handler()->ServeConnectionParameterUpdateRequest(std::move(cb));
189
190 RETURN_IF_FATAL(fake_sig()->ReceiveExpectRejectNotUnderstood(
191 kConnectionParameterUpdateRequest, param_update_req));
192 EXPECT_FALSE(cb_called);
193 }
194
195 } // namespace bt::l2cap::internal
196