blob: a2d25dd1bf9aba460a4a2d3dcf06c38dac447f44 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 "src/connectivity/bluetooth/core/bt-host/fidl/host_server.h"
#include <fuchsia/bluetooth/control/cpp/fidl_test_base.h>
#include <lib/zx/channel.h>
#include "adapter_test_fixture.h"
#include "fuchsia/bluetooth/control/cpp/fidl.h"
#include "fuchsia/bluetooth/cpp/fidl.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
#include "src/connectivity/bluetooth/core/bt-host/common/device_address.h"
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/data/fake_domain.h"
#include "src/connectivity/bluetooth/core/bt-host/fidl/helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/gap/gap.h"
#include "src/connectivity/bluetooth/core/bt-host/gatt/fake_layer.h"
#include "src/connectivity/bluetooth/core/bt-host/gatt_host.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/fake_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/fake_controller_test.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/fake_peer.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/test_packets.h"
namespace bthost {
namespace {
// Limiting the de-scoped aliases here helps test cases be more specific about whether they're using
// FIDL names or bt-host internal names.
using bt::CreateStaticByteBuffer;
using bt::LowerBits;
using bt::UpperBits;
using bt::l2cap::testing::FakeChannel;
using bt::testing::FakePeer;
namespace fbt = fuchsia::bluetooth;
namespace fctrl = fuchsia::bluetooth::control;
namespace fsys = fuchsia::bluetooth::sys;
const bt::DeviceAddress kLeTestAddr(bt::DeviceAddress::Type::kLEPublic, {0x01, 0, 0, 0, 0, 0});
const bt::DeviceAddress kBredrTestAddr(bt::DeviceAddress::Type::kBREDR, {0x01, 0, 0, 0, 0, 0});
class MockPairingDelegate : public fctrl::testing::PairingDelegate_TestBase {
public:
MockPairingDelegate(fidl::InterfaceRequest<PairingDelegate> request,
async_dispatcher_t* dispatcher)
: binding_(this, std::move(request), dispatcher) {}
~MockPairingDelegate() override = default;
MOCK_METHOD(void, OnPairingRequest,
(fctrl::RemoteDevice device, fctrl::PairingMethod method,
fidl::StringPtr displayed_passkey, OnPairingRequestCallback callback),
(override));
MOCK_METHOD(void, OnPairingComplete, (std::string device_id, fuchsia::bluetooth::Status status),
(override));
MOCK_METHOD(void, OnRemoteKeypress, (std::string device_id, fctrl::PairingKeypressType keypress),
(override));
private:
fidl::Binding<PairingDelegate> binding_;
void NotImplemented_(const std::string& name) override {
FAIL() << name << " is not implemented";
}
};
class FIDL_HostServerTest : public bthost::testing::AdapterTestFixture {
public:
FIDL_HostServerTest() = default;
~FIDL_HostServerTest() override = default;
void SetUp() override {
AdapterTestFixture::SetUp();
gatt_host_ = GattHost::CreateForTesting(dispatcher(), gatt());
ResetHostServer();
}
void ResetHostServer() {
fidl::InterfaceHandle<fuchsia::bluetooth::host::Host> host_handle;
host_server_ = std::make_unique<HostServer>(host_handle.NewRequest().TakeChannel(),
adapter()->AsWeakPtr(), gatt_host_);
host_.Bind(std::move(host_handle));
}
void TearDown() override {
RunLoopUntilIdle();
host_ = nullptr;
host_server_ = nullptr;
gatt_host_->ShutDown();
gatt_host_ = nullptr;
RunLoopUntilIdle();
AdapterTestFixture::TearDown();
}
protected:
HostServer* host_server() const { return host_server_.get(); }
fuchsia::bluetooth::host::Host* host_client() const { return host_.get(); }
// Create and bind a MockPairingDelegate and attach it to the HostServer under test. It is
// heap-allocated to permit its explicit destruction.
[[nodiscard]] std::unique_ptr<MockPairingDelegate> SetMockPairingDelegate(
fctrl::InputCapabilityType input_capability, fctrl::OutputCapabilityType output_capability) {
using ::testing::StrictMock;
fidl::InterfaceHandle<fctrl::PairingDelegate> pairing_delegate_handle;
auto pairing_delegate = std::make_unique<StrictMock<MockPairingDelegate>>(
pairing_delegate_handle.NewRequest(), dispatcher());
host_client()->SetPairingDelegate(input_capability, output_capability,
std::move(pairing_delegate_handle));
// Wait for the Control/SetPairingDelegate message to process.
RunLoopUntilIdle();
return pairing_delegate;
}
std::tuple<bt::gap::Peer*, FakeChannel*> ConnectFakePeer(bool connect_le = true) {
auto device_addr = connect_le ? kLeTestAddr : kBredrTestAddr;
auto* peer = adapter()->peer_cache()->NewPeer(device_addr, true);
EXPECT_TRUE(peer->temporary());
// This is to capture the channel created during the Connection process
FakeChannel* fake_chan = nullptr;
data_domain()->set_channel_callback(
[&fake_chan](fbl::RefPtr<FakeChannel> new_fake_chan) { fake_chan = new_fake_chan.get(); });
auto fake_peer = std::make_unique<FakePeer>(device_addr);
test_device()->AddPeer(std::move(fake_peer));
std::optional<fit::result<void, fsys::Error>> connect_result;
host_client()->Connect(fbt::PeerId{peer->identifier().value()}, [&](auto result) {
ASSERT_FALSE(result.is_err());
connect_result = std::move(result);
});
RunLoopUntilIdle();
if (!connect_result || connect_result->is_error()) {
peer = nullptr;
fake_chan = nullptr;
}
return std::make_tuple(peer, fake_chan);
}
private:
std::unique_ptr<HostServer> host_server_;
fbl::RefPtr<GattHost> gatt_host_;
fuchsia::bluetooth::host::HostPtr host_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(FIDL_HostServerTest);
};
TEST_F(FIDL_HostServerTest, FidlIoCapabilitiesMapToHostIoCapability) {
// Isolate HostServer's private bt::gap::PairingDelegate implementation.
auto host_pairing_delegate = static_cast<bt::gap::PairingDelegate*>(host_server());
// Getter should be safe to call when no PairingDelegate assigned.
EXPECT_EQ(bt::sm::IOCapability::kNoInputNoOutput, host_pairing_delegate->io_capability());
auto fidl_pairing_delegate = SetMockPairingDelegate(fctrl::InputCapabilityType::KEYBOARD,
fctrl::OutputCapabilityType::DISPLAY);
EXPECT_EQ(bt::sm::IOCapability::kKeyboardDisplay, host_pairing_delegate->io_capability());
}
TEST_F(FIDL_HostServerTest, HostCompletePairingCallsFidlOnPairingComplete) {
using namespace ::testing;
// Isolate HostServer's private bt::gap::PairingDelegate implementation.
auto host_pairing_delegate = static_cast<bt::gap::PairingDelegate*>(host_server());
auto fidl_pairing_delegate = SetMockPairingDelegate(fctrl::InputCapabilityType::KEYBOARD,
fctrl::OutputCapabilityType::DISPLAY);
// fuchsia::bluetooth::Status is move-only so check its value in a lambda.
EXPECT_CALL(*fidl_pairing_delegate, OnPairingComplete(EndsWith("c0decafe"), _))
.WillOnce([](Unused, fuchsia::bluetooth::Status status) {
ASSERT_TRUE(status.error);
EXPECT_EQ(fuchsia::bluetooth::ErrorCode::PROTOCOL_ERROR, status.error->error_code);
EXPECT_EQ(static_cast<uintmax_t>(bt::sm::ErrorCode::kConfirmValueFailed),
status.error->protocol_error_code);
});
host_pairing_delegate->CompletePairing(bt::PeerId(0xc0decafe),
bt::sm::Status(bt::sm::ErrorCode::kConfirmValueFailed));
// Wait for the PairingDelegate/OnPairingComplete message to process.
RunLoopUntilIdle();
}
TEST_F(FIDL_HostServerTest, HostConfirmPairingRequestsConsentPairingOverFidl) {
using namespace ::testing;
auto host_pairing_delegate = static_cast<bt::gap::PairingDelegate*>(host_server());
auto fidl_pairing_delegate = SetMockPairingDelegate(fctrl::InputCapabilityType::KEYBOARD,
fctrl::OutputCapabilityType::DISPLAY);
auto* const peer = adapter()->peer_cache()->NewPeer(kLeTestAddr, /*connectable=*/true);
ASSERT_TRUE(peer);
EXPECT_CALL(*fidl_pairing_delegate,
OnPairingRequest(_, fctrl::PairingMethod::CONSENT, fidl::StringPtr(nullptr), _))
.WillOnce(
[id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
fctrl::PairingDelegate::OnPairingRequestCallback callback) {
EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
callback(/*accept=*/true, /*entered_passkey=*/nullptr);
});
bool confirm_cb_value = false;
bt::gap::PairingDelegate::ConfirmCallback confirm_cb = [&confirm_cb_value](bool confirmed) {
confirm_cb_value = confirmed;
};
host_pairing_delegate->ConfirmPairing(peer->identifier(), std::move(confirm_cb));
// Wait for the PairingDelegate/OnPairingRequest message to process.
RunLoopUntilIdle();
EXPECT_TRUE(confirm_cb_value);
}
TEST_F(FIDL_HostServerTest,
HostDisplayPasskeyRequestsPasskeyDisplayOrNumericComparisonPairingOverFidl) {
using namespace ::testing;
auto host_pairing_delegate = static_cast<bt::gap::PairingDelegate*>(host_server());
auto fidl_pairing_delegate = SetMockPairingDelegate(fctrl::InputCapabilityType::KEYBOARD,
fctrl::OutputCapabilityType::DISPLAY);
auto* const peer = adapter()->peer_cache()->NewPeer(kLeTestAddr, /*connectable=*/true);
ASSERT_TRUE(peer);
// This call should use PASSKEY_DISPLAY to request that the user perform peer passkey entry.
using fctrl::PairingMethod;
using OnPairingRequestCallback = fctrl::PairingDelegate::OnPairingRequestCallback;
EXPECT_CALL(*fidl_pairing_delegate,
OnPairingRequest(_, PairingMethod::PASSKEY_DISPLAY, fidl::StringPtr("012345"), _))
.WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
OnPairingRequestCallback callback) {
EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
callback(/*accept=*/false, /*entered_passkey=*/nullptr);
});
bool confirm_cb_called = false;
auto confirm_cb = [&confirm_cb_called](bool confirmed) {
EXPECT_FALSE(confirmed);
confirm_cb_called = true;
};
using DisplayMethod = bt::gap::PairingDelegate::DisplayMethod;
host_pairing_delegate->DisplayPasskey(peer->identifier(), 12345, DisplayMethod::kPeerEntry,
confirm_cb);
// Wait for the PairingDelegate/OnPairingRequest message to process.
RunLoopUntilIdle();
EXPECT_TRUE(confirm_cb_called);
// This call should use PASSKEY_COMPARISON to request that the user compare the passkeys shown on
// the local and peer devices.
EXPECT_CALL(*fidl_pairing_delegate,
OnPairingRequest(_, PairingMethod::PASSKEY_COMPARISON, fidl::StringPtr("012345"), _))
.WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
OnPairingRequestCallback callback) {
EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
callback(/*accept=*/false, /*entered_passkey=*/nullptr);
});
confirm_cb_called = false;
host_pairing_delegate->DisplayPasskey(peer->identifier(), 12345, DisplayMethod::kComparison,
confirm_cb);
// Wait for the PairingDelegate/OnPairingRequest message to process.
RunLoopUntilIdle();
EXPECT_TRUE(confirm_cb_called);
}
TEST_F(FIDL_HostServerTest, HostRequestPasskeyRequestsPasskeyEntryPairingOverFidl) {
using namespace ::testing;
auto host_pairing_delegate = static_cast<bt::gap::PairingDelegate*>(host_server());
auto fidl_pairing_delegate = SetMockPairingDelegate(fctrl::InputCapabilityType::KEYBOARD,
fctrl::OutputCapabilityType::DISPLAY);
auto* const peer = adapter()->peer_cache()->NewPeer(kLeTestAddr, /*connectable=*/true);
ASSERT_TRUE(peer);
using OnPairingRequestCallback = fctrl::PairingDelegate::OnPairingRequestCallback;
EXPECT_CALL(*fidl_pairing_delegate,
OnPairingRequest(_, fctrl::PairingMethod::PASSKEY_ENTRY, fidl::StringPtr(nullptr), _))
.WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
OnPairingRequestCallback callback) {
EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
callback(/*accept=*/false, "012345");
})
.WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
OnPairingRequestCallback callback) {
EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
callback(/*accept=*/true, nullptr);
})
.WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
OnPairingRequestCallback callback) {
EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
callback(/*accept=*/true, u8"🍂");
})
.WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
OnPairingRequestCallback callback) {
EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
callback(/*accept=*/true, "012345");
});
std::optional<int64_t> passkey_response;
auto response_cb = [&passkey_response](int64_t passkey) { passkey_response = passkey; };
// Expect the first three pairing requests to provide passkey values that indicate failures.
for (int i = 0; i < 3; i++) {
passkey_response.reset();
host_pairing_delegate->RequestPasskey(peer->identifier(), response_cb);
// Wait for the PairingDelegate/OnPairingRequest message to process.
RunLoopUntilIdle();
// Include loop counter to help debug test failures.
ASSERT_TRUE(passkey_response.has_value()) << i;
// Negative values indicate "reject pairing."
EXPECT_LT(passkey_response.value(), 0) << i;
}
// Last request should succeed.
passkey_response.reset();
host_pairing_delegate->RequestPasskey(peer->identifier(), response_cb);
// Wait for the PairingDelegate/OnPairingRequest message to process.
RunLoopUntilIdle();
ASSERT_TRUE(passkey_response.has_value());
EXPECT_EQ(12345, passkey_response.value());
}
TEST_F(FIDL_HostServerTest, WatchState) {
std::optional<fsys::HostInfo> info;
host_server()->WatchState([&](auto value) { info = std::move(value); });
ASSERT_TRUE(info.has_value());
ASSERT_TRUE(info->has_id());
ASSERT_TRUE(info->has_technology());
ASSERT_TRUE(info->has_address());
ASSERT_TRUE(info->has_local_name());
ASSERT_TRUE(info->has_discoverable());
ASSERT_TRUE(info->has_discovering());
EXPECT_EQ(adapter()->identifier().value(), info->id().value);
EXPECT_EQ(fsys::TechnologyType::DUAL_MODE, info->technology());
EXPECT_EQ(fbt::AddressType::PUBLIC, info->address().type);
EXPECT_TRUE(
ContainersEqual(adapter()->state().controller_address().bytes(), info->address().bytes));
EXPECT_EQ("fuchsia", info->local_name());
EXPECT_FALSE(info->discoverable());
EXPECT_FALSE(info->discovering());
}
TEST_F(FIDL_HostServerTest, WatchDiscoveryState) {
std::optional<fsys::HostInfo> info;
// Make initial watch call so that subsequent calls remain pending.
host_server()->WatchState([&](auto value) { info = std::move(value); });
ASSERT_TRUE(info.has_value());
info.reset();
// Watch for updates.
host_server()->WatchState([&](auto value) { info = std::move(value); });
EXPECT_FALSE(info.has_value());
host_server()->StartDiscovery([](auto) {});
RunLoopUntilIdle();
ASSERT_TRUE(info.has_value());
ASSERT_TRUE(info->has_discovering());
EXPECT_TRUE(info->discovering());
info.reset();
host_server()->WatchState([&](auto value) { info = std::move(value); });
EXPECT_FALSE(info.has_value());
host_server()->StopDiscovery();
RunLoopUntilIdle();
ASSERT_TRUE(info.has_value());
ASSERT_TRUE(info->has_discovering());
EXPECT_FALSE(info->discovering());
}
TEST_F(FIDL_HostServerTest, WatchDiscoverableState) {
std::optional<fsys::HostInfo> info;
// Make initial watch call so that subsequent calls remain pending.
host_server()->WatchState([&](auto value) { info = std::move(value); });
ASSERT_TRUE(info.has_value());
info.reset();
// Watch for updates.
host_server()->WatchState([&](auto value) { info = std::move(value); });
EXPECT_FALSE(info.has_value());
host_server()->SetDiscoverable(true, [](auto) {});
RunLoopUntilIdle();
ASSERT_TRUE(info.has_value());
ASSERT_TRUE(info->has_discoverable());
EXPECT_TRUE(info->discoverable());
info.reset();
host_server()->WatchState([&](auto value) { info = std::move(value); });
EXPECT_FALSE(info.has_value());
host_server()->SetDiscoverable(false, [](auto) {});
RunLoopUntilIdle();
ASSERT_TRUE(info.has_value());
ASSERT_TRUE(info->has_discoverable());
EXPECT_FALSE(info->discoverable());
}
TEST_F(FIDL_HostServerTest, InitiatePairingLeDefault) {
// clang-format off
const auto kExpected = CreateStaticByteBuffer(
0x01, // code: "Pairing Request"
0x03, // IO cap.: NoInputNoOutput
0x00, // OOB: not present
0x05, // AuthReq: bonding, MITM (Authenticated)
0x10, // encr. key size: 16 (default max)
0x00, // initiator keys: none
0x03 // responder keys: enc key and identity info
);
// clang-format on
auto [peer, fake_chan] = ConnectFakePeer();
ASSERT_TRUE(peer);
ASSERT_TRUE(fake_chan);
ASSERT_EQ(bt::gap::Peer::ConnectionState::kConnected, peer->le()->connection_state());
bool pairing_request_sent = false;
// This test only checks that PairingState kicks off an LE pairing feature exchange correctly, as
// the call to Pair is only responsible for starting pairing, not for completing it.
auto expect_default_bytebuffer = [&pairing_request_sent, kExpected](bt::ByteBufferPtr sent) {
ASSERT_TRUE(sent);
ASSERT_EQ(*sent, kExpected);
pairing_request_sent = true;
};
fake_chan->SetSendCallback(expect_default_bytebuffer, dispatcher());
std::optional<fit::result<void, fsys::Error>> pair_result;
fctrl::PairingOptions opts;
host_client()->Pair(fbt::PeerId{peer->identifier().value()}, std::move(opts),
[&](auto result) { pair_result = std::move(result); });
RunLoopUntilIdle();
// TODO(fxb/886): We don't have a good mechanism for driving pairing to completion without faking
// the entire SMP exchange. We should add SMP mocks that allows us to propagate a result up to the
// FIDL layer. For now we assert that pairing has started and remains pending.
ASSERT_FALSE(pair_result); // Pairing request is pending
ASSERT_TRUE(pairing_request_sent);
}
TEST_F(FIDL_HostServerTest, InitiatePairingLeEncrypted) {
// clang-format off
const auto kExpected = CreateStaticByteBuffer(
0x01, // code: "Pairing Request"
0x03, // IO cap.: NoInputNoOutput
0x00, // OOB: not present
0x01, // AuthReq: bonding, no MITM (not authenticated)
0x10, // encr. key size: 16 (default max)
0x00, // initiator keys: none
0x03 // responder keys: enc key and identity info
);
// clang-format on
auto [peer, fake_chan] = ConnectFakePeer();
ASSERT_TRUE(peer);
ASSERT_TRUE(fake_chan);
ASSERT_EQ(bt::gap::Peer::ConnectionState::kConnected, peer->le()->connection_state());
bool pairing_request_sent = false;
// This test only checks that PairingState kicks off an LE pairing feature exchange correctly, as
// the call to Pair is only responsible for starting pairing, not for completing it.
auto expect_default_bytebuffer = [&pairing_request_sent, kExpected](bt::ByteBufferPtr sent) {
ASSERT_TRUE(sent);
ASSERT_EQ(*sent, kExpected);
pairing_request_sent = true;
};
fake_chan->SetSendCallback(expect_default_bytebuffer, dispatcher());
std::optional<fit::result<void, fsys::Error>> pair_result;
fctrl::PairingOptions opts;
opts.set_le_security_level(fctrl::PairingSecurityLevel::ENCRYPTED);
host_client()->Pair(fbt::PeerId{peer->identifier().value()}, std::move(opts),
[&](auto result) { pair_result = std::move(result); });
RunLoopUntilIdle();
// TODO(fxb/886): We don't have a good mechanism for driving pairing to completion without faking
// the entire SMP exchange. We should add SMP mocks that allows us to propagate a result up to the
// FIDL layer. For now we assert that pairing has started and remains pending.
ASSERT_FALSE(pair_result); // Pairing request is pending
ASSERT_TRUE(pairing_request_sent);
}
TEST_F(FIDL_HostServerTest, InitiatePairingNonBondableLe) {
// clang-format off
const auto kExpected = CreateStaticByteBuffer(
0x01, // code: "Pairing Request"
0x03, // IO cap.: NoInputNoOutput
0x00, // OOB: not present
0x04, // AuthReq: no bonding, MITM (authenticated)
0x10, // encr. key size: 16 (default max)
0x00, // initiator keys: none
0x00 // responder keys: none
);
// clang-format on
auto [peer, fake_chan] = ConnectFakePeer();
ASSERT_TRUE(peer);
ASSERT_TRUE(fake_chan);
ASSERT_EQ(bt::gap::Peer::ConnectionState::kConnected, peer->le()->connection_state());
bool pairing_request_sent = false;
// This test only checks that PairingState kicks off an LE pairing feature exchange correctly, as
// the call to Pair is only responsible for starting pairing, not for completing it.
auto expect_default_bytebuffer = [&pairing_request_sent, kExpected](bt::ByteBufferPtr sent) {
ASSERT_TRUE(sent);
ASSERT_EQ(*sent, kExpected);
pairing_request_sent = true;
};
fake_chan->SetSendCallback(expect_default_bytebuffer, dispatcher());
std::optional<fit::result<void, fsys::Error>> pair_result;
fctrl::PairingOptions opts;
opts.set_non_bondable(true);
host_client()->Pair(fbt::PeerId{peer->identifier().value()}, std::move(opts),
[&](auto result) { pair_result = std::move(result); });
RunLoopUntilIdle();
// TODO(fxb/886): We don't have a good mechanism for driving pairing to completion without faking
// the entire SMP exchange. We should add SMP mocks that allows us to propagate a result up to the
// FIDL layer. For now we assert that pairing has started and remains pending.
ASSERT_FALSE(pair_result); // Pairing request is pending
ASSERT_TRUE(pairing_request_sent);
}
TEST_F(FIDL_HostServerTest, InitiateBrEdrPairingLePeerFails) {
auto [peer, fake_chan] = ConnectFakePeer();
ASSERT_TRUE(peer);
ASSERT_TRUE(fake_chan);
ASSERT_EQ(bt::gap::Peer::ConnectionState::kConnected, peer->le()->connection_state());
std::optional<fit::result<void, fsys::Error>> pair_result;
fctrl::PairingOptions opts;
// Set pairing option with classic
opts.set_transport(fctrl::TechnologyType::CLASSIC);
auto pair_cb = [&](auto result) {
ASSERT_TRUE(result.is_err());
pair_result = std::move(result);
};
host_client()->Pair(fbt::PeerId{peer->identifier().value()}, std::move(opts), std::move(pair_cb));
RunLoopUntilIdle();
ASSERT_TRUE(pair_result);
ASSERT_EQ(pair_result->error(), fsys::Error::PEER_NOT_FOUND);
}
TEST_F(FIDL_HostServerTest, WatchPeersHangsOnFirstCallWithNoExistingPeers) {
// By default the peer cache contains no entries when HostServer is first constructed. The first
// call to WatchPeers should hang.
bool replied = false;
host_server()->WatchPeers([&](auto, auto) { replied = true; });
EXPECT_FALSE(replied);
}
TEST_F(FIDL_HostServerTest, WatchPeersRepliesOnFirstCallWithExistingPeers) {
__UNUSED auto* peer = adapter()->peer_cache()->NewPeer(kLeTestAddr, /*connectable=*/true);
ResetHostServer();
// By default the peer cache contains no entries when HostServer is first constructed. The first
// call to WatchPeers should hang.
bool replied = false;
host_server()->WatchPeers([&](auto updated, auto removed) {
EXPECT_EQ(1u, updated.size());
EXPECT_TRUE(removed.empty());
replied = true;
});
EXPECT_TRUE(replied);
}
TEST_F(FIDL_HostServerTest, WatchPeersStateMachine) {
std::optional<std::vector<fsys::Peer>> updated;
std::optional<std::vector<fbt::PeerId>> removed;
// Initial watch call hangs as the cache is empty.
host_server()->WatchPeers([&](auto updated_arg, auto removed_arg) {
updated = std::move(updated_arg);
removed = std::move(removed_arg);
});
ASSERT_FALSE(updated.has_value());
ASSERT_FALSE(removed.has_value());
// Adding a new peer should resolve the hanging get.
auto* peer = adapter()->peer_cache()->NewPeer(kLeTestAddr, /*connectable=*/true);
ASSERT_TRUE(updated.has_value());
ASSERT_TRUE(removed.has_value());
EXPECT_EQ(1u, updated->size());
EXPECT_TRUE(fidl::Equals(fidl_helpers::PeerToFidl(*peer), (*updated)[0]));
EXPECT_TRUE(removed->empty());
updated.reset();
removed.reset();
// The next call should hang.
host_server()->WatchPeers([&](auto updated_arg, auto removed_arg) {
updated = std::move(updated_arg);
removed = std::move(removed_arg);
});
ASSERT_FALSE(updated.has_value());
ASSERT_FALSE(removed.has_value());
// Removing the peer should resolve the hanging get.
auto peer_id = peer->identifier();
__UNUSED auto result = adapter()->peer_cache()->RemoveDisconnectedPeer(peer_id);
ASSERT_TRUE(updated.has_value());
ASSERT_TRUE(removed.has_value());
EXPECT_TRUE(updated->empty());
EXPECT_EQ(1u, removed->size());
EXPECT_TRUE(fidl::Equals(fbt::PeerId{peer_id.value()}, (*removed)[0]));
}
TEST_F(FIDL_HostServerTest, WatchPeersUpdatedThenRemoved) {
// Add then remove a peer. The watcher should only report the removal.
bt::PeerId id;
{
auto* peer = adapter()->peer_cache()->NewPeer(kLeTestAddr, /*connectable=*/true);
id = peer->identifier();
// |peer| becomes a dangling pointer after the call to RemoveDisconnectedPeer. We scoped the
// binding of |peer| so that it doesn't exist beyond this point.
__UNUSED auto result = adapter()->peer_cache()->RemoveDisconnectedPeer(id);
}
bool replied = false;
host_server()->WatchPeers([&replied, id](auto updated, auto removed) {
EXPECT_TRUE(updated.empty());
EXPECT_EQ(1u, removed.size());
EXPECT_TRUE(fidl::Equals(fbt::PeerId{id.value()}, removed[0]));
replied = true;
});
EXPECT_TRUE(replied);
}
} // namespace
} // namespace bthost