| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "device/bluetooth/floss/floss_adapter_client.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/run_loop.h" |
| #include "base/test/bind.h" |
| #include "base/test/task_environment.h" |
| #include "dbus/bus.h" |
| #include "dbus/message.h" |
| #include "dbus/mock_bus.h" |
| #include "dbus/mock_exported_object.h" |
| #include "dbus/mock_object_proxy.h" |
| #include "dbus/object_path.h" |
| #include "device/bluetooth/floss/floss_dbus_client.h" |
| #include "device/bluetooth/floss/floss_manager_client.h" |
| #include "device/bluetooth/floss/test_helpers.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| using testing::_; |
| |
| constexpr char kTestMethod0[] = "TestMethod0"; |
| constexpr char kTestMethod1[] = "TestMethod1"; |
| constexpr char kTestMethod2[] = "TestMethod2"; |
| |
| constexpr uint8_t kFakeU8Return = 100; |
| constexpr uint32_t kFakeU32Return = 20000; |
| constexpr char kFakeStrReturn[] = "fake return"; |
| constexpr uint32_t kFakeU32Param = 30; |
| constexpr char kFakeStrParam[] = "fake param"; |
| constexpr bool kFakeBoolParam = true; |
| constexpr uint32_t kFakeCallbackId = 23; |
| constexpr uint32_t kFakeConnectionCallbackId = 24; |
| |
| constexpr char kFakeDeviceAddr[] = "11:22:33:44:55:66"; |
| constexpr char kFakeDeviceName[] = "Some Device"; |
| constexpr uint8_t kFakeBytes[] = {1, 1, 2, 3, 5, 8, 13}; |
| constexpr uint8_t kFakeUuidByteArray[] = {0, 1, 2, 3, 4, 5, 6, 7, |
| 8, 9, 10, 11, 12, 13, 14, 15}; |
| constexpr char kFakeUuidStr[] = "00010203-0405-0607-0809-0a0b0c0d0e0f"; |
| constexpr floss::FlossAdapterClient::BluetoothDeviceType kFakeType = |
| floss::FlossAdapterClient::BluetoothDeviceType::kBle; |
| |
| } // namespace |
| |
| namespace floss { |
| namespace { |
| |
| class TestAdapterObserver : public FlossAdapterClient::Observer { |
| public: |
| explicit TestAdapterObserver(FlossAdapterClient* client) : client_(client) { |
| client_->AddObserver(this); |
| } |
| |
| ~TestAdapterObserver() override { client_->RemoveObserver(this); } |
| |
| void AdapterAddressChanged(const std::string& address) override { |
| address_changed_count_++; |
| address_ = address; |
| } |
| |
| void DiscoverableChanged(bool discoverable) override { |
| discoverable_changed_count_++; |
| discoverable_ = discoverable; |
| } |
| |
| void AdapterDiscoveringChanged(bool state) override { |
| discovering_changed_count_++; |
| discovering_state_ = state; |
| } |
| |
| void AdapterFoundDevice(const FlossDeviceId& device_found) override { |
| found_device_count_++; |
| found_device_ = device_found; |
| } |
| |
| void AdapterClearedDevice(const FlossDeviceId& device_cleared) override { |
| cleared_device_count_++; |
| cleared_device_ = device_cleared; |
| } |
| |
| void AdapterSspRequest(const FlossDeviceId& remote_device, |
| uint32_t cod, |
| FlossAdapterClient::BluetoothSspVariant variant, |
| uint32_t passkey) override { |
| ssp_request_count_++; |
| |
| ssp_device_ = remote_device; |
| cod_ = cod; |
| variant_ = variant; |
| passkey_ = passkey; |
| } |
| |
| void AdapterPinDisplay(const FlossDeviceId& remote_device, |
| std::string pincode) override { |
| pin_display_count_++; |
| |
| pin_display_device_ = remote_device; |
| pincode_ = pincode; |
| } |
| |
| void AdapterPinRequest(const FlossDeviceId& remote_device, |
| uint32_t cod, |
| bool min_16_digit) override { |
| pin_request_count_++; |
| |
| pin_request_device_ = remote_device; |
| } |
| |
| std::string address_; |
| bool discoverable_; |
| bool discovering_state_ = false; |
| FlossDeviceId found_device_; |
| FlossDeviceId cleared_device_; |
| |
| FlossDeviceId ssp_device_; |
| uint32_t cod_ = 0; |
| FlossAdapterClient::BluetoothSspVariant variant_ = |
| FlossAdapterClient::BluetoothSspVariant::kPasskeyConfirmation; |
| uint32_t passkey_ = 0; |
| FlossDeviceId pin_display_device_; |
| FlossDeviceId pin_request_device_; |
| std::string pincode_; |
| |
| int address_changed_count_ = 0; |
| int discoverable_changed_count_ = 0; |
| int discovering_changed_count_ = 0; |
| int found_device_count_ = 0; |
| int cleared_device_count_ = 0; |
| int ssp_request_count_ = 0; |
| int pin_display_count_ = 0; |
| int pin_request_count_ = 0; |
| |
| private: |
| raw_ptr<FlossAdapterClient> client_ = nullptr; |
| }; |
| |
| } // namespace |
| |
| class FlossAdapterClientTest : public testing::Test { |
| public: |
| FlossAdapterClientTest() = default; |
| |
| void SetUpMocks() { |
| adapter_path_ = FlossDBusClient::GenerateAdapterPath(adapter_index_); |
| adapter_object_proxy_ = base::MakeRefCounted<::dbus::MockObjectProxy>( |
| bus_.get(), kAdapterInterface, adapter_path_); |
| exported_callbacks_ = base::MakeRefCounted<::dbus::MockExportedObject>( |
| bus_.get(), |
| ::dbus::ObjectPath(FlossAdapterClient::kExportedCallbacksPath)); |
| |
| EXPECT_CALL(*bus_.get(), GetObjectProxy(kAdapterInterface, adapter_path_)) |
| .WillRepeatedly(::testing::Return(adapter_object_proxy_.get())); |
| EXPECT_CALL(*bus_.get(), GetExportedObject) |
| .WillRepeatedly(::testing::Return(exported_callbacks_.get())); |
| |
| // Exported callback methods that we don't need to invoke. This will need |
| // to be updated once new callbacks are added. |
| // TODO(b/233124093): Reduce this count by 2 when SDP tests are added. |
| EXPECT_CALL(*exported_callbacks_.get(), ExportMethod).Times(13); |
| |
| // Save the method handlers of exported callbacks that we need to invoke in |
| // test. |
| EXPECT_CALL( |
| *exported_callbacks_.get(), |
| ExportMethod(adapter::kCallbackInterface, adapter::kOnAddressChanged, |
| testing::_, testing::_)) |
| .WillOnce(testing::SaveArg<2>(&on_address_changed_)); |
| EXPECT_CALL(*exported_callbacks_.get(), |
| ExportMethod(adapter::kCallbackInterface, |
| adapter::kOnNameChanged, testing::_, testing::_)) |
| .WillOnce(testing::SaveArg<2>(&on_name_changed_)); |
| EXPECT_CALL( |
| *exported_callbacks_.get(), |
| ExportMethod(adapter::kCallbackInterface, |
| adapter::kOnDiscoverableChanged, testing::_, testing::_)) |
| .WillOnce(testing::SaveArg<2>(&on_discoverable_changed_)); |
| |
| // Handle method calls on the object proxy |
| ON_CALL( |
| *adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(adapter::kGetAddress), _, _)) |
| .WillByDefault(Invoke(this, &FlossAdapterClientTest::HandleGetAddress)); |
| ON_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(adapter::kGetName), _, _)) |
| .WillByDefault(Invoke(this, &FlossAdapterClientTest::HandleGetName)); |
| ON_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kGetDiscoverable), _, _)) |
| .WillByDefault( |
| Invoke(this, &FlossAdapterClientTest::HandleGetDiscoverable)); |
| } |
| |
| void SetUp() override { |
| ::dbus::Bus::Options options; |
| options.bus_type = ::dbus::Bus::BusType::SYSTEM; |
| bus_ = base::MakeRefCounted<::dbus::MockBus>(options); |
| client_ = FlossAdapterClient::Create(); |
| |
| SetUpMocks(); |
| } |
| |
| void TearDown() override { |
| // Clean up the client first so it gets rid of all its references to the |
| // various buses, object proxies, etc. |
| client_.reset(); |
| } |
| |
| void ExpectErrorResponse(std::unique_ptr<dbus::Response> response) { |
| EXPECT_EQ(response->GetMessageType(), |
| dbus::Message::MessageType::MESSAGE_ERROR); |
| } |
| |
| void ExpectNormalResponse(std::unique_ptr<dbus::Response> response) { |
| EXPECT_NE(response->GetMessageType(), |
| dbus::Message::MessageType::MESSAGE_ERROR); |
| } |
| |
| void HandleGetAddress(::dbus::MethodCall* method_call, |
| int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| auto response = ::dbus::Response::CreateEmpty(); |
| ::dbus::MessageWriter msg(response.get()); |
| msg.AppendString(adapter_address_); |
| |
| std::move(*cb).Run(response.get(), nullptr); |
| } |
| |
| void HandleGetName(::dbus::MethodCall* method_call, |
| int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| auto response = ::dbus::Response::CreateEmpty(); |
| ::dbus::MessageWriter msg(response.get()); |
| msg.AppendString(adapter_name_); |
| |
| std::move(*cb).Run(response.get(), nullptr); |
| } |
| |
| void HandleGetDiscoverable(::dbus::MethodCall* method_call, |
| int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| auto response = ::dbus::Response::CreateEmpty(); |
| ::dbus::MessageWriter msg(response.get()); |
| msg.AppendBool(adapter_discoverable_); |
| |
| std::move(*cb).Run(response.get(), nullptr); |
| } |
| |
| void HandleCreateBond( |
| FlossDeviceId expected_device, |
| FlossAdapterClient::BluetoothTransport expected_transport, |
| ::dbus::MethodCall* method_call, |
| int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader reader(method_call); |
| FlossDeviceId device; |
| uint32_t transport; |
| |
| ASSERT_TRUE( |
| FlossAdapterClient::ReadAllDBusParams(&reader, &device, &transport)); |
| EXPECT_EQ(expected_device, device); |
| EXPECT_EQ(expected_transport, |
| static_cast<FlossAdapterClient::BluetoothTransport>(transport)); |
| |
| auto response = ::dbus::Response::CreateEmpty(); |
| dbus::MessageWriter writer(response.get()); |
| bool kSuccess = true; |
| writer.AppendBool(kSuccess); |
| std::move(*cb).Run(response.get(), nullptr); |
| } |
| |
| void ExpectValidCreateBond(DBusResult<bool> ret) { |
| ASSERT_TRUE(ret.has_value()); |
| EXPECT_TRUE(*ret); |
| } |
| |
| void SendAddressChangeCallback( |
| bool error, |
| const std::string& address, |
| dbus::ExportedObject::ResponseSender response) { |
| dbus::MethodCall method_call(adapter::kCallbackInterface, |
| adapter::kOnAddressChanged); |
| method_call.SetSerial(serial_++); |
| if (!error) { |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(address); |
| } |
| |
| on_address_changed_.Run(&method_call, std::move(response)); |
| } |
| |
| void SendNameChangeCallback(bool error, |
| const std::string& name, |
| dbus::ExportedObject::ResponseSender response) { |
| dbus::MethodCall method_call(adapter::kCallbackInterface, |
| adapter::kOnNameChanged); |
| method_call.SetSerial(serial_++); |
| if (!error) { |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString(name); |
| } |
| |
| on_name_changed_.Run(&method_call, std::move(response)); |
| } |
| |
| void SendDiscoverableChangeCallback( |
| bool error, |
| bool discoverable, |
| dbus::ExportedObject::ResponseSender response) { |
| dbus::MethodCall method_call(adapter::kCallbackInterface, |
| adapter::kOnDiscoverableChanged); |
| method_call.SetSerial(serial_++); |
| if (!error) { |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendBool(discoverable); |
| } |
| |
| on_discoverable_changed_.Run(&method_call, std::move(response)); |
| } |
| |
| void SendDiscoveringChangeCallback( |
| bool error, |
| bool state, |
| dbus::ExportedObject::ResponseSender response) { |
| dbus::MethodCall method_call(adapter::kCallbackInterface, |
| adapter::kOnDiscoveringChanged); |
| method_call.SetSerial(serial_++); |
| if (!error) { |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendBool(state); |
| } else { |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendString("garbage"); |
| } |
| |
| client_->OnDiscoveringChanged(&method_call, std::move(response)); |
| } |
| |
| void EncodeFlossDeviceId(dbus::MessageWriter* writer, |
| const FlossDeviceId& device_id, |
| bool include_required_keys, |
| bool include_extra_keys) { |
| dbus::MessageWriter array(nullptr); |
| dbus::MessageWriter dict(nullptr); |
| |
| writer->OpenArray("{sv}", &array); |
| |
| // Required keys for FlossDeviceId to parse correctly |
| if (include_required_keys) { |
| array.OpenDictEntry(&dict); |
| dict.AppendString("name"); |
| dict.AppendVariantOfString(device_id.name); |
| array.CloseContainer(&dict); |
| |
| array.OpenDictEntry(&dict); |
| dict.AppendString("address"); |
| dict.AppendVariantOfString(device_id.address); |
| array.CloseContainer(&dict); |
| } |
| |
| // Extra keys shouldn't affect parsing |
| if (include_extra_keys) { |
| array.OpenDictEntry(&dict); |
| dict.AppendString("ignored key"); |
| dict.AppendVariantOfString("ignored"); |
| array.CloseContainer(&dict); |
| } |
| |
| writer->CloseContainer(&array); |
| } |
| |
| void SendDeviceFoundCallback(bool error, |
| const FlossDeviceId& device_id, |
| dbus::ExportedObject::ResponseSender response) { |
| dbus::MethodCall method_call(adapter::kCallbackInterface, |
| adapter::kOnDeviceFound); |
| method_call.SetSerial(serial_++); |
| |
| dbus::MessageWriter writer(&method_call); |
| EncodeFlossDeviceId(&writer, device_id, |
| /*include_required_keys=*/!error, |
| /*include_extra_keys=*/true); |
| |
| client_->OnDeviceFound(&method_call, std::move(response)); |
| } |
| |
| void SendDeviceClearedCallback( |
| bool error, |
| const FlossDeviceId& device_id, |
| dbus::ExportedObject::ResponseSender response) { |
| dbus::MethodCall method_call(adapter::kCallbackInterface, |
| adapter::kOnDeviceFound); |
| method_call.SetSerial(serial_++); |
| |
| dbus::MessageWriter writer(&method_call); |
| EncodeFlossDeviceId(&writer, device_id, |
| /*include_required_keys=*/!error, |
| /*include_extra_keys=*/true); |
| |
| client_->OnDeviceCleared(&method_call, std::move(response)); |
| } |
| |
| void SendSspRequestCallback(bool error, |
| const FlossDeviceId& device_id, |
| uint32_t cod, |
| FlossAdapterClient::BluetoothSspVariant variant, |
| uint32_t passkey, |
| dbus::ExportedObject::ResponseSender response) { |
| dbus::MethodCall method_call(adapter::kCallbackInterface, |
| adapter::kOnSspRequest); |
| method_call.SetSerial(serial_++); |
| |
| dbus::MessageWriter writer(&method_call); |
| EncodeFlossDeviceId(&writer, device_id, |
| /*include_required_keys=*/!error, |
| /*include_extra_keys=*/false); |
| if (!error) { |
| writer.AppendUint32(cod); |
| writer.AppendUint32(static_cast<uint32_t>(variant)); |
| writer.AppendUint32(passkey); |
| } |
| |
| client_->OnSspRequest(&method_call, std::move(response)); |
| } |
| |
| void SendAdapterPropertyChangedCallback( |
| bool error, |
| FlossAdapterClient::BtPropertyType type, |
| dbus::ExportedObject::ResponseSender response) { |
| dbus::MethodCall method_call(adapter::kCallbackInterface, |
| adapter::kOnAdapterPropertyChanged); |
| method_call.SetSerial(serial_++); |
| |
| dbus::MessageWriter writer(&method_call); |
| writer.AppendUint32(static_cast<uint32_t>(type)); |
| |
| client_->OnAdapterPropertyChanged(&method_call, std::move(response)); |
| } |
| |
| int serial_ = 1; |
| int adapter_index_ = 5; |
| dbus::ObjectPath adapter_path_; |
| std::string adapter_address_ = "00:11:22:33:44:55"; |
| std::string adapter_name_ = "floss"; |
| bool adapter_discoverable_ = false; |
| |
| scoped_refptr<::dbus::MockBus> bus_; |
| scoped_refptr<::dbus::MockExportedObject> exported_callbacks_; |
| scoped_refptr<::dbus::MockObjectProxy> adapter_object_proxy_; |
| std::unique_ptr<FlossAdapterClient> client_; |
| |
| dbus::ExportedObject::MethodCallCallback on_address_changed_; |
| dbus::ExportedObject::MethodCallCallback on_name_changed_; |
| dbus::ExportedObject::MethodCallCallback on_discoverable_changed_; |
| |
| base::test::TaskEnvironment task_environment_; |
| base::WeakPtrFactory<FlossAdapterClientTest> weak_ptr_factory_{this}; |
| }; |
| |
| // Verify initial states and assumptions. |
| TEST_F(FlossAdapterClientTest, InitializesCorrectly) { |
| TestAdapterObserver test_observer(client_.get()); |
| |
| // Because of the specific method call expectations below, we need a catch all |
| // here to say that it is okay to have more method calls of any sort (not |
| // exclusively those specific calls). |
| EXPECT_CALL(*adapter_object_proxy_.get(), DoCallMethodWithErrorResponse) |
| .Times(testing::AnyNumber()); |
| |
| // Expected specific method calls. |
| EXPECT_CALL( |
| *adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(adapter::kGetAddress), _, _)) |
| .Times(1); |
| EXPECT_CALL( |
| *adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(adapter::kGetName), _, _)) |
| .Times(1); |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kGetDiscoverable), _, _)) |
| .Times(1); |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kRegisterCallback), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 1 parameter. |
| dbus::ObjectPath param1; |
| ASSERT_TRUE(msg.PopObjectPath(¶m1)); |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with uint32_t return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendUint32(kFakeCallbackId); |
| std::move(*cb).Run(response.get(), /*err=*/nullptr); |
| }); |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kRegisterConnectionCallback), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 1 parameter. |
| dbus::ObjectPath param1; |
| ASSERT_TRUE(msg.PopObjectPath(¶m1)); |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with uint32_t return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendUint32(kFakeConnectionCallbackId); |
| std::move(*cb).Run(response.get(), /*err=*/nullptr); |
| }); |
| |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| |
| // Make sure the address is initialized correctly |
| EXPECT_EQ(test_observer.address_changed_count_, 1); |
| EXPECT_EQ(client_->GetAddress(), adapter_address_); |
| |
| // Make sure name is initialized correctly |
| EXPECT_EQ(client_->GetName(), adapter_name_); |
| |
| // Make sure discoverable is initialized correctly |
| EXPECT_EQ(test_observer.discoverable_changed_count_, 1); |
| EXPECT_EQ(client_->GetDiscoverable(), adapter_discoverable_); |
| |
| // Make sure to unregister callbacks when client is destroyed |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kUnregisterCallback), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 1 parameter. |
| uint32_t param1; |
| ASSERT_TRUE(FlossDBusClient::ReadAllDBusParams(&msg, ¶m1)); |
| EXPECT_EQ(kFakeCallbackId, param1); |
| EXPECT_FALSE(msg.HasMoreData()); |
| }); |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kUnregisterConnectionCallback), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 1 parameter. |
| uint32_t param1; |
| ASSERT_TRUE(FlossDBusClient::ReadAllDBusParams(&msg, ¶m1)); |
| EXPECT_EQ(kFakeConnectionCallbackId, param1); |
| EXPECT_FALSE(msg.HasMoreData()); |
| }); |
| } |
| |
| TEST_F(FlossAdapterClientTest, HandlesAddressChanges) { |
| TestAdapterObserver test_observer(client_.get()); |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| EXPECT_EQ(test_observer.address_changed_count_, 1); |
| |
| SendAddressChangeCallback( |
| /*error=*/true, /*address=*/std::string(), |
| base::BindOnce(&FlossAdapterClientTest::ExpectErrorResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(test_observer.address_changed_count_, 1); |
| |
| std::string test_address("11:22:33:44:55:66"); |
| SendAddressChangeCallback( |
| /*error=*/false, test_address, |
| base::BindOnce(&FlossAdapterClientTest::ExpectNormalResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(test_observer.address_changed_count_, 2); |
| EXPECT_EQ(test_observer.address_, test_address); |
| EXPECT_EQ(client_->GetAddress(), test_address); |
| } |
| |
| TEST_F(FlossAdapterClientTest, HandlesNameChanges) { |
| TestAdapterObserver test_observer(client_.get()); |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| |
| std::string test_name("floss_test_name"); |
| SendNameChangeCallback( |
| /*error=*/false, test_name, |
| base::BindOnce(&FlossAdapterClientTest::ExpectNormalResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(client_->GetName(), test_name); |
| } |
| |
| TEST_F(FlossAdapterClientTest, HandlesDiscoverableChanges) { |
| TestAdapterObserver test_observer(client_.get()); |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| EXPECT_EQ(test_observer.discoverable_changed_count_, 1); |
| |
| SendDiscoverableChangeCallback( |
| /*error=*/true, /*discoverable=*/true, |
| base::BindOnce(&FlossAdapterClientTest::ExpectErrorResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(test_observer.discoverable_changed_count_, 1); |
| EXPECT_EQ(test_observer.discoverable_, false); |
| EXPECT_EQ(client_->GetDiscoverable(), false); |
| |
| SendDiscoverableChangeCallback( |
| /*error=*/false, /*discoverable=*/true, |
| base::BindOnce(&FlossAdapterClientTest::ExpectNormalResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(test_observer.discoverable_changed_count_, 2); |
| EXPECT_EQ(test_observer.discoverable_, true); |
| EXPECT_EQ(client_->GetDiscoverable(), true); |
| } |
| |
| TEST_F(FlossAdapterClientTest, HandlesDiscoveryChanges) { |
| TestAdapterObserver test_observer(client_.get()); |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| EXPECT_EQ(test_observer.discovering_changed_count_, 0); |
| |
| SendDiscoveringChangeCallback( |
| /*error=*/true, /*state=*/false, |
| base::BindOnce(&FlossAdapterClientTest::ExpectErrorResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(test_observer.discovering_changed_count_, 0); |
| EXPECT_FALSE(test_observer.discovering_state_); |
| |
| // Adapter client doesn't cache the discovering state and will just forward it |
| // without checking if it's the same as before. |
| SendDiscoveringChangeCallback( |
| /*error=*/false, /*state=*/false, |
| base::BindOnce(&FlossAdapterClientTest::ExpectNormalResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| EXPECT_EQ(test_observer.discovering_changed_count_, 1); |
| EXPECT_FALSE(test_observer.discovering_state_); |
| |
| SendDiscoveringChangeCallback( |
| /*error=*/false, /*state=*/true, |
| base::BindOnce(&FlossAdapterClientTest::ExpectNormalResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| EXPECT_EQ(test_observer.discovering_changed_count_, 2); |
| EXPECT_TRUE(test_observer.discovering_state_); |
| } |
| |
| TEST_F(FlossAdapterClientTest, HandlesFoundDevices) { |
| TestAdapterObserver test_observer(client_.get()); |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| EXPECT_EQ(test_observer.found_device_count_, 0); |
| |
| FlossDeviceId device_id = {.address = "66:55:44:33:22:11", .name = "First"}; |
| |
| SendDeviceFoundCallback( |
| /*error=*/true, device_id, |
| base::BindOnce(&FlossAdapterClientTest::ExpectErrorResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(test_observer.found_device_count_, 0); |
| SendDeviceFoundCallback( |
| /*error=*/false, device_id, |
| base::BindOnce(&FlossAdapterClientTest::ExpectNormalResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(test_observer.found_device_count_, 1); |
| EXPECT_EQ(test_observer.found_device_.name, device_id.name); |
| EXPECT_EQ(test_observer.found_device_.address, device_id.address); |
| } |
| |
| TEST_F(FlossAdapterClientTest, HandlesClearedDevices) { |
| TestAdapterObserver test_observer(client_.get()); |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| EXPECT_EQ(test_observer.cleared_device_count_, 0); |
| |
| FlossDeviceId device_id = {.address = "66:55:44:33:22:11", .name = "First"}; |
| |
| SendDeviceClearedCallback( |
| /*error=*/true, device_id, |
| base::BindOnce(&FlossAdapterClientTest::ExpectErrorResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(test_observer.cleared_device_count_, 0); |
| SendDeviceClearedCallback( |
| /*error=*/false, device_id, |
| base::BindOnce(&FlossAdapterClientTest::ExpectNormalResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(test_observer.cleared_device_count_, 1); |
| EXPECT_EQ(test_observer.cleared_device_.name, device_id.name); |
| EXPECT_EQ(test_observer.cleared_device_.address, device_id.address); |
| } |
| |
| TEST_F(FlossAdapterClientTest, HandlesSsp) { |
| TestAdapterObserver test_observer(client_.get()); |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| EXPECT_EQ(test_observer.ssp_request_count_, 0); |
| |
| FlossDeviceId device_id = {.address = "11:22:33:66:55:44", .name = "Foobar"}; |
| auto variant = FlossAdapterClient::BluetoothSspVariant::kPasskeyNotification; |
| uint32_t cod = 0x0c0dc0d0; |
| uint32_t passkey = 0xc0dec0de; |
| |
| SendSspRequestCallback( |
| /*error=*/true, device_id, cod, variant, passkey, |
| base::BindOnce(&FlossAdapterClientTest::ExpectErrorResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(test_observer.ssp_request_count_, 0); |
| SendSspRequestCallback( |
| /*error=*/false, device_id, cod, variant, passkey, |
| base::BindOnce(&FlossAdapterClientTest::ExpectNormalResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| EXPECT_EQ(test_observer.ssp_request_count_, 1); |
| EXPECT_EQ(test_observer.ssp_device_.name, device_id.name); |
| EXPECT_EQ(test_observer.ssp_device_.address, device_id.address); |
| EXPECT_EQ(test_observer.cod_, cod); |
| EXPECT_EQ(test_observer.variant_, variant); |
| EXPECT_EQ(test_observer.passkey_, passkey); |
| } |
| |
| TEST_F(FlossAdapterClientTest, CreateBond) { |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| |
| FlossDeviceId bond = {.address = "00:22:44:11:33:55", .name = "James"}; |
| auto transport = FlossAdapterClient::BluetoothTransport::kBrEdr; |
| |
| EXPECT_CALL( |
| *adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(adapter::kCreateBond), _, _)) |
| .WillOnce([this, &bond, &transport]( |
| ::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| HandleCreateBond(bond, transport, method_call, timeout_ms, cb); |
| }); |
| |
| client_->CreateBond( |
| base::BindOnce(&FlossAdapterClientTest::ExpectValidCreateBond, |
| weak_ptr_factory_.GetWeakPtr()), |
| bond, transport); |
| } |
| |
| TEST_F(FlossAdapterClientTest, CallAdapterMethods) { |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| |
| // Method of 0 parameters with no return. |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(kTestMethod0), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have no parameters. |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with no return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| std::move(*cb).Run(response.get(), nullptr); |
| }); |
| client_->CallAdapterMethod(base::BindOnce([](DBusResult<Void> ret) { |
| // Check that there should be no error. |
| EXPECT_TRUE(ret.has_value()); |
| }), |
| kTestMethod0); |
| |
| // Method of 0 parameters with uint8_t return. |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(kTestMethod0), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have no parameters. |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with a return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendByte(kFakeU8Return); |
| std::move(*cb).Run(response.get(), nullptr); |
| }); |
| client_->CallAdapterMethod(base::BindOnce([](DBusResult<uint8_t> ret) { |
| // Check that return is correctly parsed and |
| // there should be no error. |
| EXPECT_TRUE(ret.has_value()); |
| EXPECT_EQ(100, *ret); |
| }), |
| kTestMethod0); |
| |
| // Method of 1 parameter with string return. |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(kTestMethod1), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 1 parameter. |
| uint32_t param1; |
| ASSERT_TRUE(msg.PopUint32(¶m1)); |
| EXPECT_EQ(kFakeU32Param, param1); |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with a return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendString(kFakeStrReturn); |
| std::move(*cb).Run(response.get(), nullptr); |
| }); |
| client_->CallAdapterMethod(base::BindOnce([](DBusResult<std::string> ret) { |
| // Check that return is correctly parsed and |
| // there should be no error. |
| EXPECT_TRUE(ret.has_value()); |
| EXPECT_EQ(kFakeStrReturn, *ret); |
| }), |
| kTestMethod1, kFakeU32Param); |
| |
| // Method of 2 parameters with no return. |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(kTestMethod2), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 2 parameter. |
| uint32_t param1; |
| std::string param2; |
| ASSERT_TRUE(msg.PopUint32(¶m1)); |
| ASSERT_TRUE(msg.PopString(¶m2)); |
| EXPECT_EQ(kFakeU32Param, param1); |
| EXPECT_EQ(kFakeStrParam, param2); |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with no return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| std::move(*cb).Run(response.get(), nullptr); |
| }); |
| std::string str_param(kFakeStrParam); |
| client_->CallAdapterMethod(base::BindOnce([](DBusResult<Void> ret) { |
| // Check that there should be no error. |
| EXPECT_TRUE(ret.has_value()); |
| }), |
| kTestMethod2, kFakeU32Param, str_param); |
| |
| // Method of 0 parameters with invalid return. |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(kTestMethod0), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have no parameters. |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with a return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendUint32(kFakeU8Return); |
| std::move(*cb).Run(response.get(), nullptr); |
| }); |
| client_->CallAdapterMethod(base::BindOnce([](DBusResult<uint8_t> ret) { |
| // Check that return cannot be parsed and there |
| // should be an error. |
| EXPECT_FALSE(ret.has_value()); |
| EXPECT_EQ(FlossDBusClient::kErrorInvalidReturn, |
| ret.error().ToString()); |
| }), |
| kTestMethod0); |
| } |
| |
| TEST_F(FlossAdapterClientTest, GenericMethodGetConnectionState) { |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| |
| // Method of 1 parameter with uint32_t return. |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kGetConnectionState), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 1 parameter. |
| FlossDeviceId param1; |
| ASSERT_TRUE(FlossAdapterClient::ReadAllDBusParams(&msg, ¶m1)); |
| EXPECT_EQ(FlossDeviceId( |
| {.address = kFakeDeviceAddr, .name = kFakeDeviceName}), |
| param1); |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with a return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendUint32(kFakeU32Return); |
| std::move(*cb).Run(response.get(), /*err=*/nullptr); |
| }); |
| base::RunLoop run_loop; |
| client_->GetConnectionState( |
| base::BindLambdaForTesting([&run_loop](DBusResult<uint32_t> ret) { |
| // Check that return is correctly parsed and there should be no |
| // error. |
| EXPECT_TRUE(ret.has_value()); |
| EXPECT_EQ(kFakeU32Return, *ret); |
| run_loop.Quit(); |
| }), |
| FlossDeviceId({.address = kFakeDeviceAddr, .name = kFakeDeviceName})); |
| run_loop.Run(); |
| } |
| |
| TEST_F(FlossAdapterClientTest, |
| GenericMethodConnectAndDisconnectAllEnabledProfiles) { |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| |
| // Method of 1 parameter with no return. |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kConnectAllEnabledProfiles), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 1 parameter. |
| FlossDeviceId param1; |
| ASSERT_TRUE(FlossAdapterClient::ReadAllDBusParams(&msg, ¶m1)); |
| EXPECT_EQ(FlossDeviceId( |
| {.address = kFakeDeviceAddr, .name = kFakeDeviceName}), |
| param1); |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with no return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| std::move(*cb).Run(response.get(), /*err=*/nullptr); |
| }); |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kDisconnectAllEnabledProfiles), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 1 parameter. |
| FlossDeviceId param1; |
| ASSERT_TRUE(FlossAdapterClient::ReadAllDBusParams(&msg, ¶m1)); |
| EXPECT_EQ(FlossDeviceId( |
| {.address = kFakeDeviceAddr, .name = kFakeDeviceName}), |
| param1); |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with no return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| std::move(*cb).Run(response.get(), /*err=*/nullptr); |
| }); |
| base::RunLoop run_loop; |
| client_->ConnectAllEnabledProfiles( |
| base::BindLambdaForTesting([&run_loop](DBusResult<Void> ret) { |
| // Check that there should be no error. |
| EXPECT_TRUE(ret.has_value()); |
| run_loop.Quit(); |
| }), |
| FlossDeviceId({.address = kFakeDeviceAddr, .name = kFakeDeviceName})); |
| client_->DisconnectAllEnabledProfiles( |
| base::BindLambdaForTesting([&run_loop](DBusResult<Void> ret) { |
| // Check that there should be no error. |
| EXPECT_TRUE(ret.has_value()); |
| run_loop.Quit(); |
| }), |
| FlossDeviceId({.address = kFakeDeviceAddr, .name = kFakeDeviceName})); |
| run_loop.Run(); |
| } |
| |
| TEST_F(FlossAdapterClientTest, GenericMethodSetPairingConfirmation) { |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| |
| // Method of 2 parameters with no return. |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kSetPairingConfirmation), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 2 parameters. |
| FlossDeviceId param1; |
| bool param2; |
| ASSERT_TRUE(FlossAdapterClient::ReadAllDBusParams(&msg, ¶m1)); |
| ASSERT_TRUE(msg.PopBool(¶m2)); |
| EXPECT_EQ(FlossDeviceId( |
| {.address = kFakeDeviceAddr, .name = kFakeDeviceName}), |
| param1); |
| EXPECT_EQ(kFakeBoolParam, param2); |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with no return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| std::move(*cb).Run(response.get(), /*err=*/nullptr); |
| }); |
| base::RunLoop run_loop; |
| client_->SetPairingConfirmation( |
| base::BindLambdaForTesting([&run_loop](DBusResult<Void> ret) { |
| // Check that there should be no error. |
| EXPECT_TRUE(ret.has_value()); |
| run_loop.Quit(); |
| }), |
| FlossDeviceId({.address = kFakeDeviceAddr, .name = kFakeDeviceName}), |
| kFakeBoolParam); |
| run_loop.Run(); |
| } |
| |
| TEST_F(FlossAdapterClientTest, GenericMethodSetPasskey) { |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| |
| // Method of 3 parameters with no return. |
| EXPECT_CALL( |
| *adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(adapter::kSetPasskey), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 3 parameters. |
| FlossDeviceId param1; |
| bool param2; |
| const uint8_t* param3; |
| size_t param3_len; |
| ASSERT_TRUE(FlossAdapterClient::ReadAllDBusParams(&msg, ¶m1)); |
| ASSERT_TRUE(msg.PopBool(¶m2)); |
| ASSERT_TRUE(msg.PopArrayOfBytes(¶m3, ¶m3_len)); |
| EXPECT_EQ(FlossDeviceId( |
| {.address = kFakeDeviceAddr, .name = kFakeDeviceName}), |
| param1); |
| EXPECT_EQ(kFakeBoolParam, param2); |
| EXPECT_EQ( |
| std::vector<uint8_t>(kFakeBytes, kFakeBytes + sizeof(kFakeBytes)), |
| std::vector<uint8_t>(param3, param3 + param3_len)); |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a fake response with no return value. |
| auto response = ::dbus::Response::CreateEmpty(); |
| std::move(*cb).Run(response.get(), /*err=*/nullptr); |
| }); |
| base::RunLoop run_loop; |
| client_->SetPasskey( |
| base::BindLambdaForTesting([&run_loop](DBusResult<Void> ret) { |
| // Check that there should be no error. |
| EXPECT_TRUE(ret.has_value()); |
| run_loop.Quit(); |
| }), |
| FlossDeviceId({.address = kFakeDeviceAddr, .name = kFakeDeviceName}), |
| kFakeBoolParam, |
| std::vector<uint8_t>(kFakeBytes, kFakeBytes + sizeof(kFakeBytes))); |
| run_loop.Run(); |
| } |
| |
| TEST_F(FlossAdapterClientTest, GenericMethodGetRemoteUuids) { |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| |
| // Method of 1 parameter with UUID response. |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kGetRemoteUuids), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 1 parameter. |
| FlossDeviceId param1; |
| ASSERT_TRUE(FlossAdapterClient::ReadAllDBusParams(&msg, ¶m1)); |
| EXPECT_EQ(FlossDeviceId( |
| {.address = kFakeDeviceAddr, .name = kFakeDeviceName}), |
| param1); |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a response with valid UUID. Format is array of UUIDs (array of |
| // bytes) |
| auto response = ::dbus::Response::CreateEmpty(); |
| dbus::MessageWriter writer(response.get()); |
| dbus::MessageWriter array_writer(nullptr); |
| writer.OpenArray("ay", &array_writer); |
| array_writer.AppendArrayOfBytes(kFakeUuidByteArray, |
| sizeof(kFakeUuidByteArray)); |
| writer.CloseContainer(&array_writer); |
| std::move(*cb).Run(response.get(), /*err=*/nullptr); |
| }); |
| base::RunLoop run_loop; |
| client_->GetRemoteUuids( |
| base::BindLambdaForTesting( |
| [&run_loop](DBusResult<device::BluetoothDevice::UUIDList> ret) { |
| // Check that there is no error. |
| EXPECT_TRUE(ret.has_value()); |
| // Check we parse the returned UUID correctly |
| device::BluetoothDevice::UUIDList uuid_list = *ret; |
| EXPECT_EQ(uuid_list[0], device::BluetoothUUID(kFakeUuidStr)); |
| run_loop.Quit(); |
| }), |
| FlossDeviceId({.address = kFakeDeviceAddr, .name = kFakeDeviceName})); |
| run_loop.Run(); |
| } |
| |
| TEST_F(FlossAdapterClientTest, GenericMethodGetRemoteType) { |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| |
| // Method of 1 parameter with BluetoothDeviceType response. |
| EXPECT_CALL( |
| *adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse(HasMemberOf(adapter::kGetRemoteType), _, _)) |
| .WillOnce([](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have 1 parameter. |
| FlossDeviceId param1; |
| ASSERT_TRUE(FlossAdapterClient::ReadAllDBusParams(&msg, ¶m1)); |
| EXPECT_EQ(FlossDeviceId( |
| {.address = kFakeDeviceAddr, .name = kFakeDeviceName}), |
| param1); |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a response with valid BluetoothDeviceType |
| auto response = ::dbus::Response::CreateEmpty(); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendUint32(static_cast<uint32_t>(kFakeType)); |
| std::move(*cb).Run(response.get(), /*err=*/nullptr); |
| }); |
| base::RunLoop run_loop; |
| client_->GetRemoteType( |
| base::BindLambdaForTesting( |
| [&run_loop]( |
| DBusResult<floss::FlossAdapterClient::BluetoothDeviceType> ret) { |
| // Check that there is no error. |
| EXPECT_TRUE(ret.has_value()); |
| // Check we parse the returned type correctly |
| EXPECT_EQ(*ret, kFakeType); |
| run_loop.Quit(); |
| }), |
| FlossDeviceId({.address = kFakeDeviceAddr, .name = kFakeDeviceName})); |
| run_loop.Run(); |
| } |
| |
| TEST_F(FlossAdapterClientTest, OnAdapterPropertyChanged) { |
| TestAdapterObserver test_observer(client_.get()); |
| client_->Init(bus_.get(), kAdapterInterface, adapter_index_, |
| base::DoNothing()); |
| EXPECT_EQ(test_observer.found_device_count_, 0); |
| |
| // Method of no parameters with vector of FlossDeviceId response. |
| EXPECT_CALL(*adapter_object_proxy_.get(), |
| DoCallMethodWithErrorResponse( |
| HasMemberOf(adapter::kGetBondedDevices), _, _)) |
| .WillOnce([this](::dbus::MethodCall* method_call, int timeout_ms, |
| ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) { |
| dbus::MessageReader msg(method_call); |
| // D-Bus method call should have no parameters. |
| EXPECT_FALSE(msg.HasMoreData()); |
| // Create a response with valid array of FlossDeviceIds |
| FlossDeviceId device_id = {.address = "66:55:44:33:22:11", |
| .name = "First"}; |
| auto response = ::dbus::Response::CreateEmpty(); |
| dbus::MessageWriter writer(response.get()); |
| dbus::MessageWriter array(nullptr); |
| writer.OpenArray("a{sv}", &array); |
| this->EncodeFlossDeviceId(&array, device_id, |
| /*include_required_keys=*/true, |
| /*include_extra_keys=*/false); |
| writer.CloseContainer(&array); |
| std::move(*cb).Run(response.get(), /*err=*/nullptr); |
| }); |
| SendAdapterPropertyChangedCallback( |
| /*error=*/false, |
| FlossAdapterClient::BtPropertyType::kAdapterBondedDevices, |
| base::BindOnce(&FlossAdapterClientTest::ExpectNormalResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| EXPECT_EQ(test_observer.found_device_count_, 1); |
| } |
| |
| } // namespace floss |