// Copyright 2013 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/bluetooth_adapter_mac.h"
#import <IOBluetooth/IOBluetooth.h>
#include <memory>
#include "base/memory/raw_ptr.h"
#import "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/test/test_simple_task_runner.h"
#include "device/bluetooth/bluetooth_classic_device_mac.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#import "device/bluetooth/test/test_bluetooth_adapter_observer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
namespace {
class MockBluetoothClassicDeviceMac : public BluetoothClassicDeviceMac {
public:
MockBluetoothClassicDeviceMac(BluetoothAdapterMac* adapter,
IOBluetoothDevice* device)
: BluetoothClassicDeviceMac(adapter, device) {}
MOCK_METHOD(UUIDSet, GetUUIDs, (), (const, override));
MOCK_METHOD(std::string, GetAddress, (), (const override));
};
} // namespace
using ::testing::Return;
class BluetoothAdapterMacTest : public testing::Test {
public:
BluetoothAdapterMacTest()
: ui_task_runner_(new base::TestSimpleTaskRunner()),
adapter_(new BluetoothAdapterMac()),
adapter_mac_(static_cast<BluetoothAdapterMac*>(adapter_.get())),
observer_(adapter_) {
adapter_mac_->InitForTest(ui_task_runner_);
}
void TearDown() override { task_environment_.RunUntilIdle(); }
// Helper methods for setup and access to BluetoothAdapterMacTest's
// members.
void PollAdapter() { adapter_mac_->PollAdapter(); }
void SetHostControllerPowerFunction(bool powered) {
adapter_mac_->SetHostControllerStateFunctionForTesting(
base::BindLambdaForTesting([powered] {
BluetoothAdapterMac::HostControllerState state;
state.classic_powered = powered;
return state;
}));
}
std::unique_ptr<MockBluetoothClassicDeviceMac> CreateClassicDevice(
const std::string& device_address,
BluetoothDevice::UUIDSet uuids) {
auto device =
std::make_unique<testing::NiceMock<MockBluetoothClassicDeviceMac>>(
adapter_mac_, /*device=*/nil);
ON_CALL(*device, GetUUIDs).WillByDefault(Return(uuids));
ON_CALL(*device, GetAddress).WillByDefault(Return(device_address));
return device;
}
void AddClassicDevice(const std::string& device_address,
BluetoothDevice::UUIDSet uuids) {
auto device = CreateClassicDevice(device_address, uuids);
adapter_mac_->ClassicDeviceAdded(std::move(device));
}
void ClassicDeviceConnected(const std::string& device_address,
BluetoothDevice::UUIDSet uuids) {
auto device = CreateClassicDevice(device_address, uuids);
adapter_mac_->DeviceConnected(std::move(device));
}
protected:
base::test::TaskEnvironment task_environment_;
scoped_refptr<base::TestSimpleTaskRunner> ui_task_runner_;
scoped_refptr<BluetoothAdapter> adapter_;
raw_ptr<BluetoothAdapterMac> adapter_mac_;
TestBluetoothAdapterObserver observer_;
};
// TODO(https://crbug.com/331653043): Re-enable when passing on macOS 14 bots.
TEST_F(BluetoothAdapterMacTest, DISABLED_Poll) {
PollAdapter();
EXPECT_TRUE(ui_task_runner_->HasPendingTask());
}
// TODO(https://crbug.com/331653043): Re-enable when passing on macOS 14 bots.
TEST_F(BluetoothAdapterMacTest, DISABLED_PollAndChangePower) {
// By default the adapter is powered off, check that this expectation matches
// reality.
EXPECT_FALSE(adapter_mac_->IsPowered());
EXPECT_EQ(0, observer_.powered_changed_count());
SetHostControllerPowerFunction(true);
PollAdapter();
EXPECT_TRUE(ui_task_runner_->HasPendingTask());
ui_task_runner_->RunPendingTasks();
EXPECT_EQ(1, observer_.powered_changed_count());
EXPECT_TRUE(observer_.last_powered());
EXPECT_TRUE(adapter_mac_->IsPowered());
SetHostControllerPowerFunction(false);
PollAdapter();
EXPECT_TRUE(ui_task_runner_->HasPendingTask());
ui_task_runner_->RunPendingTasks();
EXPECT_EQ(2, observer_.powered_changed_count());
EXPECT_FALSE(observer_.last_powered());
EXPECT_FALSE(adapter_mac_->IsPowered());
}
TEST_F(BluetoothAdapterMacTest, ClassicDeviceAddedAndChanged) {
// Simulate a paired Bluetooth Classic device with one service UUID.
std::string device_address = "AA:BB:CC:DD:EE:FF";
BluetoothDevice::UUIDSet uuids;
uuids.insert(BluetoothUUID("110b"));
AddClassicDevice(device_address, uuids);
EXPECT_EQ(1, observer_.device_added_count());
EXPECT_EQ(0, observer_.device_changed_count());
EXPECT_EQ(observer_.last_device_address(), device_address);
observer_.Reset();
// Adding the same device again does not notify observers.
AddClassicDevice(device_address, uuids);
EXPECT_EQ(0, observer_.device_added_count());
EXPECT_EQ(0, observer_.device_changed_count());
observer_.Reset();
// Update the device by adding a second service UUID.
uuids.insert(BluetoothUUID("110c"));
AddClassicDevice(device_address, uuids);
EXPECT_EQ(0, observer_.device_added_count());
EXPECT_EQ(1, observer_.device_changed_count());
EXPECT_EQ(observer_.last_device_address(), device_address);
}
TEST_F(BluetoothAdapterMacTest, DeviceConnected) {
// Simulate a paired Bluetooth Classic device with one service UUID.
std::string device_address = "AA:BB:CC:DD:EE:FF";
BluetoothDevice::UUIDSet uuids;
uuids.insert(BluetoothUUID("110b"));
// Device connected when device is unknown to the adapter.
ClassicDeviceConnected(device_address, uuids);
EXPECT_EQ(1, observer_.device_added_count());
EXPECT_EQ(0, observer_.device_changed_count());
EXPECT_EQ(observer_.last_device_address(), device_address);
observer_.Reset();
// Device connected when device is known to the adapter.
ClassicDeviceConnected(device_address, uuids);
EXPECT_EQ(0, observer_.device_added_count());
EXPECT_EQ(1, observer_.device_changed_count());
}
} // namespace device