| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/low_energy_command_handler.h" |
| |
| #include <pw_async/fake_dispatcher_fixture.h> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/fake_signaling_channel.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_helpers.h" |
| |
| namespace bt::l2cap::internal { |
| |
| class LowEnergyCommandHandlerTest |
| : public pw::async::test::FakeDispatcherFixture { |
| public: |
| LowEnergyCommandHandlerTest() = default; |
| ~LowEnergyCommandHandlerTest() override = default; |
| BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyCommandHandlerTest); |
| |
| protected: |
| // TestLoopFixture overrides |
| void SetUp() override { |
| signaling_channel_ = |
| std::make_unique<testing::FakeSignalingChannel>(dispatcher()); |
| command_handler_ = std::make_unique<LowEnergyCommandHandler>( |
| fake_sig(), |
| fit::bind_member<&LowEnergyCommandHandlerTest::OnRequestFail>(this)); |
| request_fail_callback_ = nullptr; |
| failed_requests_ = 0; |
| } |
| |
| void TearDown() override { |
| request_fail_callback_ = nullptr; |
| signaling_channel_ = nullptr; |
| command_handler_ = nullptr; |
| } |
| |
| testing::FakeSignalingChannel* fake_sig() const { |
| return signaling_channel_.get(); |
| } |
| LowEnergyCommandHandler* cmd_handler() const { |
| return command_handler_.get(); |
| } |
| size_t failed_requests() const { return failed_requests_; } |
| |
| void set_request_fail_callback(fit::closure request_fail_callback) { |
| BT_ASSERT(!request_fail_callback_); |
| request_fail_callback_ = std::move(request_fail_callback); |
| } |
| |
| private: |
| void OnRequestFail() { |
| failed_requests_++; |
| if (request_fail_callback_) { |
| request_fail_callback_(); |
| } |
| } |
| |
| std::unique_ptr<testing::FakeSignalingChannel> signaling_channel_; |
| std::unique_ptr<LowEnergyCommandHandler> command_handler_; |
| fit::closure request_fail_callback_; |
| size_t failed_requests_; |
| }; |
| |
| TEST_F(LowEnergyCommandHandlerTest, OutboundConnParamUpdateReqRspOk) { |
| constexpr uint16_t kIntervalMin = 0; |
| constexpr uint16_t kIntervalMax = 1; |
| constexpr uint16_t kPeripheralLatency = 2; |
| constexpr uint16_t kTimeoutMult = 3; |
| StaticByteBuffer param_update_req( |
| // Interval Min |
| LowerBits(kIntervalMin), |
| UpperBits(kIntervalMin), |
| // Interval Max |
| LowerBits(kIntervalMax), |
| UpperBits(kIntervalMax), |
| // Peripheral Latency |
| LowerBits(kPeripheralLatency), |
| UpperBits(kPeripheralLatency), |
| // Timeout Multiplier |
| LowerBits(kTimeoutMult), |
| UpperBits(kTimeoutMult)); |
| |
| StaticByteBuffer param_update_rsp( |
| LowerBits( |
| static_cast<uint16_t>(ConnectionParameterUpdateResult::kRejected)), |
| UpperBits( |
| static_cast<uint16_t>(ConnectionParameterUpdateResult::kRejected))); |
| |
| bool cb_called = false; |
| LowEnergyCommandHandler::ConnectionParameterUpdateResponseCallback rsp_cb = |
| [&](const LowEnergyCommandHandler::ConnectionParameterUpdateResponse& |
| rsp) { |
| cb_called = true; |
| EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status()); |
| EXPECT_EQ(ConnectionParameterUpdateResult::kRejected, rsp.result()); |
| }; |
| |
| EXPECT_OUTBOUND_REQ( |
| *fake_sig(), |
| kConnectionParameterUpdateRequest, |
| param_update_req.view(), |
| {SignalingChannel::Status::kSuccess, param_update_rsp.view()}); |
| |
| EXPECT_TRUE( |
| cmd_handler()->SendConnectionParameterUpdateRequest(kIntervalMin, |
| kIntervalMax, |
| kPeripheralLatency, |
| kTimeoutMult, |
| std::move(rsp_cb))); |
| RunUntilIdle(); |
| EXPECT_TRUE(cb_called); |
| } |
| |
| TEST_F(LowEnergyCommandHandlerTest, InboundConnParamsUpdateReqRspOk) { |
| constexpr uint16_t kIntervalMin = 0; |
| constexpr uint16_t kIntervalMax = 1; |
| constexpr uint16_t kPeripheralLatency = 2; |
| constexpr uint16_t kTimeoutMult = 3; |
| StaticByteBuffer param_update_req( |
| // Interval Min |
| LowerBits(kIntervalMin), |
| UpperBits(kIntervalMin), |
| // Interval Max |
| LowerBits(kIntervalMax), |
| UpperBits(kIntervalMax), |
| // Peripheral Latency |
| LowerBits(kPeripheralLatency), |
| UpperBits(kPeripheralLatency), |
| // Timeout Multiplier |
| LowerBits(kTimeoutMult), |
| UpperBits(kTimeoutMult)); |
| |
| auto param_update_rsp = StaticByteBuffer( |
| LowerBits( |
| static_cast<uint16_t>(ConnectionParameterUpdateResult::kRejected)), |
| UpperBits( |
| static_cast<uint16_t>(ConnectionParameterUpdateResult::kRejected))); |
| |
| LowEnergyCommandHandler::ConnectionParameterUpdateRequestCallback cb = |
| [&](uint16_t interval_min, |
| uint16_t interval_max, |
| uint16_t peripheral_latency, |
| uint16_t timeout_multiplier, |
| LowEnergyCommandHandler::ConnectionParameterUpdateResponder* |
| responder) { |
| EXPECT_EQ(kIntervalMin, interval_min); |
| EXPECT_EQ(kIntervalMax, interval_max); |
| EXPECT_EQ(kPeripheralLatency, peripheral_latency); |
| EXPECT_EQ(kTimeoutMult, timeout_multiplier); |
| responder->Send(ConnectionParameterUpdateResult::kRejected); |
| }; |
| |
| cmd_handler()->ServeConnectionParameterUpdateRequest(std::move(cb)); |
| |
| RETURN_IF_FATAL(fake_sig()->ReceiveExpect( |
| kConnectionParameterUpdateRequest, param_update_req, param_update_rsp)); |
| } |
| |
| TEST_F(LowEnergyCommandHandlerTest, InboundConnParamsUpdateReqNotEnoughBytes) { |
| constexpr uint16_t kIntervalMin = 0; |
| |
| // Request is missing interval max, peripheral latency, and timeout multiplier |
| // fields. |
| auto param_update_req = StaticByteBuffer( |
| // Interval Min |
| LowerBits(kIntervalMin), |
| UpperBits(kIntervalMin)); |
| |
| bool cb_called = false; |
| auto cb = [&](uint16_t interval_min, |
| uint16_t interval_max, |
| uint16_t peripheral_latency, |
| uint16_t timeout_multiplier, |
| auto responder) { cb_called = true; }; |
| |
| cmd_handler()->ServeConnectionParameterUpdateRequest(std::move(cb)); |
| |
| RETURN_IF_FATAL(fake_sig()->ReceiveExpectRejectNotUnderstood( |
| kConnectionParameterUpdateRequest, param_update_req)); |
| EXPECT_FALSE(cb_called); |
| } |
| |
| } // namespace bt::l2cap::internal |