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 #pragma once 16 #include <pw_async/heap_dispatcher.h> 17 18 #include <memory> 19 20 #include "pw_async/fake_dispatcher_fixture.h" 21 #include "pw_bluetooth/controller.h" 22 #include "pw_bluetooth_sapphire/internal/host/common/macros.h" 23 #include "pw_bluetooth_sapphire/internal/host/transport/acl_data_channel.h" 24 #include "pw_bluetooth_sapphire/internal/host/transport/acl_data_packet.h" 25 #include "pw_bluetooth_sapphire/internal/host/transport/sco_data_channel.h" 26 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h" 27 28 namespace bt::testing { 29 30 class ControllerTestDoubleBase; 31 32 // ControllerTest is a test harness intended for tests that rely on HCI 33 // transactions. It is templated on ControllerTestDoubleType which must derive 34 // from ControllerTestDoubleBase and must be able to send and receive HCI 35 // packets over Zircon channels, acting as the controller endpoint of HCI. 36 // 37 // The testing library provides two such types: 38 // 39 // - MockController (mock_controller.h): Routes HCI packets directly to the 40 // test harness. It allows tests to setup expectations based on the receipt 41 // of HCI packets. 42 // 43 // - FakeController (fake_controller.h): Emulates a Bluetooth controller. This 44 // can respond to HCI commands the way a real controller would (albeit in a 45 // contrived fashion), emulate discovery and connection events, etc. 46 template <class ControllerTestDoubleType> 47 class ControllerTest { 48 public: 49 // Default data buffer information used by ACLDataChannel. 50 static constexpr size_t kDefaultMaxAclDataPacketLength = 1024; 51 static constexpr size_t kDefaultMaxAclPacketCount = 5; 52 53 // Default data buffer information used by ScoDataChannel. 54 static constexpr size_t kDefaultMaxScoPacketLength = 255; 55 static constexpr size_t kDefaultMaxScoPacketCount = 5; 56 ControllerTest(pw::async::Dispatcher & dispatcher)57 ControllerTest(pw::async::Dispatcher& dispatcher) : dispatcher_(dispatcher) {} 58 ~ControllerTest() = default; 59 60 protected: 61 void Initialize(pw::bluetooth::Controller::FeaturesBits features, 62 bool initialize_transport = true) { 63 std::unique_ptr<pw::bluetooth::Controller> controller = 64 ControllerTest<ControllerTestDoubleType>::SetUpTestController(); 65 test_device_->set_features(features); 66 transport_ = 67 std::make_unique<hci::Transport>(std::move(controller), dispatcher_); 68 69 if (initialize_transport) { 70 std::optional<bool> init_result; 71 transport_->Initialize( 72 [&init_result](bool success) { init_result = success; }); 73 ASSERT_TRUE(init_result.has_value()); 74 ASSERT_TRUE(init_result.value()); 75 } 76 } 77 78 // Directly initializes the ACL data channel and wires up its data rx 79 // callback. It is OK to override the data rx callback after this is called. 80 // 81 // If data buffer information isn't provided, the ACLDataChannel will be 82 // initialized with shared BR/EDR/LE buffers using the constants declared 83 // above. 84 bool InitializeACLDataChannel( 85 const hci::DataBufferInfo& bredr_buffer_info = hci::DataBufferInfo( 86 kDefaultMaxAclDataPacketLength, kDefaultMaxAclPacketCount), 87 const hci::DataBufferInfo& le_buffer_info = hci::DataBufferInfo()) { 88 if (!transport_->InitializeACLDataChannel(bredr_buffer_info, 89 le_buffer_info)) { 90 return false; 91 } 92 93 transport_->acl_data_channel()->SetDataRxHandler( 94 std::bind(&ControllerTest<ControllerTestDoubleType>::OnAclDataReceived, 95 this, 96 std::placeholders::_1)); 97 98 return true; 99 } 100 101 // Directly initializes the SCO data channel. 102 bool InitializeScoDataChannel( 103 const hci::DataBufferInfo& buffer_info = hci::DataBufferInfo( 104 kDefaultMaxScoPacketLength, kDefaultMaxScoPacketCount)) { 105 return transport_->InitializeScoDataChannel(buffer_info); 106 } 107 108 // Sets a callback which will be invoked when we receive packets from the test 109 // controller. |callback| will be posted on the test loop, thus no locking is 110 // necessary within the callback. 111 // 112 // InitializeACLDataChannel() must be called once and its data rx handler must 113 // not be overridden by tests for |callback| to work. set_data_received_callback(hci::ACLPacketHandler callback)114 void set_data_received_callback(hci::ACLPacketHandler callback) { 115 data_received_callback_ = std::move(callback); 116 } 117 transport()118 hci::Transport* transport() const { return transport_.get(); } cmd_channel()119 hci::CommandChannel* cmd_channel() const { 120 return transport_->command_channel(); 121 } acl_data_channel()122 hci::AclDataChannel* acl_data_channel() const { 123 return transport_->acl_data_channel(); 124 } sco_data_channel()125 hci::ScoDataChannel* sco_data_channel() const { 126 return transport_->sco_data_channel(); 127 } 128 129 // Deletes |test_device_| and resets the pointer. DeleteTestDevice()130 void DeleteTestDevice() { test_device_ = nullptr; } DeleteTransport()131 void DeleteTransport() { transport_ = nullptr; } 132 133 // Getters for internal fields frequently used by tests. test_device()134 const typename ControllerTestDoubleType::WeakPtr& test_device() const { 135 return test_device_; 136 } 137 138 private: SetUpTestController()139 std::unique_ptr<pw::bluetooth::Controller> SetUpTestController() { 140 std::unique_ptr<ControllerTestDoubleType> controller = 141 std::make_unique<ControllerTestDoubleType>(dispatcher_); 142 test_device_ = controller->GetWeakPtr(); 143 return controller; 144 } 145 OnAclDataReceived(hci::ACLDataPacketPtr data_packet)146 void OnAclDataReceived(hci::ACLDataPacketPtr data_packet) { 147 // Accessing |data_received_callback_| is racy but unlikely to cause issues 148 // in unit tests. NOTE(armansito): Famous last words? 149 if (!data_received_callback_) 150 return; 151 152 (void)heap_dispatcher_.Post( 153 [this, packet = std::move(data_packet)](pw::async::Context /*ctx*/, 154 pw::Status status) mutable { 155 if (status.ok()) { 156 data_received_callback_(std::move(packet)); 157 } 158 }); 159 } 160 161 pw::async::Dispatcher& dispatcher_; 162 pw::async::HeapDispatcher heap_dispatcher_{dispatcher_}; 163 typename ControllerTestDoubleType::WeakPtr test_device_; 164 std::unique_ptr<hci::Transport> transport_; 165 hci::ACLPacketHandler data_received_callback_; 166 167 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ControllerTest); 168 static_assert( 169 std::is_base_of<ControllerTestDoubleBase, 170 ControllerTestDoubleType>::value, 171 "TestBase must be used with a derivative of ControllerTestDoubleBase"); 172 }; 173 174 // FakeDispatcherControllerTest is a convenience test fixture that initializes 175 // ControllerTest with a pw_async FakeDispatcherFixture backend. Only if a 176 // different underlying dispatcher is desired (e.g. Zircon TestLoopFixture) 177 // should ControllerTest be referenced directly and passed the desired 178 // dispatcher, which must implement the pw_async Dispatcher interface. 179 // 180 // To properly "TearDown" ControllerTest, the Dispatcher must be driven, then 181 // DeleteTransport() called. 182 template <typename ControllerTestDoubleType> 183 class FakeDispatcherControllerTest 184 : public pw::async::test::FakeDispatcherFixture, 185 public ControllerTest<ControllerTestDoubleType> { 186 protected: FakeDispatcherControllerTest()187 FakeDispatcherControllerTest() 188 : ControllerTest<ControllerTestDoubleType>(dispatcher()) {} 189 SetUp()190 void SetUp() override { 191 SetUp(pw::bluetooth::Controller::FeaturesBits::kHciSco); 192 } 193 194 void SetUp(pw::bluetooth::Controller::FeaturesBits features, 195 bool initialize_transport = true) { 196 ControllerTest<ControllerTestDoubleType>::Initialize(features, 197 initialize_transport); 198 RunUntilIdle(); 199 } 200 TearDown()201 void TearDown() override { 202 RunUntilIdle(); 203 ControllerTest<ControllerTestDoubleType>::DeleteTransport(); 204 } 205 }; 206 207 } // namespace bt::testing 208