| // Copyright 2018 The Chromium 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 "device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.h" |
| |
| #import <CoreBluetooth/CoreBluetooth.h> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/test/test_simple_task_runner.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/ocmock/OCMock/OCMock.h" |
| |
| namespace { |
| const char kTestUUID[] = "00000000-1111-2222-3333-444444444444"; |
| } // namespace |
| |
| namespace device { |
| |
| class BluetoothLowEnergyAdvertisementManagerMacTest : public testing::Test { |
| public: |
| BluetoothLowEnergyAdvertisementManagerMacTest() |
| : ui_task_runner_(new base::TestSimpleTaskRunner()), |
| peripheral_manager_state_(CBPeripheralManagerStatePoweredOn), |
| unregister_success_(false) {} |
| |
| void SetUp() override { |
| peripheral_manager_ = OCMClassMock([CBPeripheralManager class]); |
| peripheral_manager_mock_ = peripheral_manager_; |
| OCMStub([peripheral_manager_ state]).andDo(^(NSInvocation* invocation) { |
| [invocation setReturnValue:&peripheral_manager_state_]; |
| }); |
| |
| advertisement_manager_.Init(ui_task_runner_, peripheral_manager_); |
| } |
| |
| void OnAdvertisementRegistered( |
| scoped_refptr<BluetoothAdvertisement> advertisement) { |
| ASSERT_FALSE(advertisement_.get()); |
| advertisement_ = advertisement; |
| } |
| |
| void OnAdvertisementRegisterError( |
| BluetoothAdvertisement::ErrorCode error_code) { |
| ASSERT_FALSE(registration_error_.get()); |
| registration_error_.reset( |
| new BluetoothAdvertisement::ErrorCode(error_code)); |
| } |
| |
| void OnUnregisterSuccess() { |
| ASSERT_FALSE(unregister_success_); |
| unregister_success_ = true; |
| } |
| |
| void OnUnregisterError(BluetoothAdvertisement::ErrorCode error_code) { |
| ASSERT_FALSE(unregister_error_); |
| unregister_error_.reset(new BluetoothAdvertisement::ErrorCode(error_code)); |
| } |
| |
| std::unique_ptr<BluetoothAdvertisement::Data> CreateAdvertisementData() { |
| std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data = |
| std::make_unique<BluetoothAdvertisement::Data>( |
| BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST); |
| |
| std::unique_ptr<BluetoothAdvertisement::UUIDList> uuid_list = |
| std::make_unique<BluetoothAdvertisement::UUIDList>(); |
| uuid_list->push_back(kTestUUID); |
| |
| advertisement_data->set_service_uuids(std::move(uuid_list)); |
| |
| return advertisement_data; |
| } |
| |
| void RegisterAdvertisement( |
| std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data) { |
| advertisement_manager_.RegisterAdvertisement( |
| std::move(advertisement_data), |
| base::Bind(&BluetoothLowEnergyAdvertisementManagerMacTest:: |
| OnAdvertisementRegistered, |
| base::Unretained(this)), |
| base::Bind(&BluetoothLowEnergyAdvertisementManagerMacTest:: |
| OnAdvertisementRegisterError, |
| base::Unretained(this))); |
| } |
| |
| void Unregister(scoped_refptr<BluetoothAdvertisement> advertisement) { |
| advertisement->Unregister( |
| base::Bind( |
| &BluetoothLowEnergyAdvertisementManagerMacTest::OnUnregisterSuccess, |
| base::Unretained(this)), |
| base::Bind( |
| &BluetoothLowEnergyAdvertisementManagerMacTest::OnUnregisterError, |
| base::Unretained(this))); |
| } |
| |
| protected: |
| scoped_refptr<base::TestSimpleTaskRunner> ui_task_runner_; |
| BluetoothLowEnergyAdvertisementManagerMac advertisement_manager_; |
| CBPeripheralManager* peripheral_manager_; |
| id peripheral_manager_mock_; |
| CBPeripheralManagerState peripheral_manager_state_; |
| |
| scoped_refptr<BluetoothAdvertisement> advertisement_; |
| std::unique_ptr<BluetoothAdvertisement::ErrorCode> registration_error_; |
| |
| bool unregister_success_; |
| std::unique_ptr<BluetoothAdvertisement::ErrorCode> unregister_error_; |
| }; |
| |
| TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, |
| Register_AdapterPoweredOff) { |
| // Simulate adapter being powered off. |
| peripheral_manager_state_ = CBPeripheralManagerStatePoweredOff; |
| |
| RegisterAdvertisement(CreateAdvertisementData()); |
| ui_task_runner_->RunPendingTasks(); |
| |
| EXPECT_FALSE(advertisement_); |
| ASSERT_TRUE(registration_error_); |
| EXPECT_EQ(BluetoothAdvertisement::ERROR_ADAPTER_POWERED_OFF, |
| *registration_error_); |
| } |
| |
| TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, |
| Register_AdapterInitializing_ThenUnsupported) { |
| // Simulate adapter state being unknown and is initializing. |
| peripheral_manager_state_ = CBPeripheralManagerStateUnknown; |
| |
| // The advertisement will not start or fail until the adapter state is |
| // initialized. |
| RegisterAdvertisement(CreateAdvertisementData()); |
| ui_task_runner_->RunPendingTasks(); |
| EXPECT_FALSE(advertisement_); |
| EXPECT_FALSE(registration_error_); |
| |
| // Change the adapter state to CBPeripheralManagerStateUnsupported, which |
| // causes the registration to fail. |
| peripheral_manager_state_ = CBPeripheralManagerStateUnsupported; |
| advertisement_manager_.OnPeripheralManagerStateChanged(); |
| ui_task_runner_->RunPendingTasks(); |
| EXPECT_FALSE(advertisement_); |
| ASSERT_TRUE(registration_error_); |
| EXPECT_EQ(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM, |
| *registration_error_); |
| } |
| |
| TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, |
| Register_AdapterInitializing_ThenPoweredOff) { |
| // Simulate adapter state being unknown and is initializing. |
| peripheral_manager_state_ = CBPeripheralManagerStateUnknown; |
| |
| // The advertisement will not start or fail until the adapter state is |
| // initialized. |
| RegisterAdvertisement(CreateAdvertisementData()); |
| ui_task_runner_->RunPendingTasks(); |
| EXPECT_FALSE(advertisement_); |
| EXPECT_FALSE(registration_error_); |
| |
| // Change the adapter state to CBPeripheralManagerStatePoweredOff, which |
| // causes the registration to fail. |
| peripheral_manager_state_ = CBPeripheralManagerStatePoweredOff; |
| advertisement_manager_.OnPeripheralManagerStateChanged(); |
| ui_task_runner_->RunPendingTasks(); |
| EXPECT_FALSE(advertisement_); |
| ASSERT_TRUE(registration_error_); |
| EXPECT_EQ(BluetoothAdvertisement::ERROR_ADAPTER_POWERED_OFF, |
| *registration_error_); |
| } |
| |
| TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, |
| Register_ExistingAdvertisement) { |
| // Start an advertisement. |
| RegisterAdvertisement(CreateAdvertisementData()); |
| advertisement_manager_.DidStartAdvertising(nil); |
| ui_task_runner_->RunPendingTasks(); |
| ASSERT_TRUE(advertisement_); |
| |
| // Only one advertisement is supported. |
| RegisterAdvertisement(CreateAdvertisementData()); |
| ui_task_runner_->RunPendingTasks(); |
| ASSERT_TRUE(registration_error_); |
| EXPECT_EQ(BluetoothAdvertisement::ERROR_ADVERTISEMENT_ALREADY_EXISTS, |
| *registration_error_); |
| } |
| |
| TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, Register_InvalidData) { |
| std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data = |
| CreateAdvertisementData(); |
| |
| // Advertising service data is not supported. |
| std::unique_ptr<BluetoothAdvertisement::ServiceData> service_data = |
| std::make_unique<BluetoothAdvertisement::ServiceData>(); |
| (*service_data)[kTestUUID] = std::vector<uint8_t>(10); |
| advertisement_data->set_service_data(std::move(service_data)); |
| |
| RegisterAdvertisement(std::move(advertisement_data)); |
| ui_task_runner_->RunPendingTasks(); |
| |
| EXPECT_FALSE(advertisement_); |
| ASSERT_TRUE(registration_error_); |
| EXPECT_EQ(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM, |
| *registration_error_); |
| } |
| |
| TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, Register_Success) { |
| OCMExpect([peripheral_manager_ startAdvertising:[OCMArg any]]); |
| |
| RegisterAdvertisement(CreateAdvertisementData()); |
| advertisement_manager_.DidStartAdvertising(nil); |
| ui_task_runner_->RunPendingTasks(); |
| |
| EXPECT_TRUE(advertisement_); |
| EXPECT_FALSE(registration_error_); |
| |
| [peripheral_manager_mock_ verifyAtLocation:nil]; |
| } |
| |
| TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, Unregister_Success) { |
| RegisterAdvertisement(CreateAdvertisementData()); |
| advertisement_manager_.DidStartAdvertising(nil); |
| ui_task_runner_->RunPendingTasks(); |
| ASSERT_TRUE(advertisement_); |
| |
| OCMExpect([peripheral_manager_ stopAdvertising]); |
| Unregister(advertisement_); |
| ui_task_runner_->RunPendingTasks(); |
| |
| EXPECT_TRUE(unregister_success_); |
| EXPECT_FALSE(unregister_error_); |
| [peripheral_manager_mock_ verifyAtLocation:nil]; |
| } |
| |
| TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, |
| Unregister_InvalidAdvertisement) { |
| scoped_refptr<BluetoothAdvertisementMac> invalid_advertisement = |
| new BluetoothAdvertisementMac( |
| std::make_unique<BluetoothAdvertisement::UUIDList>(), |
| base::DoNothing(), base::DoNothing(), &advertisement_manager_); |
| |
| // Register advertisement. |
| RegisterAdvertisement(CreateAdvertisementData()); |
| advertisement_manager_.DidStartAdvertising(nil); |
| ui_task_runner_->RunPendingTasks(); |
| ASSERT_TRUE(advertisement_); |
| |
| // Try to unregister the invalid advertisement. |
| Unregister(invalid_advertisement); |
| ui_task_runner_->RunPendingTasks(); |
| EXPECT_FALSE(unregister_success_); |
| ASSERT_TRUE(unregister_error_); |
| } |
| |
| TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, Register_Twice) { |
| // Register one advertisement. |
| RegisterAdvertisement(CreateAdvertisementData()); |
| advertisement_manager_.DidStartAdvertising(nil); |
| ui_task_runner_->RunPendingTasks(); |
| EXPECT_TRUE(advertisement_); |
| |
| // Unregister the advertisement. |
| Unregister(advertisement_); |
| advertisement_ = nullptr; |
| |
| // Register another advertisement. |
| RegisterAdvertisement(CreateAdvertisementData()); |
| advertisement_manager_.DidStartAdvertising(nil); |
| ui_task_runner_->RunPendingTasks(); |
| EXPECT_TRUE(advertisement_); |
| } |
| |
| TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, |
| AdapterReset_RestartAdvertising) { |
| // Register advertisement. |
| RegisterAdvertisement(CreateAdvertisementData()); |
| advertisement_manager_.DidStartAdvertising(nil); |
| ui_task_runner_->RunPendingTasks(); |
| EXPECT_TRUE(advertisement_); |
| |
| // Reset the adapter (i.e. on system crash). |
| peripheral_manager_state_ = CBPeripheralManagerStateResetting; |
| advertisement_manager_.OnPeripheralManagerStateChanged(); |
| advertisement_ = nullptr; |
| peripheral_manager_state_ = CBPeripheralManagerStatePoweredOn; |
| |
| // Registering another advertisement should succeed. |
| OCMExpect([peripheral_manager_ startAdvertising:[OCMArg any]]); |
| RegisterAdvertisement(CreateAdvertisementData()); |
| advertisement_manager_.DidStartAdvertising(nil); |
| ui_task_runner_->RunPendingTasks(); |
| EXPECT_TRUE(advertisement_); |
| [peripheral_manager_mock_ verifyAtLocation:nil]; |
| } |
| |
| TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, |
| Advertising_ThenAdapterPoweredOff_ThenReregister) { |
| // Register advertisement. |
| RegisterAdvertisement(CreateAdvertisementData()); |
| advertisement_manager_.DidStartAdvertising(nil); |
| ui_task_runner_->RunPendingTasks(); |
| EXPECT_TRUE(advertisement_); |
| |
| // Power off the adapter. Advertisement should be stopped. |
| OCMExpect([peripheral_manager_ stopAdvertising]); |
| peripheral_manager_state_ = CBPeripheralManagerStatePoweredOff; |
| advertisement_manager_.OnPeripheralManagerStateChanged(); |
| [peripheral_manager_mock_ verifyAtLocation:nil]; |
| |
| // Register a new advertisement after powering back on the adapter. |
| // This should fail as the caller needs to manually unregister the |
| // advertisement. |
| peripheral_manager_state_ = CBPeripheralManagerStatePoweredOn; |
| advertisement_ = nullptr; |
| RegisterAdvertisement(CreateAdvertisementData()); |
| ui_task_runner_->RunPendingTasks(); |
| EXPECT_FALSE(advertisement_); |
| ASSERT_TRUE(registration_error_); |
| EXPECT_EQ(BluetoothAdvertisement::ERROR_ADVERTISEMENT_ALREADY_EXISTS, |
| *registration_error_); |
| } |
| |
| } // namespace device |