#include "device/fido/cable/fido_cable_discovery.h"
#include <algorithm>
#include <memory>
#include <string_view>
#include <utility>
#include "base/containers/contains.h"
#include "base/containers/span.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/test/bluetooth_test.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/fido/cable/fido_ble_uuids.h"
#include "device/fido/cable/fido_cable_device.h"
#include "device/fido/cable/fido_cable_handshake_handler.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/mock_fido_discovery_observer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "base/test/scoped_feature_list.h"
#include "device/bluetooth/floss/floss_features.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "device/fido/mac/util.h"
#endif
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Sequence;
namespace device {
namespace {
constexpr auto kTestCableVersion = CableDiscoveryData::Version::V1;
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
constexpr auto kTestCableVersionNumber = 1;
#endif
constexpr CableEidArray kClientEid = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13,
0x14, 0x15}};
constexpr char kUuidFormattedClientEid[] =
"00010203-0405-0607-0809-101112131415";
constexpr CableEidArray kAuthenticatorEid = {
{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01}};
constexpr CableEidArray kInvalidAuthenticatorEid = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}};
constexpr CableSessionPreKeyArray kTestSessionPreKey = {
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
#if !BUILDFLAG(IS_WIN)
constexpr CableEidArray kSecondaryClientEid = {
{0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
0x03, 0x02, 0x01, 0x00}};
constexpr char kUuidFormattedSecondaryClientEid[] =
"15141312-1110-0908-0706-050403020100";
constexpr CableEidArray kSecondaryAuthenticatorEid = {
{0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
0xee, 0xee, 0xee, 0xee}};
constexpr CableSessionPreKeyArray kSecondarySessionPreKey = {
{0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd}};
#endif
constexpr char kTestBleDeviceAddress[] = "11:12:13:14:15:16";
constexpr char kTestBleDeviceName[] = "test_cable_device";
std::unique_ptr<MockBluetoothDevice> CreateTestBluetoothDevice() {
return std::make_unique<testing::NiceMock<MockBluetoothDevice>>(
nullptr , 0 , kTestBleDeviceName,
kTestBleDeviceAddress, true , true );
}
ACTION_P(ReturnFromAsyncCall, closure) {
closure.Run();
}
MATCHER_P2(IsAdvertisementContent,
expected_client_eid,
expected_uuid_formatted_client_eid,
"") {
#if BUILDFLAG(IS_MAC)
return base::Contains(*arg->service_uuids(),
expected_uuid_formatted_client_eid);
#elif BUILDFLAG(IS_WIN)
const auto manufacturer_data = arg->manufacturer_data();
const auto manufacturer_data_value = manufacturer_data->find(0x00E0);
if (manufacturer_data_value == manufacturer_data->end()) {
return false;
}
const auto& manufacturer_data_payload = manufacturer_data_value->second;
return manufacturer_data_payload.size() >= 4u &&
manufacturer_data_payload[0] == manufacturer_data_payload.size() - 1 &&
manufacturer_data_payload[1] == 0x15 &&
manufacturer_data_payload[2] == 0x20 &&
manufacturer_data_payload[3] == kTestCableVersionNumber &&
std::equal(manufacturer_data_payload.begin() + 4,
manufacturer_data_payload.end(),
expected_client_eid.begin(), expected_client_eid.end());
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
const auto service_data = arg->service_data();
const auto service_data_with_uuid = service_data->find(kGoogleCableUUID128);
if (service_data_with_uuid == service_data->end()) {
return false;
}
const auto& service_data_value = service_data_with_uuid->second;
return (service_data_value[0] >> 5 & 1) &&
service_data_value[1] == kTestCableVersionNumber &&
service_data_value.size() == 18u &&
std::equal(service_data_value.begin() + 2, service_data_value.end(),
expected_client_eid.begin(), expected_client_eid.end());
#else
return true;
#endif
}
class CableMockBluetoothAdvertisement : public BluetoothAdvertisement {
public:
MOCK_METHOD2(Unregister,
void(SuccessCallback success_callback,
ErrorCallback error_callback));
void ExpectUnregisterAndSucceed() {
EXPECT_CALL(*this, Unregister(_, _))
.WillOnce(::testing::WithArg<0>([](auto success_cb) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(success_cb));
}));
}
private:
~CableMockBluetoothAdvertisement() override = default;
};
class CableMockAdapter : public MockBluetoothAdapter {
public:
static scoped_refptr<CableMockAdapter> MakePoweredOn() {
auto mock_adapter =
base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
EXPECT_CALL(*mock_adapter, IsPresent())
.WillRepeatedly(::testing::Return(true));
EXPECT_CALL(*mock_adapter, IsPowered())
.WillRepeatedly(::testing::Return(true));
return mock_adapter;
}
static scoped_refptr<CableMockAdapter> MakePoweredOff() {
auto mock_adapter =
base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
EXPECT_CALL(*mock_adapter, IsPresent())
.WillRepeatedly(::testing::Return(true));
EXPECT_CALL(*mock_adapter, IsPowered())
.WillRepeatedly(::testing::Return(false));
return mock_adapter;
}
static scoped_refptr<CableMockAdapter> MakeNotPresent() {
auto mock_adapter =
base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
EXPECT_CALL(*mock_adapter, IsPresent())
.WillRepeatedly(::testing::Return(false));
return mock_adapter;
}
static scoped_refptr<CableMockAdapter> MakeWithUndeterminedPermission() {
auto mock_adapter =
base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
EXPECT_CALL(*mock_adapter, IsPresent())
.WillRepeatedly(::testing::Return(true));
EXPECT_CALL(*mock_adapter, GetOsPermissionStatus())
.WillRepeatedly(testing::Return(PermissionStatus::kUndetermined));
return mock_adapter;
}
MOCK_METHOD3(RegisterAdvertisement,
void(std::unique_ptr<BluetoothAdvertisement::Data>,
CreateAdvertisementCallback,
AdvertisementErrorCallback));
BluetoothDevice* CreateNewTestBluetoothDevice(
base::span<const uint8_t, kCableEphemeralIdSize> authenticator_eid) {
auto mock_device = CreateTestBluetoothDevice();
std::vector<uint8_t> service_data(18);
service_data[0] = 1 << 5;
std::ranges::copy(authenticator_eid, service_data.begin() + 2);
BluetoothDevice::ServiceDataMap service_data_map;
service_data_map.emplace(kGoogleCableUUID128, std::move(service_data));
mock_device->UpdateAdvertisementData(
1 , std::nullopt , BluetoothDevice::UUIDList(),
std::nullopt , std::move(service_data_map),
BluetoothDevice::ManufacturerDataMap());
auto* mock_device_ptr = mock_device.get();
AddMockDevice(std::move(mock_device));
return mock_device_ptr;
}
void AddNewTestBluetoothDevice(
base::span<const uint8_t, kCableEphemeralIdSize> authenticator_eid) {
auto* device = CreateNewTestBluetoothDevice(authenticator_eid);
for (auto& observer : GetObservers()) {
observer.DeviceAdded(this, device);
}
}
void AddNewTestAppleBluetoothDevice(
base::span<const uint8_t, kCableEphemeralIdSize> authenticator_eid) {
auto mock_device = CreateTestBluetoothDevice();
mock_device->AddUUID(BluetoothUUID("fde2"));
mock_device->AddUUID(BluetoothUUID(
fido_parsing_utils::ConvertBytesToUuid(authenticator_eid)));
auto* mock_device_ptr = mock_device.get();
AddMockDevice(std::move(mock_device));
for (auto& observer : GetObservers()) {
observer.DeviceAdded(this, mock_device_ptr);
}
}
void ExpectRegisterAdvertisementWithResponse(
bool simulate_success,
base::span<const uint8_t> expected_client_eid,
std::string_view expected_uuid_formatted_client_eid,
Sequence sequence = Sequence(),
scoped_refptr<CableMockBluetoothAdvertisement> advertisement = nullptr) {
if (!advertisement) {
advertisement = base::MakeRefCounted<CableMockBluetoothAdvertisement>();
EXPECT_CALL(*advertisement, Unregister(_, _))
.WillRepeatedly(::testing::WithArg<0>(
[](auto callback) { std::move(callback).Run(); }));
}
EXPECT_CALL(*this,
RegisterAdvertisement(
IsAdvertisementContent(expected_client_eid,
expected_uuid_formatted_client_eid),
_, _))
.InSequence(sequence)
.WillOnce(::testing::WithArgs<1, 2>(
[simulate_success, advertisement](auto success_callback,
auto failure_callback) {
simulate_success ? std::move(success_callback).Run(advertisement)
: std::move(failure_callback)
.Run(BluetoothAdvertisement::ErrorCode::
INVALID_ADVERTISEMENT_ERROR_CODE);
}));
}
void ExpectDiscoveryWithScanCallback() {
EXPECT_CALL(*this, StartScanWithFilter_(_, _))
.WillOnce(::testing::WithArg<1>([](auto& callback) {
std::move(callback).Run(
false, device::UMABluetoothDiscoverySessionOutcome::SUCCESS);
}));
}
void ExpectDiscoveryWithScanCallback(
base::span<const uint8_t, kCableEphemeralIdSize> eid,
bool is_apple_device = false) {
EXPECT_CALL(*this, StartScanWithFilter_(_, _))
.WillOnce(
::testing::WithArg<1>([this, eid, is_apple_device](auto& callback) {
std::move(callback).Run(
false, device::UMABluetoothDiscoverySessionOutcome::SUCCESS);
if (is_apple_device) {
AddNewTestAppleBluetoothDevice(eid);
} else {
AddNewTestBluetoothDevice(eid);
}
}));
}
#if BUILDFLAG(IS_CHROMEOS)
void ExpectLEScan(base::span<const uint8_t, kCableEphemeralIdSize> eid) {
EXPECT_CALL(*this, StartLowEnergyScanSession(_, _))
.WillOnce(
[this, eid](std::unique_ptr<BluetoothLowEnergyScanFilter> filter,
base::WeakPtr<BluetoothLowEnergyScanSession::Delegate>
delegate) {
EXPECT_TRUE(filter);
delegate->OnSessionStarted(nullptr,
std::nullopt);
auto* device = CreateNewTestBluetoothDevice(eid);
delegate->OnDeviceFound(nullptr, device);
return nullptr;
});
}
#endif
protected:
~CableMockAdapter() override = default;
};
class FakeHandshakeHandler : public FidoCableV1HandshakeHandler {
public:
FakeHandshakeHandler(FidoCableDevice* device,
base::span<const uint8_t, 8> nonce,
base::span<const uint8_t, 32> session_pre_key)
: FidoCableV1HandshakeHandler(device, nonce, session_pre_key) {}
~FakeHandshakeHandler() override = default;
void InitiateCableHandshake(FidoDevice::DeviceCallback callback) override {
std::move(callback).Run(std::vector<uint8_t>());
}
bool ValidateAuthenticatorHandshakeMessage(
base::span<const uint8_t> response) override {
return true;
}
};
class FakeFidoCableDiscovery : public FidoCableDiscovery {
public:
explicit FakeFidoCableDiscovery(
std::vector<CableDiscoveryData> discovery_data)
: FidoCableDiscovery(std::move(discovery_data)) {}
~FakeFidoCableDiscovery() override = default;
private:
std::unique_ptr<FidoCableHandshakeHandler> CreateV1HandshakeHandler(
FidoCableDevice* device,
const CableDiscoveryData& discovery_data,
const CableEidArray& eid) override {
std::array<uint8_t, 8> nonce;
CHECK(fido_parsing_utils::ExtractArray(eid, 0, &nonce));
return std::make_unique<FakeHandshakeHandler>(
device, nonce, discovery_data.v1->session_pre_key);
}
};
}
class FidoCableDiscoveryTest : public ::testing::Test {
public:
std::unique_ptr<FidoCableDiscovery> CreateDiscovery() {
std::vector<CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersion, kClientEid,
kAuthenticatorEid, kTestSessionPreKey);
return std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data));
}
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
};
TEST_F(FidoCableDiscoveryTest, TestDiscoveryFails) {
auto cable_discovery = CreateDiscovery();
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer,
DiscoveryStarted(cable_discovery.get(), false,
std::vector<FidoAuthenticator*>()));
EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)).Times(0);
cable_discovery->set_observer(&mock_observer);
auto mock_adapter = CableMockAdapter::MakeNotPresent();
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
}
TEST_F(FidoCableDiscoveryTest, TestDiscoveryStartedWithUnpoweredAdapter) {
auto cable_discovery = CreateDiscovery();
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer,
DiscoveryStarted(cable_discovery.get(), true,
std::vector<FidoAuthenticator*>()));
EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)).Times(0);
cable_discovery->set_observer(&mock_observer);
auto mock_adapter = CableMockAdapter::MakePoweredOff();
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
}
TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewDevice) {
auto cable_discovery = CreateDiscovery();
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer,
DiscoveryStarted(cable_discovery.get(), true,
std::vector<FidoAuthenticator*>()));
EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _));
cable_discovery->set_observer(&mock_observer);
auto mock_adapter = CableMockAdapter::MakePoweredOn();
mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true , kClientEid, kUuidFormattedClientEid);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
}
TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewAppleDevice) {
auto cable_discovery = CreateDiscovery();
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer,
DiscoveryStarted(cable_discovery.get(), true,
std::vector<FidoAuthenticator*>()));
EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _));
cable_discovery->set_observer(&mock_observer);
auto mock_adapter = CableMockAdapter::MakePoweredOn();
mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid, true);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true , kClientEid, kUuidFormattedClientEid);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
}
#if BUILDFLAG(IS_MAC)
TEST_F(FidoCableDiscoveryTest, TestDiscoveryDoesNotUseBluetoothIfUnauthorized) {
fido::mac::ScopedProcessIsSignedOverride scoped_process_is_signed_override(
fido::mac::CodeSigningState::kSigned);
auto cable_discovery = CreateDiscovery();
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer,
DiscoveryStarted(cable_discovery.get(), true,
std::vector<FidoAuthenticator*>()));
cable_discovery->set_observer(&mock_observer);
auto mock_adapter = CableMockAdapter::MakeWithUndeterminedPermission();
EXPECT_CALL(*mock_adapter, IsPowered()).Times(0);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
}
TEST_F(FidoCableDiscoveryTest,
TestDiscoveryAssumesBluetoothAuthorizedIfUnsigned) {
fido::mac::ScopedProcessIsSignedOverride scoped_process_is_signed_override(
fido::mac::CodeSigningState::kNotSigned);
auto cable_discovery = CreateDiscovery();
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer,
DiscoveryStarted(cable_discovery.get(), true,
std::vector<FidoAuthenticator*>()));
EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _));
cable_discovery->set_observer(&mock_observer);
auto mock_adapter = CableMockAdapter::MakeWithUndeterminedPermission();
EXPECT_CALL(*mock_adapter, IsPowered())
.WillRepeatedly(::testing::Return(true));
mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true , kClientEid, kUuidFormattedClientEid);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
}
#endif
TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsIncorrectDevice) {
auto cable_discovery = CreateDiscovery();
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)).Times(0);
EXPECT_CALL(mock_observer, DiscoveryStarted(cable_discovery.get(), true,
testing::IsEmpty()));
cable_discovery->set_observer(&mock_observer);
auto mock_adapter = CableMockAdapter::MakePoweredOn();
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true , kClientEid, kUuidFormattedClientEid);
mock_adapter->ExpectDiscoveryWithScanCallback(kInvalidAuthenticatorEid);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
}
#if !BUILDFLAG(IS_WIN)
TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithMultipleEids) {
std::vector<CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersion, kClientEid, kAuthenticatorEid,
kTestSessionPreKey);
discovery_data.emplace_back(kTestCableVersion, kSecondaryClientEid,
kSecondaryAuthenticatorEid,
kSecondarySessionPreKey);
auto cable_discovery =
std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data));
auto mock_adapter = CableMockAdapter::MakePoweredOn();
mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid);
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer,
DiscoveryStarted(cable_discovery.get(), true,
std::vector<FidoAuthenticator*>()));
EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _));
cable_discovery->set_observer(&mock_observer);
Sequence sequence;
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true , kClientEid, kUuidFormattedClientEid,
sequence);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true , kSecondaryClientEid,
kUuidFormattedSecondaryClientEid, sequence);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
}
TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithPartialAdvertisementSuccess) {
std::vector<CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersion, kClientEid, kAuthenticatorEid,
kTestSessionPreKey);
discovery_data.emplace_back(kTestCableVersion, kSecondaryClientEid,
kSecondaryAuthenticatorEid,
kSecondarySessionPreKey);
auto cable_discovery =
std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data));
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer,
DiscoveryStarted(cable_discovery.get(), true,
std::vector<FidoAuthenticator*>()));
EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _));
cable_discovery->set_observer(&mock_observer);
auto mock_adapter = CableMockAdapter::MakePoweredOn();
Sequence sequence;
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true , kClientEid, kUuidFormattedClientEid,
sequence);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
false , kSecondaryClientEid,
kUuidFormattedSecondaryClientEid, sequence);
mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
}
TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithAdvertisementFailures) {
std::vector<CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersion, kClientEid, kAuthenticatorEid,
kTestSessionPreKey);
discovery_data.emplace_back(kTestCableVersion, kSecondaryClientEid,
kSecondaryAuthenticatorEid,
kSecondarySessionPreKey);
auto cable_discovery =
std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data));
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)).Times(0);
EXPECT_CALL(mock_observer, DiscoveryStarted(cable_discovery.get(), true,
testing::IsEmpty()));
cable_discovery->set_observer(&mock_observer);
auto mock_adapter = CableMockAdapter::MakePoweredOn();
Sequence sequence;
mock_adapter->ExpectRegisterAdvertisementWithResponse(
false , kClientEid, kUuidFormattedClientEid,
sequence);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
false , kSecondaryClientEid,
kUuidFormattedSecondaryClientEid, sequence);
mock_adapter->ExpectDiscoveryWithScanCallback();
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_TRUE(cable_discovery->AdvertisementsForTesting().empty());
}
#endif
TEST_F(FidoCableDiscoveryTest, TestUnregisterAdvertisementUponDestruction) {
auto cable_discovery = CreateDiscovery();
auto advertisement = base::MakeRefCounted<CableMockBluetoothAdvertisement>();
advertisement->ExpectUnregisterAndSucceed();
auto mock_adapter = CableMockAdapter::MakePoweredOn();
mock_adapter->ExpectDiscoveryWithScanCallback();
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true , kClientEid, kUuidFormattedClientEid,
Sequence(), std::move(advertisement));
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_EQ(1u, cable_discovery->AdvertisementsForTesting().size());
cable_discovery.reset();
}
TEST_F(FidoCableDiscoveryTest, TestUnregisterAdvertisementUponStop) {
auto cable_discovery = CreateDiscovery();
auto advertisement = base::MakeRefCounted<CableMockBluetoothAdvertisement>();
advertisement->ExpectUnregisterAndSucceed();
auto mock_adapter = CableMockAdapter::MakePoweredOn();
mock_adapter->ExpectDiscoveryWithScanCallback();
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true , kClientEid, kUuidFormattedClientEid,
Sequence(), std::move(advertisement));
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_EQ(1u, cable_discovery->AdvertisementsForTesting().size());
cable_discovery->Stop();
task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_EQ(0u, cable_discovery->AdvertisementsForTesting().size());
}
TEST_F(FidoCableDiscoveryTest, TestStopWithNoAdvertisementsSucceeds) {
auto mock_adapter = CableMockAdapter::MakePoweredOff();
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
auto cable_discovery = CreateDiscovery();
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer,
DiscoveryStarted(cable_discovery.get(), true,
std::vector<FidoAuthenticator*>()));
cable_discovery->set_observer(&mock_observer);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_EQ(0u, cable_discovery->AdvertisementsForTesting().size());
cable_discovery->Stop();
}
TEST_F(FidoCableDiscoveryTest, TestResumeDiscoveryAfterPoweredOn) {
auto cable_discovery = CreateDiscovery();
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer,
DiscoveryStarted(cable_discovery.get(), true,
std::vector<FidoAuthenticator*>()));
EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _));
cable_discovery->set_observer(&mock_observer);
auto mock_adapter =
base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
EXPECT_CALL(*mock_adapter, IsPresent())
.WillRepeatedly(::testing::Return(true));
mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true , kClientEid, kUuidFormattedClientEid);
{
base::RunLoop run_loop;
auto quit = run_loop.QuitClosure();
EXPECT_CALL(*mock_adapter, IsPowered)
.WillRepeatedly(::testing::DoAll(ReturnFromAsyncCall(quit),
::testing::Return(false)));
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
run_loop.Run();
}
mock_adapter->NotifyAdapterPoweredChanged(true);
task_environment_.FastForwardUntilNoTasksRemain();
}
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewDeviceFloss) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(floss::features::kFlossEnabled);
auto cable_discovery = CreateDiscovery();
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer,
DiscoveryStarted(cable_discovery.get(), true,
std::vector<FidoAuthenticator*>()));
EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _));
cable_discovery->set_observer(&mock_observer);
auto mock_adapter = CableMockAdapter::MakePoweredOn();
mock_adapter->ExpectLEScan(kAuthenticatorEid);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true , kClientEid, kUuidFormattedClientEid);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
task_environment_.FastForwardUntilNoTasksRemain();
}
#endif
}