// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "chromeos/ash/components/nearby/common/connections_manager/nearby_connections_manager_impl.h"
#include <algorithm>
#include <memory>
#include "base/files/file_util.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/ash/components/nearby/common/connections_manager/nearby_connection_impl.h"
#include "chromeos/ash/components/nearby/presence/conversions/nearby_presence_conversions.h"
#include "chromeos/ash/services/nearby/public/cpp/mock_nearby_connections.h"
#include "chromeos/ash/services/nearby/public/cpp/mock_nearby_process_manager.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_connections_types.mojom.h"
#include "components/cross_device/nearby/nearby_features.h"
#include "content/public/test/browser_task_environment.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/mock_network_change_notifier.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char kServiceId[] = "NearbySharing";
const nearby::connections::mojom::Strategy kStrategy =
nearby::connections::mojom::Strategy::kP2pPointToPoint;
const char kEndpointId[] = "ABCD";
const char kRemoteEndpointId[] = "WXYZ";
const char kEndpointInfo[] = {0x0d, 0x07, 0x07, 0x07, 0x07};
const char kRemoteEndpointInfo[] = {0x0d, 0x07, 0x06, 0x08, 0x09};
const char kAuthenticationToken[] = "authentication_token";
const char kRawAuthenticationToken[] = {0x00, 0x05, 0x04, 0x03, 0x02};
const char kBytePayload[] = {0x08, 0x09, 0x06, 0x04, 0x0f};
const char kBytePayload2[] = {0x0a, 0x0b, 0x0c, 0x0d, 0x0e};
const char kDeviceName[] = "Cris Cros's Pixel";
const std::vector<uint8_t> kDeviceId = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
0xcd, 0xef, 0x01, 0x23, 0x45, 0x67,
0x89, 0xab, 0xcd, 0xef};
const int64_t kPayloadId = 689777;
const int64_t kPayloadId2 = 777689;
const int64_t kPayloadId3 = 986777;
const uint64_t kTotalSize = 5201314;
const uint64_t kBytesTransferred = 721831;
const uint8_t kPayload[] = {0x0f, 0x0a, 0x0c, 0x0e};
const std::vector<uint8_t> kBluetoothMacAddress = {0x00, 0x00, 0xe6,
0x88, 0x64, 0x13};
const std::vector<uint8_t> kInvalidBluetoothMacAddress = {0x07, 0x07, 0x07};
// Timeout for initiating a connection to a remote device.
constexpr base::TimeDelta kInitiateNearbyConnectionTimeout = base::Seconds(60);
constexpr base::TimeDelta kConnectV3ToSuccessfulConnectionLatency =
base::Milliseconds(123u);
void VerifyFileReadWrite(base::File& input_file, base::File& output_file) {
base::ScopedAllowBlockingForTesting allow_blocking;
const std::vector<uint8_t> expected_bytes(std::begin(kPayload),
std::end(kPayload));
EXPECT_TRUE(output_file.WriteAndCheck(
/*offset=*/0, base::make_span(expected_bytes)));
output_file.Flush();
output_file.Close();
std::vector<uint8_t> payload_bytes(input_file.GetLength());
EXPECT_TRUE(input_file.ReadAndCheck(
/*offset=*/0, base::make_span(payload_bytes)));
EXPECT_EQ(expected_bytes, payload_bytes);
input_file.Close();
}
base::FilePath InitializeTemporaryFile(base::File& file) {
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath path;
if (!base::CreateTemporaryFile(&path)) {
return path;
}
file.Initialize(path, base::File::Flags::FLAG_CREATE_ALWAYS |
base::File::Flags::FLAG_READ |
base::File::Flags::FLAG_WRITE);
EXPECT_TRUE(file.WriteAndCheck(
/*offset=*/0, base::make_span(kPayload, sizeof(kPayload))));
EXPECT_TRUE(file.Flush());
return path;
}
nearby::presence::PresenceDevice CreatePresenceDevice() {
nearby::internal::DeviceIdentityMetaData metadata;
metadata.set_device_type(nearby::internal::DeviceType::DEVICE_TYPE_PHONE);
metadata.set_device_name(kDeviceName);
metadata.set_bluetooth_mac_address(
std::string(kBluetoothMacAddress.begin(), kBluetoothMacAddress.end()));
metadata.set_device_id(std::string(kDeviceId.begin(), kDeviceId.end()));
nearby::presence::PresenceDevice presence_device(kRemoteEndpointId);
presence_device.SetDeviceIdentityMetaData(metadata);
presence_device.AddAction(static_cast<uint32_t>(
nearby::presence::ActionBit::kPresenceManagerAction));
return presence_device;
}
} // namespace
using Status = nearby::connections::mojom::Status;
using DiscoveredEndpointInfo =
nearby::connections::mojom::DiscoveredEndpointInfo;
using ConnectionInfo = nearby::connections::mojom::ConnectionInfo;
using Medium = nearby::connections::mojom::Medium;
using BandwidthQuality = nearby::connections::mojom::BandwidthQuality;
using MediumSelection = nearby::connections::mojom::MediumSelection;
using PayloadContent = nearby::connections::mojom::PayloadContent;
using PayloadStatus = nearby::connections::mojom::PayloadStatus;
using PayloadTransferUpdate = nearby::connections::mojom::PayloadTransferUpdate;
using Payload = nearby::connections::mojom::Payload;
using BytesPayload = nearby::connections::mojom::BytesPayload;
using FilePayload = nearby::connections::mojom::FilePayload;
class MockDiscoveryListener
: public NearbyConnectionsManager::DiscoveryListener {
public:
MOCK_METHOD(void,
OnEndpointDiscovered,
(const std::string& endpoint_id,
const std::vector<uint8_t>& endpoint_info),
(override));
MOCK_METHOD(void,
OnEndpointLost,
(const std::string& endpoint_id),
(override));
};
class MockIncomingConnectionListener
: public NearbyConnectionsManager::IncomingConnectionListener {
public:
MOCK_METHOD(void,
OnIncomingConnectionInitiated,
(const std::string& endpoint_id,
const std::vector<uint8_t>& endpoint_info),
(override));
MOCK_METHOD(void,
OnIncomingConnectionAccepted,
(const std::string& endpoint_id,
const std::vector<uint8_t>& endpoint_info,
NearbyConnection* connection),
(override));
};
class MockPayloadStatusListener
: public NearbyConnectionsManager::PayloadStatusListener {
public:
MOCK_METHOD(void,
OnStatusUpdate,
(PayloadTransferUpdatePtr update,
std::optional<Medium> upgraded_medium),
(override));
};
class MockBandwidthUpgradeListener
: public NearbyConnectionsManager::BandwidthUpgradeListener {
public:
MOCK_METHOD(void,
OnInitialMedium,
(const std::string& endpoint_id, const Medium medium),
(override));
MOCK_METHOD(void,
OnBandwidthUpgrade,
(const std::string& endpoint_id, const Medium medium),
(override));
MOCK_METHOD(void,
OnBandwidthUpgradeV3,
(nearby::presence::PresenceDevice, const Medium medium),
(override));
base::WeakPtr<MockBandwidthUpgradeListener> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
private:
base::WeakPtrFactory<MockBandwidthUpgradeListener> weak_ptr_factory_{this};
};
class NearbyConnectionsManagerImplTest : public testing::Test {
public:
void SetUp() override {
nearby_connections_manager_ =
std::make_unique<NearbyConnectionsManagerImpl>(&nearby_process_manager_,
kServiceId);
scoped_feature_list_.InitAndEnableFeature(features::kNearbySharingWebRtc);
histogram_tester_ = std::make_unique<base::HistogramTester>();
EXPECT_CALL(nearby_process_manager_, GetNearbyProcessReference)
.WillRepeatedly([&](ash::nearby::NearbyProcessManager::
NearbyProcessStoppedCallback) {
auto mock_reference_ptr =
std::make_unique<ash::nearby::MockNearbyProcessManager::
MockNearbyProcessReference>();
EXPECT_CALL(*(mock_reference_ptr.get()), GetNearbyConnections)
.WillRepeatedly(
testing::ReturnRef(nearby_connections_.shared_remote()));
return mock_reference_ptr;
});
}
protected:
void StartDiscovery(
mojo::Remote<EndpointDiscoveryListener>& listener_remote,
testing::NiceMock<MockDiscoveryListener>& discovery_listener) {
StartDiscovery(listener_remote, default_data_usage_, discovery_listener);
}
void StartDiscovery(
mojo::Remote<EndpointDiscoveryListener>& listener_remote,
NearbyConnectionsManager::DataUsage data_usage,
testing::NiceMock<MockDiscoveryListener>& discovery_listener) {
EXPECT_CALL(nearby_connections_, StartDiscovery)
.WillOnce([&listener_remote, this](
const std::string& service_id,
DiscoveryOptionsPtr options,
mojo::PendingRemote<EndpointDiscoveryListener> listener,
NearbyConnectionsMojom::StartDiscoveryCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kStrategy, options->strategy);
EXPECT_TRUE(options->allowed_mediums->bluetooth);
EXPECT_TRUE(options->allowed_mediums->ble);
EXPECT_EQ(should_use_web_rtc_, options->allowed_mediums->web_rtc);
EXPECT_FALSE(options->allowed_mediums->wifi_lan);
EXPECT_EQ(should_use_wifidirect_,
options->allowed_mediums->wifi_direct);
EXPECT_EQ(
device::BluetoothUUID("0000fef3-0000-1000-8000-00805f9b34fb"),
options->fast_advertisement_service_uuid);
listener_remote.Bind(std::move(listener));
std::move(callback).Run(Status::kSuccess);
});
base::RunLoop run_loop;
NearbyConnectionsManager::ConnectionsCallback callback =
base::BindLambdaForTesting([&run_loop](Status status) {
EXPECT_EQ(status, Status::kSuccess);
run_loop.Quit();
});
nearby_connections_manager_->StartDiscovery(&discovery_listener, data_usage,
std::move(callback));
run_loop.Run();
}
void StartAdvertising(
mojo::Remote<ConnectionLifecycleListener>& listener_remote,
testing::NiceMock<MockIncomingConnectionListener>&
incoming_connection_listener) {
const std::vector<uint8_t> local_endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
EXPECT_CALL(nearby_connections_, StartAdvertising)
.WillOnce(
[&](const std::string& service_id,
const std::vector<uint8_t>& endpoint_info,
AdvertisingOptionsPtr options,
mojo::PendingRemote<ConnectionLifecycleListener> listener,
NearbyConnectionsMojom::StartAdvertisingCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(local_endpoint_info, endpoint_info);
EXPECT_EQ(kStrategy, options->strategy);
EXPECT_TRUE(options->enforce_topology_constraints);
listener_remote.Bind(std::move(listener));
std::move(callback).Run(Status::kSuccess);
});
base::RunLoop run_loop;
NearbyConnectionsManager::ConnectionsCallback callback =
base::BindLambdaForTesting([&run_loop](Status status) {
EXPECT_EQ(status, Status::kSuccess);
run_loop.Quit();
});
nearby_connections_manager_->StartAdvertising(
local_endpoint_info, &incoming_connection_listener,
NearbyConnectionsManager::PowerLevel::kHighPower,
NearbyConnectionsManager::DataUsage::kOnline, std::move(callback));
run_loop.Run();
}
enum class ConnectionResponse { kAccepted, kRejceted, kDisconnected };
NearbyConnection* Connect(
mojo::Remote<ConnectionLifecycleListener>& connection_listener_remote,
mojo::Remote<PayloadListener>& payload_listener_remote,
ConnectionResponse connection_response) {
const std::vector<uint8_t> local_endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
const std::vector<uint8_t> remote_endpoint_info(
std::begin(kRemoteEndpointInfo), std::end(kRemoteEndpointInfo));
const std::vector<uint8_t> raw_authentication_token(
std::begin(kRawAuthenticationToken), std::end(kRawAuthenticationToken));
base::RunLoop request_connection_run_loop;
EXPECT_CALL(nearby_connections_, RequestConnection)
.WillOnce(
[&](const std::string& service_id,
const std::vector<uint8_t>& endpoint_info,
const std::string& endpoint_id, ConnectionOptionsPtr options,
mojo::PendingRemote<ConnectionLifecycleListener> listener,
NearbyConnectionsMojom::RequestConnectionCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(local_endpoint_info, endpoint_info);
EXPECT_EQ(kRemoteEndpointId, endpoint_id);
connection_listener_remote.Bind(std::move(listener));
std::move(callback).Run(Status::kSuccess);
request_connection_run_loop.Quit();
});
base::RunLoop run_loop;
NearbyConnection* nearby_connection;
nearby_connections_manager_->Connect(
local_endpoint_info, kRemoteEndpointId,
/*bluetooth_mac_address=*/std::nullopt,
NearbyConnectionsManager::DataUsage::kOffline,
base::BindLambdaForTesting([&](NearbyConnection* connection) {
nearby_connection = connection;
}));
request_connection_run_loop.Run();
EXPECT_CALL(nearby_connections_, AcceptConnection)
.WillOnce(
[&](const std::string& service_id, const std::string& endpoint_id,
mojo::PendingRemote<PayloadListener> listener,
NearbyConnectionsMojom::AcceptConnectionCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kRemoteEndpointId, endpoint_id);
payload_listener_remote.Bind(std::move(listener));
std::move(callback).Run(Status::kSuccess);
run_loop.Quit();
});
connection_listener_remote->OnConnectionInitiated(
kRemoteEndpointId,
ConnectionInfo::New(kAuthenticationToken, raw_authentication_token,
remote_endpoint_info,
/*is_incoming_connection=*/false));
switch (connection_response) {
case ConnectionResponse::kAccepted:
connection_listener_remote->OnConnectionAccepted(kRemoteEndpointId);
break;
case ConnectionResponse::kRejceted:
connection_listener_remote->OnConnectionRejected(
kRemoteEndpointId, Status::kConnectionRejected);
break;
case ConnectionResponse::kDisconnected:
connection_listener_remote->OnDisconnected(kRemoteEndpointId);
break;
}
run_loop.Run();
return nearby_connection;
}
NearbyConnection* OnIncomingConnection(
mojo::Remote<ConnectionLifecycleListener>& connection_listener_remote,
testing::NiceMock<MockIncomingConnectionListener>&
incoming_connection_listener,
mojo::Remote<PayloadListener>& payload_listener_remote) {
base::RunLoop accept_run_loop;
EXPECT_CALL(incoming_connection_listener,
OnIncomingConnectionInitiated(testing::_, testing::_));
EXPECT_CALL(nearby_connections_, AcceptConnection)
.WillOnce(
[&](const std::string& service_id, const std::string& endpoint_id,
mojo::PendingRemote<PayloadListener> listener,
NearbyConnectionsMojom::AcceptConnectionCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kRemoteEndpointId, endpoint_id);
payload_listener_remote.Bind(std::move(listener));
std::move(callback).Run(Status::kSuccess);
accept_run_loop.Quit();
});
const std::vector<uint8_t> remote_endpoint_info(
std::begin(kRemoteEndpointInfo), std::end(kRemoteEndpointInfo));
const std::vector<uint8_t> raw_authentication_token(
std::begin(kRawAuthenticationToken), std::end(kRawAuthenticationToken));
connection_listener_remote->OnConnectionInitiated(
kRemoteEndpointId,
ConnectionInfo::New(kAuthenticationToken, raw_authentication_token,
remote_endpoint_info,
/*is_incoming_connection=*/true));
accept_run_loop.Run();
NearbyConnection* nearby_connection;
base::RunLoop incoming_connection_run_loop;
EXPECT_CALL(
incoming_connection_listener,
OnIncomingConnectionAccepted(testing::_, testing::_, testing::_))
.WillOnce([&](const std::string&, const std::vector<uint8_t>&,
NearbyConnection* connection) {
nearby_connection = connection;
incoming_connection_run_loop.Quit();
});
connection_listener_remote->OnConnectionAccepted(kRemoteEndpointId);
incoming_connection_run_loop.Run();
EXPECT_EQ(raw_authentication_token,
nearby_connections_manager_->GetRawAuthenticationToken(
kRemoteEndpointId));
return nearby_connection;
}
void SendPayload(
int64_t payload_id,
testing::NiceMock<MockPayloadStatusListener>& payload_listener) {
const std::vector<uint8_t> expected_payload(std::begin(kPayload),
std::end(kPayload));
base::File file;
InitializeTemporaryFile(file);
base::RunLoop run_loop;
EXPECT_CALL(nearby_connections_, SendPayload)
.WillOnce([&](const std::string& service_id,
const std::vector<std::string>& endpoint_ids,
PayloadPtr payload,
NearbyConnectionsMojom::SendPayloadCallback callback) {
ASSERT_EQ(1u, endpoint_ids.size());
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kRemoteEndpointId, endpoint_ids.front());
ASSERT_TRUE(payload);
EXPECT_EQ(payload_id, payload->id);
ASSERT_EQ(PayloadContent::Tag::kFile, payload->content->which());
base::ScopedAllowBlockingForTesting allow_blocking;
base::File file = std::move(payload->content->get_file()->file);
std::vector<uint8_t> payload_bytes(file.GetLength());
EXPECT_TRUE(file.ReadAndCheck(
/*offset=*/0, base::make_span(payload_bytes)));
EXPECT_EQ(expected_payload, payload_bytes);
std::move(callback).Run(Status::kSuccess);
run_loop.Quit();
});
nearby_connections_manager_->Send(
kRemoteEndpointId,
Payload::New(payload_id, PayloadContent::NewFile(
FilePayload::New(std::move(file)))),
payload_listener.GetWeakPtr());
run_loop.Run();
}
void ConnectV3(
nearby::presence::PresenceDevice remote_presence_device,
mojo::Remote<ConnectionListenerV3>& connection_listener_v3_remote,
mojo::Remote<PayloadListenerV3>& payload_listener_v3_remote,
nearby::connections::mojom::InitialConnectionInfoV3Ptr info_v3,
Status on_connection_result_status) {
ash::nearby::presence::mojom::PresenceDevicePtr presence_device_mojom =
ash::nearby::presence::BuildPresenceMojomDevice(remote_presence_device);
base::RunLoop request_connection_run_loop;
EXPECT_CALL(nearby_connections_, RequestConnectionV3)
.WillOnce(
[&](const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
ConnectionOptionsPtr options,
mojo::PendingRemote<ConnectionListenerV3> listener,
NearbyConnectionsMojom::RequestConnectionV3Callback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(remote_device->endpoint_id, kRemoteEndpointId);
connection_listener_v3_remote.Bind(std::move(listener));
std::move(callback).Run(Status::kSuccess);
request_connection_run_loop.Quit();
});
base::RunLoop accept_or_reject_run_loop;
NearbyConnection* nearby_connection;
nearby_connections_manager_->ConnectV3(
remote_presence_device, NearbyConnectionsManager::DataUsage::kOffline,
base::BindLambdaForTesting([&](NearbyConnection* connection) {
nearby_connection = connection;
if (on_connection_result_status == Status::kSuccess) {
EXPECT_TRUE(nearby_connection);
} else {
EXPECT_FALSE(nearby_connection);
}
}));
task_environment_.FastForwardBy(kConnectV3ToSuccessfulConnectionLatency);
request_connection_run_loop.Run();
if (info_v3->authentication_status ==
nearby::connections::mojom::AuthenticationStatus::kSuccess) {
EXPECT_CALL(nearby_connections_, AcceptConnectionV3)
.WillOnce(
[&](const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
mojo::PendingRemote<PayloadListenerV3> listener,
NearbyConnectionsMojom::AcceptConnectionV3Callback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(remote_device->endpoint_id, kRemoteEndpointId);
payload_listener_v3_remote.Bind(std::move(listener));
std::move(callback).Run(Status::kSuccess);
accept_or_reject_run_loop.Quit();
});
} else {
EXPECT_CALL(nearby_connections_, RejectConnectionV3)
.WillOnce(
[&](const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
NearbyConnectionsMojom::RejectConnectionV3Callback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(remote_device->endpoint_id, kRemoteEndpointId);
std::move(callback).Run(Status::kSuccess);
accept_or_reject_run_loop.Quit();
});
}
connection_listener_v3_remote->OnConnectionInitiatedV3(
presence_device_mojom->endpoint_id, std::move(info_v3));
connection_listener_v3_remote->OnConnectionResultV3(
presence_device_mojom->endpoint_id, on_connection_result_status);
accept_or_reject_run_loop.Run();
}
base::HistogramTester* histogram_tester() { return histogram_tester_.get(); }
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<base::HistogramTester> histogram_tester_;
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
bool should_use_web_rtc_ = true;
bool should_use_wifilan_ = false;
bool should_use_wifidirect_ = false;
NearbyConnectionsManager::DataUsage default_data_usage_ =
NearbyConnectionsManager::DataUsage::kWifiOnly;
std::unique_ptr<net::test::MockNetworkChangeNotifier> network_notifier_ =
net::test::MockNetworkChangeNotifier::Create();
base::ScopedDisallowBlocking disallow_blocking_;
testing::NiceMock<ash::nearby::MockNearbyConnections> nearby_connections_;
testing::NiceMock<ash::nearby::MockNearbyProcessManager>
nearby_process_manager_;
std::unique_ptr<NearbyConnectionsManagerImpl> nearby_connections_manager_;
};
TEST_F(NearbyConnectionsManagerImplTest, DiscoveryFlow) {
const std::vector<uint8_t> endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(listener_remote, discovery_listener);
// Invoking OnEndpointFound over remote will invoke OnEndpointDiscovered.
base::RunLoop discovered_run_loop;
EXPECT_CALL(discovery_listener,
OnEndpointDiscovered(testing::Eq(kEndpointId),
testing::Eq(endpoint_info)))
.WillOnce([&discovered_run_loop]() { discovered_run_loop.Quit(); });
listener_remote->OnEndpointFound(
kEndpointId, DiscoveredEndpointInfo::New(endpoint_info, kServiceId));
discovered_run_loop.Run();
// Invoking OnEndpointFound over remote on same endpointId will do nothing.
EXPECT_CALL(discovery_listener, OnEndpointDiscovered(testing::_, testing::_))
.Times(0);
listener_remote->OnEndpointFound(
kEndpointId, DiscoveredEndpointInfo::New(endpoint_info, kServiceId));
// Invoking OnEndpointLost over remote will invoke OnEndpointLost.
base::RunLoop lost_run_loop;
EXPECT_CALL(discovery_listener, OnEndpointLost(testing::Eq(kEndpointId)))
.WillOnce([&lost_run_loop]() { lost_run_loop.Quit(); });
listener_remote->OnEndpointLost(kEndpointId);
lost_run_loop.Run();
// Invoking OnEndpointLost over remote on same endpointId will do nothing.
EXPECT_CALL(discovery_listener, OnEndpointLost(testing::_)).Times(0);
listener_remote->OnEndpointLost(kEndpointId);
// After OnEndpointLost the same endpointId can be discovered again.
base::RunLoop discovered_run_loop_2;
EXPECT_CALL(discovery_listener,
OnEndpointDiscovered(testing::Eq(kEndpointId),
testing::Eq(endpoint_info)))
.WillOnce([&discovered_run_loop_2]() { discovered_run_loop_2.Quit(); });
listener_remote->OnEndpointFound(
kEndpointId, DiscoveredEndpointInfo::New(endpoint_info, kServiceId));
discovered_run_loop_2.Run();
// Stop discvoery will call through mojo.
base::RunLoop stop_discovery_run_loop;
EXPECT_CALL(nearby_connections_, StopDiscovery)
.WillOnce([&stop_discovery_run_loop](
const std::string& service_id,
NearbyConnectionsMojom::StopDiscoveryCallback callback) {
EXPECT_EQ(kServiceId, service_id);
std::move(callback).Run(Status::kSuccess);
stop_discovery_run_loop.Quit();
});
nearby_connections_manager_->StopDiscovery();
stop_discovery_run_loop.Run();
// StartDiscovery again will succeed.
listener_remote.reset();
StartDiscovery(listener_remote, discovery_listener);
// Same endpointId can be discovered again.
base::RunLoop discovered_run_loop_3;
EXPECT_CALL(discovery_listener,
OnEndpointDiscovered(testing::Eq(kEndpointId),
testing::Eq(endpoint_info)))
.WillOnce([&discovered_run_loop_3]() { discovered_run_loop_3.Quit(); });
listener_remote->OnEndpointFound(
kEndpointId, DiscoveredEndpointInfo::New(endpoint_info, kServiceId));
discovered_run_loop_3.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, DiscoveryProcessStopped) {
const std::vector<uint8_t> endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(listener_remote, discovery_listener);
EXPECT_CALL(nearby_connections_, StopAllEndpoints).Times(0);
nearby_connections_manager_->OnNearbyProcessStopped(
ash::nearby::NearbyProcessManager::NearbyProcessShutdownReason::kNormal);
// Invoking OnEndpointFound will do nothing.
EXPECT_CALL(discovery_listener, OnEndpointDiscovered(testing::_, testing::_))
.Times(0);
listener_remote->OnEndpointFound(
kEndpointId, DiscoveredEndpointInfo::New(endpoint_info, kServiceId));
}
TEST_F(NearbyConnectionsManagerImplTest, StopDiscoveryBeforeStart) {
EXPECT_CALL(nearby_connections_, StopDiscovery).Times(0);
nearby_connections_manager_->StopDiscovery();
}
/******************************************************************************/
// Begin: NearbyConnectionsManagerImplTestConnectionMediums
/******************************************************************************/
using ConnectionMediumsTestParam =
std::tuple<NearbyConnectionsManager::DataUsage,
net::NetworkChangeNotifier::ConnectionType,
bool,
bool,
bool>;
class NearbyConnectionsManagerImplTestConnectionMediums
: public NearbyConnectionsManagerImplTest,
public testing::WithParamInterface<ConnectionMediumsTestParam> {};
TEST_P(NearbyConnectionsManagerImplTestConnectionMediums,
RequestConnection_MediumSelection) {
const ConnectionMediumsTestParam& param = GetParam();
NearbyConnectionsManager::DataUsage data_usage = std::get<0>(param);
net::NetworkChangeNotifier::ConnectionType connection_type =
std::get<1>(param);
bool is_webrtc_enabled = std::get<2>(GetParam());
bool is_wifilan_enabled = std::get<3>(GetParam());
bool is_wifidirect_enabled = std::get<4>(GetParam());
std::vector<base::test::FeatureRef> enabled_features;
std::vector<base::test::FeatureRef> disabled_features;
if (is_webrtc_enabled) {
enabled_features.push_back(features::kNearbySharingWebRtc);
} else {
disabled_features.push_back(features::kNearbySharingWebRtc);
}
if (is_wifilan_enabled) {
enabled_features.push_back(features::kNearbySharingWifiLan);
} else {
disabled_features.push_back(features::kNearbySharingWifiLan);
}
if (is_wifidirect_enabled) {
enabled_features.push_back(features::kNearbySharingWifiDirect);
} else {
disabled_features.push_back(features::kNearbySharingWifiDirect);
}
scoped_feature_list_.Reset();
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
network_notifier_->SetConnectionType(connection_type);
network_notifier_->SetUseDefaultConnectionCostImplementation(true);
bool should_use_internet =
data_usage != NearbyConnectionsManager::DataUsage::kOffline &&
connection_type != net::NetworkChangeNotifier::CONNECTION_NONE &&
(data_usage != NearbyConnectionsManager::DataUsage::kWifiOnly ||
(net::NetworkChangeNotifier::GetConnectionCost() !=
net::NetworkChangeNotifier::CONNECTION_COST_METERED));
bool is_connection_wifi_or_ethernet =
connection_type == net::NetworkChangeNotifier::CONNECTION_WIFI ||
connection_type == net::NetworkChangeNotifier::CONNECTION_ETHERNET;
should_use_web_rtc_ = is_webrtc_enabled && should_use_internet;
should_use_wifilan_ = is_wifilan_enabled && should_use_internet &&
is_connection_wifi_or_ethernet;
should_use_wifidirect_ = is_wifidirect_enabled;
// TODO(crbug.com/1129069): Update when WiFi LAN is supported.
auto expected_mediums = MediumSelection::New(
/*bluetooth=*/true,
/*ble=*/false,
/*web_rtc=*/should_use_web_rtc_,
/*wifi_lan=*/should_use_wifilan_,
/*wifi_direct=*/should_use_wifidirect_);
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, data_usage, discovery_listener);
base::RunLoop run_loop;
const std::vector<uint8_t> local_endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
EXPECT_CALL(nearby_connections_, RequestConnection)
.WillOnce(
[&](const std::string& service_id,
const std::vector<uint8_t>& endpoint_info,
const std::string& endpoint_id, ConnectionOptionsPtr options,
mojo::PendingRemote<ConnectionLifecycleListener> listener,
NearbyConnectionsMojom::RequestConnectionCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(local_endpoint_info, endpoint_info);
EXPECT_EQ(kRemoteEndpointId, endpoint_id);
EXPECT_EQ(expected_mediums, options->allowed_mediums);
std::move(callback).Run(Status::kSuccess);
run_loop.Quit();
});
nearby_connections_manager_->Connect(local_endpoint_info, kRemoteEndpointId,
/*bluetooth_mac_address=*/std::nullopt,
data_usage, base::DoNothing());
run_loop.Run();
}
INSTANTIATE_TEST_SUITE_P(
NearbyConnectionsManagerImplTestConnectionMediums,
NearbyConnectionsManagerImplTestConnectionMediums,
testing::Combine(
testing::Values(NearbyConnectionsManager::DataUsage::kWifiOnly,
NearbyConnectionsManager::DataUsage::kOffline,
NearbyConnectionsManager::DataUsage::kOnline),
testing::Values(net::NetworkChangeNotifier::CONNECTION_NONE,
net::NetworkChangeNotifier::CONNECTION_WIFI,
net::NetworkChangeNotifier::CONNECTION_3G),
testing::Bool(),
testing::Bool(),
testing::Bool()));
/******************************************************************************/
// End: NearbyConnectionsManagerImplTestConnectionMediums
/******************************************************************************/
/******************************************************************************/
// Begin: NearbyConnectionsManagerImplTestConnectionBluetoothMacAddress
/******************************************************************************/
struct ConnectionBluetoothMacAddressTestData {
std::optional<std::vector<uint8_t>> bluetooth_mac_address;
std::optional<std::vector<uint8_t>> expected_bluetooth_mac_address;
} kConnectionBluetoothMacAddressTestData[] = {
{std::make_optional(kBluetoothMacAddress),
std::make_optional(kBluetoothMacAddress)},
{std::make_optional(kInvalidBluetoothMacAddress), std::nullopt},
{std::nullopt, std::nullopt}};
class NearbyConnectionsManagerImplTestConnectionBluetoothMacAddress
: public NearbyConnectionsManagerImplTest,
public testing::WithParamInterface<
ConnectionBluetoothMacAddressTestData> {};
TEST_P(NearbyConnectionsManagerImplTestConnectionBluetoothMacAddress,
RequestConnection_BluetoothMacAddress) {
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
base::RunLoop run_loop;
const std::vector<uint8_t> local_endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
EXPECT_CALL(nearby_connections_, RequestConnection)
.WillOnce(
[&](const std::string& service_id,
const std::vector<uint8_t>& endpoint_info,
const std::string& endpoint_id, ConnectionOptionsPtr options,
mojo::PendingRemote<ConnectionLifecycleListener> listener,
NearbyConnectionsMojom::RequestConnectionCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(local_endpoint_info, endpoint_info);
EXPECT_EQ(kRemoteEndpointId, endpoint_id);
EXPECT_EQ(GetParam().expected_bluetooth_mac_address,
options->remote_bluetooth_mac_address);
std::move(callback).Run(Status::kSuccess);
run_loop.Quit();
});
nearby_connections_manager_->Connect(
local_endpoint_info, kRemoteEndpointId, GetParam().bluetooth_mac_address,
NearbyConnectionsManager::DataUsage::kOffline, base::DoNothing());
run_loop.Run();
}
INSTANTIATE_TEST_SUITE_P(
NearbyConnectionsManagerImplTestConnectionBluetoothMacAddress,
NearbyConnectionsManagerImplTestConnectionBluetoothMacAddress,
testing::ValuesIn(kConnectionBluetoothMacAddressTestData));
/******************************************************************************/
// End: NearbyConnectionsManagerImplTestConnectionBluetoothMacAddress
/******************************************************************************/
TEST_F(NearbyConnectionsManagerImplTest, ConnectRejected) {
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* nearby_connection =
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kRejceted);
EXPECT_FALSE(nearby_connection);
EXPECT_FALSE(nearby_connections_manager_->GetRawAuthenticationToken(
kRemoteEndpointId));
}
TEST_F(NearbyConnectionsManagerImplTest, ConnectDisconnected) {
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* nearby_connection =
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kDisconnected);
EXPECT_FALSE(nearby_connection);
EXPECT_FALSE(nearby_connections_manager_->GetRawAuthenticationToken(
kRemoteEndpointId));
}
TEST_F(NearbyConnectionsManagerImplTest, ConnectAccepted) {
const std::vector<uint8_t> raw_authentication_token(
std::begin(kRawAuthenticationToken), std::end(kRawAuthenticationToken));
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* nearby_connection =
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
EXPECT_TRUE(nearby_connection);
EXPECT_EQ(raw_authentication_token,
nearby_connections_manager_->GetRawAuthenticationToken(
kRemoteEndpointId));
}
TEST_F(NearbyConnectionsManagerImplTest, ConnectReadBeforeAppend) {
const std::vector<uint8_t> byte_payload(std::begin(kBytePayload),
std::end(kBytePayload));
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* nearby_connection =
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
ASSERT_TRUE(nearby_connection);
// Read before message is appended should also succeed.
base::RunLoop read_run_loop;
nearby_connection->Read(base::BindLambdaForTesting(
[&](std::optional<std::vector<uint8_t>> bytes) {
EXPECT_EQ(byte_payload, bytes);
read_run_loop.Quit();
}));
payload_listener_remote->OnPayloadReceived(
kRemoteEndpointId,
Payload::New(kPayloadId,
PayloadContent::NewBytes(BytesPayload::New(byte_payload))));
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
read_run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, ConnectReadAfterAppend) {
const std::vector<uint8_t> byte_payload(std::begin(kBytePayload),
std::end(kBytePayload));
const std::vector<uint8_t> byte_payload_2(std::begin(kBytePayload2),
std::end(kBytePayload2));
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* nearby_connection =
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
ASSERT_TRUE(nearby_connection);
// Read after message is appended should succeed.
payload_listener_remote->OnPayloadReceived(
kRemoteEndpointId,
Payload::New(kPayloadId,
PayloadContent::NewBytes(BytesPayload::New(byte_payload))));
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
payload_listener_remote->OnPayloadReceived(
kRemoteEndpointId,
Payload::New(kPayloadId2, PayloadContent::NewBytes(
BytesPayload::New(byte_payload_2))));
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId2, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
base::RunLoop read_run_loop;
nearby_connection->Read(base::BindLambdaForTesting(
[&](std::optional<std::vector<uint8_t>> bytes) {
EXPECT_EQ(byte_payload, bytes);
read_run_loop.Quit();
}));
read_run_loop.Run();
base::RunLoop read_run_loop_2;
nearby_connection->Read(base::BindLambdaForTesting(
[&](std::optional<std::vector<uint8_t>> bytes) {
EXPECT_EQ(byte_payload_2, bytes);
read_run_loop_2.Quit();
}));
read_run_loop_2.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, ConnectWrite) {
const std::vector<uint8_t> byte_payload(std::begin(kBytePayload),
std::end(kBytePayload));
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* nearby_connection =
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
ASSERT_TRUE(nearby_connection);
base::RunLoop run_loop;
EXPECT_CALL(nearby_connections_, SendPayload)
.WillOnce([&](const std::string& service_id,
const std::vector<std::string>& endpoint_ids,
PayloadPtr payload,
NearbyConnectionsMojom::SendPayloadCallback callback) {
EXPECT_EQ(kServiceId, service_id);
ASSERT_EQ(1u, endpoint_ids.size());
EXPECT_EQ(kRemoteEndpointId, endpoint_ids.front());
ASSERT_TRUE(payload);
ASSERT_EQ(PayloadContent::Tag::kBytes, payload->content->which());
EXPECT_EQ(byte_payload, payload->content->get_bytes()->bytes);
std::move(callback).Run(Status::kSuccess);
run_loop.Quit();
});
nearby_connection->Write(byte_payload);
run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, ConnectClosed) {
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* nearby_connection =
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
ASSERT_TRUE(nearby_connection);
// Close should invoke disconnection callback and read callback.
base::RunLoop close_run_loop;
nearby_connection->SetDisconnectionListener(
base::BindLambdaForTesting([&]() { close_run_loop.Quit(); }));
base::RunLoop read_run_loop_3;
nearby_connection->Read(base::BindLambdaForTesting(
[&](std::optional<std::vector<uint8_t>> bytes) {
EXPECT_FALSE(bytes);
read_run_loop_3.Quit();
}));
base::RunLoop disconnect_run_loop;
EXPECT_CALL(nearby_connections_, DisconnectFromEndpoint)
.WillOnce(
[&](const std::string& service_id, const std::string& endpoint_id,
NearbyConnectionsMojom::DisconnectFromEndpointCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kRemoteEndpointId, endpoint_id);
std::move(callback).Run(Status::kSuccess);
disconnect_run_loop.Quit();
});
nearby_connection->Close();
close_run_loop.Run();
read_run_loop_3.Run();
disconnect_run_loop.Run();
EXPECT_FALSE(nearby_connections_manager_->GetRawAuthenticationToken(
kRemoteEndpointId));
}
TEST_F(NearbyConnectionsManagerImplTest, ConnectClosedByRemote) {
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* nearby_connection =
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
ASSERT_TRUE(nearby_connection);
// Remote closing should invoke disconnection callback and read callback.
base::RunLoop close_run_loop;
nearby_connection->SetDisconnectionListener(
base::BindLambdaForTesting([&]() { close_run_loop.Quit(); }));
base::RunLoop read_run_loop;
nearby_connection->Read(base::BindLambdaForTesting(
[&](std::optional<std::vector<uint8_t>> bytes) {
EXPECT_FALSE(bytes);
read_run_loop.Quit();
}));
connection_listener_remote->OnDisconnected(kRemoteEndpointId);
close_run_loop.Run();
read_run_loop.Run();
EXPECT_FALSE(nearby_connections_manager_->GetRawAuthenticationToken(
kRemoteEndpointId));
}
TEST_F(NearbyConnectionsManagerImplTest, ConnectClosedByClient) {
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* nearby_connection =
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
ASSERT_TRUE(nearby_connection);
// Remote closing should invoke disconnection callback and read callback.
base::RunLoop close_run_loop;
nearby_connection->SetDisconnectionListener(
base::BindLambdaForTesting([&]() { close_run_loop.Quit(); }));
base::RunLoop read_run_loop;
nearby_connection->Read(base::BindLambdaForTesting(
[&](std::optional<std::vector<uint8_t>> bytes) {
EXPECT_FALSE(bytes);
read_run_loop.Quit();
}));
base::RunLoop disconnect_run_loop;
EXPECT_CALL(nearby_connections_, DisconnectFromEndpoint)
.WillOnce(
[&](const std::string& service_id, const std::string& endpoint_id,
NearbyConnectionsMojom::DisconnectFromEndpointCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kRemoteEndpointId, endpoint_id);
std::move(callback).Run(Status::kSuccess);
disconnect_run_loop.Quit();
});
nearby_connections_manager_->Disconnect(kRemoteEndpointId);
close_run_loop.Run();
read_run_loop.Run();
disconnect_run_loop.Run();
EXPECT_FALSE(nearby_connections_manager_->GetRawAuthenticationToken(
kRemoteEndpointId));
}
TEST_F(NearbyConnectionsManagerImplTest, ConnectSendPayload) {
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
testing::NiceMock<MockPayloadStatusListener> payload_listener;
SendPayload(kPayloadId, payload_listener);
auto expected_update = PayloadTransferUpdate::New(
kPayloadId, PayloadStatus::kInProgress, kTotalSize, kBytesTransferred);
base::RunLoop payload_run_loop;
EXPECT_CALL(payload_listener, OnStatusUpdate(testing::_, testing::_))
.WillOnce([&](MockPayloadStatusListener::PayloadTransferUpdatePtr update,
std::optional<Medium> upgraded_medium) {
EXPECT_EQ(expected_update, update);
EXPECT_EQ(std::nullopt, upgraded_medium);
payload_run_loop.Quit();
});
payload_listener_remote->OnPayloadTransferUpdate(kRemoteEndpointId,
expected_update.Clone());
payload_run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, ConnectCancelPayload) {
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
testing::NiceMock<MockPayloadStatusListener> payload_listener;
SendPayload(kPayloadId, payload_listener);
base::RunLoop cancel_run_loop;
EXPECT_CALL(nearby_connections_, CancelPayload)
.WillOnce([&](const std::string& service_id, int64_t payload_id,
NearbyConnectionsMojom::CancelPayloadCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kPayloadId, payload_id);
std::move(callback).Run(Status::kSuccess);
cancel_run_loop.Quit();
});
base::RunLoop payload_run_loop;
EXPECT_CALL(payload_listener, OnStatusUpdate(testing::_, testing::_))
.WillOnce([&](MockPayloadStatusListener::PayloadTransferUpdatePtr update,
std::optional<Medium> upgraded_medium) {
EXPECT_EQ(kPayloadId, update->payload_id);
EXPECT_EQ(PayloadStatus::kCanceled, update->status);
EXPECT_EQ(0u, update->total_bytes);
EXPECT_EQ(0u, update->bytes_transferred);
EXPECT_EQ(std::nullopt, upgraded_medium);
payload_run_loop.Quit();
});
nearby_connections_manager_->Cancel(kPayloadId);
payload_run_loop.Run();
cancel_run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest,
ConnectCancelPayload_MultiplePayloads_HandleDestroyedPayloadListener) {
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
// Send two payloads with the same listener. We will eventually cancel both
// payloads, but we will reset the listener before cancelling the second
// payload. This can happen in practice: if the first payload is cancelled or
// fails, it makes sense to clean everything up before waiting for the other
// payload cancellation/failure signals. We are testing that the connections
// manager handles the missing listener gracefully.
auto payload_listener =
std::make_unique<testing::NiceMock<MockPayloadStatusListener>>();
SendPayload(kPayloadId, *payload_listener);
SendPayload(kPayloadId2, *payload_listener);
base::RunLoop cancel_run_loop;
EXPECT_CALL(nearby_connections_, CancelPayload)
.Times(2)
.WillOnce([&](const std::string& service_id, int64_t payload_id,
NearbyConnectionsMojom::CancelPayloadCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kPayloadId, payload_id);
std::move(callback).Run(Status::kSuccess);
})
.WillOnce([&](const std::string& service_id, int64_t payload_id,
NearbyConnectionsMojom::CancelPayloadCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kPayloadId2, payload_id);
std::move(callback).Run(Status::kSuccess);
cancel_run_loop.Quit();
});
// Because the payload listener is reset before the second payload is
// cancelled, we can only receive the first status update.
base::RunLoop payload_run_loop;
EXPECT_CALL(*payload_listener, OnStatusUpdate(testing::_, testing::_))
.Times(1)
.WillOnce([&](MockPayloadStatusListener::PayloadTransferUpdatePtr update,
std::optional<Medium> upgraded_medium) {
EXPECT_EQ(kPayloadId, update->payload_id);
EXPECT_EQ(PayloadStatus::kCanceled, update->status);
EXPECT_EQ(0u, update->total_bytes);
EXPECT_EQ(0u, update->bytes_transferred);
EXPECT_EQ(std::nullopt, upgraded_medium);
// Destroy the PayloadStatusListener after the first payload is
// cancelled.
payload_listener.reset();
payload_run_loop.Quit();
});
nearby_connections_manager_->Cancel(kPayloadId);
nearby_connections_manager_->Cancel(kPayloadId2);
payload_run_loop.Run();
cancel_run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, ConnectTimeout) {
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will time out.
const std::vector<uint8_t> local_endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
NearbyConnectionsMojom::RequestConnectionCallback connect_callback;
EXPECT_CALL(nearby_connections_, RequestConnection)
.WillOnce(
[&](const std::string& service_id,
const std::vector<uint8_t>& endpoint_info,
const std::string& endpoint_id, ConnectionOptionsPtr options,
mojo::PendingRemote<ConnectionLifecycleListener> listener,
NearbyConnectionsMojom::RequestConnectionCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(local_endpoint_info, endpoint_info);
EXPECT_EQ(kRemoteEndpointId, endpoint_id);
connection_listener_remote.Bind(std::move(listener));
// Do not call callback until connection timed out.
connect_callback = std::move(callback);
});
// Timing out should call disconnect.
EXPECT_CALL(nearby_connections_, DisconnectFromEndpoint)
.WillOnce(
[&](const std::string& service_id, const std::string& endpoint_id,
NearbyConnectionsMojom::DisconnectFromEndpointCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kRemoteEndpointId, endpoint_id);
std::move(callback).Run(Status::kSuccess);
});
base::RunLoop run_loop;
NearbyConnection* nearby_connection = nullptr;
nearby_connections_manager_->Connect(
local_endpoint_info, kRemoteEndpointId,
/*bluetooth_mac_address=*/std::nullopt,
NearbyConnectionsManager::DataUsage::kOffline,
base::BindLambdaForTesting([&](NearbyConnection* connection) {
nearby_connection = connection;
run_loop.Quit();
}));
// Simulate time passing until timeout is reached.
task_environment_.FastForwardBy(kInitiateNearbyConnectionTimeout);
run_loop.Run();
// Expect callback to be called with null connection.
EXPECT_FALSE(nearby_connection);
// Resolving the connect callback after timeout should do nothing.
std::move(connect_callback).Run(Status::kSuccess);
}
TEST_F(NearbyConnectionsManagerImplTest, StartAdvertising) {
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(connection_listener_remote, incoming_connection_listener);
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* connection = OnIncomingConnection(
connection_listener_remote, incoming_connection_listener,
payload_listener_remote);
EXPECT_TRUE(connection);
}
TEST_F(NearbyConnectionsManagerImplTest, IncomingPayloadStatusListener) {
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(connection_listener_remote, incoming_connection_listener);
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* connection = OnIncomingConnection(
connection_listener_remote, incoming_connection_listener,
payload_listener_remote);
EXPECT_TRUE(connection);
testing::NiceMock<MockPayloadStatusListener> payload_listener;
nearby_connections_manager_->RegisterPayloadStatusListener(
kPayloadId, payload_listener.GetWeakPtr());
auto expected_update = PayloadTransferUpdate::New(
kPayloadId, PayloadStatus::kInProgress, kTotalSize, kBytesTransferred);
base::RunLoop payload_run_loop;
EXPECT_CALL(payload_listener, OnStatusUpdate(testing::_, testing::_))
.WillOnce([&](MockPayloadStatusListener::PayloadTransferUpdatePtr update,
std::optional<Medium> upgraded_medium) {
EXPECT_EQ(expected_update, update);
EXPECT_EQ(std::nullopt, upgraded_medium);
payload_run_loop.Quit();
});
payload_listener_remote->OnPayloadTransferUpdate(kRemoteEndpointId,
expected_update.Clone());
payload_run_loop.Run();
// After success status.
base::RunLoop payload_run_loop_2;
EXPECT_CALL(payload_listener, OnStatusUpdate(testing::_, testing::_))
.WillOnce([&payload_run_loop_2]() { payload_run_loop_2.Quit(); });
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
payload_run_loop_2.Run();
// PayloadStatusListener will be unregistered and won't receive further
// updates.
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
EXPECT_CALL(payload_listener, OnStatusUpdate(testing::_, testing::_))
.Times(0);
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
}
TEST_F(
NearbyConnectionsManagerImplTest,
IncomingPayloadStatusListener_MultiplePayloads_HandleDestroyedPayloadListener) {
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(connection_listener_remote, incoming_connection_listener);
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* connection = OnIncomingConnection(
connection_listener_remote, incoming_connection_listener,
payload_listener_remote);
EXPECT_TRUE(connection);
// Register three payloads with the same listener. This happens when multiple
// payloads are included in the same transfer. Use both file and byte payloads
// to ensure control-frame logic is not invoked for either.
auto payload_listener =
std::make_unique<testing::NiceMock<MockPayloadStatusListener>>();
nearby_connections_manager_->RegisterPayloadStatusListener(
kPayloadId, payload_listener->GetWeakPtr());
nearby_connections_manager_->RegisterPayloadStatusListener(
kPayloadId2, payload_listener->GetWeakPtr());
nearby_connections_manager_->RegisterPayloadStatusListener(
kPayloadId3, payload_listener->GetWeakPtr());
base::File file1, file2;
InitializeTemporaryFile(file1);
InitializeTemporaryFile(file2);
payload_listener_remote->OnPayloadReceived(
kRemoteEndpointId,
Payload::New(kPayloadId, PayloadContent::NewFile(
FilePayload::New(std::move(file1)))));
payload_listener_remote->OnPayloadReceived(
kRemoteEndpointId,
Payload::New(kPayloadId2, PayloadContent::NewFile(
FilePayload::New(std::move(file2)))));
const std::vector<uint8_t> byte_payload(std::begin(kBytePayload),
std::end(kBytePayload));
payload_listener_remote->OnPayloadReceived(
kRemoteEndpointId,
Payload::New(kPayloadId3,
PayloadContent::NewBytes(BytesPayload::New(byte_payload))));
// Fail the first payload and destroy the payload listener. Then, send updates
// that the second and third payloads succeeded; this is unlikely in practice,
// but we test to ensure that no control-frame logic is exercised. Expect that
// a status update is only sent for the first payload failure because the
// listener does not exist afterwards.
base::RunLoop payload_run_loop;
EXPECT_CALL(*payload_listener, OnStatusUpdate(testing::_, testing::_))
.Times(1)
.WillOnce([&](MockPayloadStatusListener::PayloadTransferUpdatePtr update,
std::optional<Medium> upgraded_medium) {
EXPECT_EQ(kPayloadId, update->payload_id);
EXPECT_EQ(PayloadStatus::kFailure, update->status);
EXPECT_EQ(kTotalSize, update->total_bytes);
EXPECT_EQ(0u, update->bytes_transferred);
EXPECT_EQ(std::nullopt, upgraded_medium);
// Destroy the PayloadStatusListener after the first payload fails.
payload_listener.reset();
});
// Ensure that no control-frame logic is run, which can happen when a payload
// update is received for an unregistered payload.
EXPECT_CALL(nearby_connections_, CancelPayload).Times(0);
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId, PayloadStatus::kFailure,
kTotalSize, /*bytes_transferred=*/0u));
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId2, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/0u));
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId3, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/0u));
payload_run_loop.RunUntilIdle();
// Ensure that no control-frame logic was run, notably that no bytes were
// written to the connection. Closing the connection will invoke any
// outstanding Read() callback if there are no bytes to read.
base::RunLoop read_run_loop;
connection->Read(base::BindLambdaForTesting(
[&](std::optional<std::vector<uint8_t>> bytes) {
EXPECT_FALSE(bytes);
read_run_loop.Quit();
}));
connection->Close();
read_run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, IncomingRegisterPayloadPath) {
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(connection_listener_remote, incoming_connection_listener);
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* connection = OnIncomingConnection(
connection_listener_remote, incoming_connection_listener,
payload_listener_remote);
EXPECT_TRUE(connection);
base::RunLoop register_payload_run_loop;
EXPECT_CALL(nearby_connections_, RegisterPayloadFile)
.WillOnce(
[&](const std::string& service_id, int64_t payload_id,
base::File input_file, base::File output_file,
NearbyConnectionsMojom::RegisterPayloadFileCallback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kPayloadId, payload_id);
ASSERT_TRUE(input_file.IsValid());
ASSERT_TRUE(output_file.IsValid());
VerifyFileReadWrite(input_file, output_file);
std::move(callback).Run(Status::kSuccess);
});
base::FilePath path;
{
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(base::CreateTemporaryFile(&path));
}
NearbyConnectionsManager::ConnectionsCallback callback =
base::BindLambdaForTesting([®ister_payload_run_loop](Status status) {
EXPECT_EQ(status, Status::kSuccess);
register_payload_run_loop.Quit();
});
nearby_connections_manager_->RegisterPayloadPath(kPayloadId, path,
std::move(callback));
register_payload_run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, IncomingBytesPayload) {
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(connection_listener_remote, incoming_connection_listener);
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* connection = OnIncomingConnection(
connection_listener_remote, incoming_connection_listener,
payload_listener_remote);
EXPECT_TRUE(connection);
testing::NiceMock<MockPayloadStatusListener> payload_listener;
nearby_connections_manager_->RegisterPayloadStatusListener(
kPayloadId, payload_listener.GetWeakPtr());
const std::vector<uint8_t> expected_payload(std::begin(kPayload),
std::end(kPayload));
payload_listener_remote->OnPayloadReceived(
kRemoteEndpointId,
Payload::New(kPayloadId, PayloadContent::NewBytes(
BytesPayload::New(expected_payload))));
base::RunLoop payload_run_loop;
EXPECT_CALL(payload_listener, OnStatusUpdate(testing::_, testing::_))
.WillOnce([&payload_run_loop]() { payload_run_loop.Quit(); });
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
payload_run_loop.Run();
Payload* payload =
nearby_connections_manager_->GetIncomingPayload(kPayloadId);
ASSERT_TRUE(payload);
ASSERT_TRUE(payload->content->is_bytes());
EXPECT_EQ(expected_payload, payload->content->get_bytes()->bytes);
}
TEST_F(NearbyConnectionsManagerImplTest, IncomingFilePayload) {
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(connection_listener_remote, incoming_connection_listener);
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* connection = OnIncomingConnection(
connection_listener_remote, incoming_connection_listener,
payload_listener_remote);
EXPECT_TRUE(connection);
testing::NiceMock<MockPayloadStatusListener> payload_listener;
nearby_connections_manager_->RegisterPayloadStatusListener(
kPayloadId, payload_listener.GetWeakPtr());
const std::vector<uint8_t> expected_payload(std::begin(kPayload),
std::end(kPayload));
base::File file;
InitializeTemporaryFile(file);
payload_listener_remote->OnPayloadReceived(
kRemoteEndpointId,
Payload::New(kPayloadId,
PayloadContent::NewFile(FilePayload::New(std::move(file)))));
base::RunLoop payload_run_loop;
EXPECT_CALL(payload_listener, OnStatusUpdate(testing::_, testing::_))
.WillOnce([&payload_run_loop]() { payload_run_loop.Quit(); });
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
payload_run_loop.Run();
{
base::ScopedAllowBlockingForTesting allow_blocking;
Payload* payload =
nearby_connections_manager_->GetIncomingPayload(kPayloadId);
ASSERT_TRUE(payload);
ASSERT_TRUE(payload->content->is_file());
std::vector<uint8_t> payload_bytes(
payload->content->get_file()->file.GetLength());
EXPECT_TRUE(payload->content->get_file()->file.ReadAndCheck(
/*offset=*/0, base::make_span(payload_bytes)));
EXPECT_EQ(expected_payload, payload_bytes);
}
}
TEST_F(NearbyConnectionsManagerImplTest, ClearIncomingPayloads) {
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(connection_listener_remote, incoming_connection_listener);
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* connection = OnIncomingConnection(
connection_listener_remote, incoming_connection_listener,
payload_listener_remote);
EXPECT_TRUE(connection);
testing::NiceMock<MockPayloadStatusListener> payload_listener;
nearby_connections_manager_->RegisterPayloadStatusListener(
kPayloadId, payload_listener.GetWeakPtr());
base::File file;
InitializeTemporaryFile(file);
payload_listener_remote->OnPayloadReceived(
kRemoteEndpointId,
Payload::New(kPayloadId,
PayloadContent::NewFile(FilePayload::New(std::move(file)))));
base::RunLoop payload_run_loop;
EXPECT_CALL(payload_listener, OnStatusUpdate(testing::_, testing::_))
.WillOnce([&payload_run_loop]() { payload_run_loop.Quit(); });
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
payload_run_loop.Run();
nearby_connections_manager_->ClearIncomingPayloads();
EXPECT_FALSE(nearby_connections_manager_->GetIncomingPayload(kPayloadId));
}
TEST_F(NearbyConnectionsManagerImplTest, ClearIncomingPayloadWithId) {
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(connection_listener_remote, incoming_connection_listener);
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* connection = OnIncomingConnection(
connection_listener_remote, incoming_connection_listener,
payload_listener_remote);
EXPECT_TRUE(connection);
testing::NiceMock<MockPayloadStatusListener> payload_listener;
nearby_connections_manager_->RegisterPayloadStatusListener(
kPayloadId, payload_listener.GetWeakPtr());
nearby_connections_manager_->RegisterPayloadStatusListener(
kPayloadId2, payload_listener.GetWeakPtr());
base::File file_1, file_2;
InitializeTemporaryFile(file_1);
InitializeTemporaryFile(file_2);
payload_listener_remote->OnPayloadReceived(
kRemoteEndpointId,
Payload::New(kPayloadId, PayloadContent::NewFile(
FilePayload::New(std::move(file_1)))));
payload_listener_remote->OnPayloadReceived(
kRemoteEndpointId,
Payload::New(kPayloadId2, PayloadContent::NewFile(
FilePayload::New(std::move(file_2)))));
base::RunLoop payload_run_loop;
testing::InSequence seq;
EXPECT_CALL(payload_listener, OnStatusUpdate(testing::_, testing::_));
EXPECT_CALL(payload_listener, OnStatusUpdate(testing::_, testing::_))
.WillOnce([&payload_run_loop]() { payload_run_loop.Quit(); });
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
payload_listener_remote->OnPayloadTransferUpdate(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId2, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
payload_run_loop.Run();
nearby_connections_manager_->ClearIncomingPayloadWithId(kPayloadId);
EXPECT_FALSE(nearby_connections_manager_->GetIncomingPayload(kPayloadId));
EXPECT_TRUE(nearby_connections_manager_->GetIncomingPayload(kPayloadId2));
}
/******************************************************************************/
// Begin: NearbyConnectionsManagerImplTestMediums
/******************************************************************************/
using MediumsTestParam = std::tuple<NearbyConnectionsManager::PowerLevel,
NearbyConnectionsManager::DataUsage,
net::NetworkChangeNotifier::ConnectionType,
bool,
bool>;
class NearbyConnectionsManagerImplTestMediums
: public NearbyConnectionsManagerImplTest,
public testing::WithParamInterface<MediumsTestParam> {};
TEST_P(NearbyConnectionsManagerImplTestMediums, StartAdvertising_Options) {
const MediumsTestParam& param = GetParam();
NearbyConnectionsManager::PowerLevel power_level = std::get<0>(param);
NearbyConnectionsManager::DataUsage data_usage = std::get<1>(param);
net::NetworkChangeNotifier::ConnectionType connection_type =
std::get<2>(param);
bool is_webrtc_enabled = std::get<3>(GetParam());
bool is_ble_v2_enabled = std::get<4>(GetParam());
scoped_feature_list_.Reset();
scoped_feature_list_.InitWithFeatureStates(
{{features::kNearbySharingWebRtc, is_webrtc_enabled},
{features::kEnableNearbyBleV2, is_ble_v2_enabled}});
network_notifier_->SetConnectionType(connection_type);
network_notifier_->SetUseDefaultConnectionCostImplementation(true);
should_use_web_rtc_ =
is_webrtc_enabled &&
data_usage != NearbyConnectionsManager::DataUsage::kOffline &&
connection_type != net::NetworkChangeNotifier::CONNECTION_NONE &&
(data_usage != NearbyConnectionsManager::DataUsage::kWifiOnly ||
(net::NetworkChangeNotifier::GetConnectionCost() !=
net::NetworkChangeNotifier::CONNECTION_COST_METERED));
bool is_high_power =
power_level == NearbyConnectionsManager::PowerLevel::kHighPower;
bool use_ble = is_ble_v2_enabled || !is_high_power;
// TODO(crbug.com/1129069): Update when WiFi LAN is supported.
auto expected_mediums = MediumSelection::New(
/*bluetooth=*/is_high_power,
/*ble=*/use_ble,
/*web_rtc=*/should_use_web_rtc_,
/*wifi_lan=*/false,
/*wifi_direct=*/false);
base::RunLoop run_loop;
const std::vector<uint8_t> local_endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
NearbyConnectionsManager::ConnectionsCallback callback =
base::BindLambdaForTesting([&run_loop](Status status) {
EXPECT_EQ(Status::kSuccess, status);
run_loop.Quit();
});
EXPECT_CALL(nearby_connections_, StartAdvertising)
.WillOnce([&](const std::string& service_id,
const std::vector<uint8_t>& endpoint_info,
AdvertisingOptionsPtr options,
mojo::PendingRemote<ConnectionLifecycleListener> listener,
NearbyConnectionsMojom::StartAdvertisingCallback callback) {
EXPECT_EQ(is_high_power, options->auto_upgrade_bandwidth);
EXPECT_EQ(expected_mediums, options->allowed_mediums);
EXPECT_EQ(use_ble, options->enable_bluetooth_listening);
EXPECT_EQ(is_high_power && should_use_web_rtc_,
options->enable_webrtc_listening);
// BLE v2 expects this field to be null when in high power mode.
EXPECT_EQ(!is_ble_v2_enabled || !is_high_power,
options->fast_advertisement_service_uuid.has_value());
std::move(callback).Run(Status::kSuccess);
});
nearby_connections_manager_->StartAdvertising(
local_endpoint_info, &incoming_connection_listener, power_level,
data_usage, std::move(callback));
run_loop.Run();
}
INSTANTIATE_TEST_SUITE_P(
NearbyConnectionsManagerImplTestMediums,
NearbyConnectionsManagerImplTestMediums,
testing::Combine(
testing::Values(NearbyConnectionsManager::PowerLevel::kLowPower,
NearbyConnectionsManager::PowerLevel::kHighPower),
testing::Values(NearbyConnectionsManager::DataUsage::kWifiOnly,
NearbyConnectionsManager::DataUsage::kOffline,
NearbyConnectionsManager::DataUsage::kOnline),
testing::Values(net::NetworkChangeNotifier::CONNECTION_NONE,
net::NetworkChangeNotifier::CONNECTION_WIFI,
net::NetworkChangeNotifier::CONNECTION_3G),
testing::Bool(),
testing::Bool()));
/******************************************************************************/
// End: NearbyConnectionsManagerImplTestMediums
/******************************************************************************/
TEST_F(NearbyConnectionsManagerImplTest, StopAdvertising_BeforeStart) {
EXPECT_CALL(nearby_connections_, StopAdvertising).Times(0);
nearby_connections_manager_->StopAdvertising(base::BindOnce(
[](Status status) { EXPECT_EQ(status, Status::kSuccess); }));
}
TEST_F(NearbyConnectionsManagerImplTest, StopAdvertising) {
mojo::Remote<ConnectionLifecycleListener> listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(listener_remote, incoming_connection_listener);
base::RunLoop run_loop;
EXPECT_CALL(nearby_connections_, StopAdvertising)
.WillOnce([&](const std::string& service_id,
NearbyConnectionsMojom::StopAdvertisingCallback callback) {
EXPECT_EQ(kServiceId, service_id);
std::move(callback).Run(Status::kSuccess);
run_loop.Quit();
});
nearby_connections_manager_->StopAdvertising(base::BindOnce(
[](Status status) { EXPECT_EQ(status, Status::kSuccess); }));
run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, ShutdownAdvertising) {
mojo::Remote<ConnectionLifecycleListener> listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(listener_remote, incoming_connection_listener);
base::RunLoop run_loop;
EXPECT_CALL(nearby_connections_, StopAllEndpoints)
.WillOnce(
[&](const std::string& service_id,
NearbyConnectionsMojom::DisconnectFromEndpointCallback callback) {
EXPECT_EQ(kServiceId, service_id);
std::move(callback).Run(Status::kSuccess);
run_loop.Quit();
});
nearby_connections_manager_->Shutdown();
run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, ShutdownDiscoveryConnectionFails) {
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
base::RunLoop shutdown_run_loop;
EXPECT_CALL(nearby_connections_, StopAllEndpoints)
.WillOnce(
[&](const std::string& service_id,
NearbyConnectionsMojom::DisconnectFromEndpointCallback callback) {
EXPECT_EQ(kServiceId, service_id);
std::move(callback).Run(Status::kSuccess);
shutdown_run_loop.Quit();
});
nearby_connections_manager_->Shutdown();
shutdown_run_loop.Run();
// RequestConnection will fail.
const std::vector<uint8_t> local_endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
base::RunLoop connect_run_loop;
NearbyConnection* nearby_connection;
nearby_connections_manager_->Connect(
local_endpoint_info, kRemoteEndpointId,
/*bluetooth_mac_address=*/std::nullopt,
NearbyConnectionsManager::DataUsage::kOffline,
base::BindLambdaForTesting([&](NearbyConnection* connection) {
nearby_connection = connection;
connect_run_loop.Quit();
}));
connect_run_loop.Run();
EXPECT_FALSE(nearby_connection);
}
TEST_F(NearbyConnectionsManagerImplTest,
ShutdownBeforeStartDiscoveryOrAdvertising) {
EXPECT_CALL(nearby_connections_, StopAllEndpoints).Times(0);
nearby_connections_manager_->Shutdown();
}
TEST_F(NearbyConnectionsManagerImplTest,
UpgradeBandwidthAfterAdvertisingSucceeds) {
mojo::Remote<ConnectionLifecycleListener> listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(listener_remote, incoming_connection_listener);
// Upgrading bandwidth will succeed.
base::RunLoop run_loop;
EXPECT_CALL(nearby_connections_, InitiateBandwidthUpgrade)
.WillOnce([&](const std::string& service_id,
const std::string& endpoint_id,
NearbyConnectionsMojom::InitiateBandwidthUpgradeCallback
callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kRemoteEndpointId, endpoint_id);
std::move(callback).Run(Status::kSuccess);
run_loop.Quit();
});
nearby_connections_manager_->UpgradeBandwidth(kRemoteEndpointId);
run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest,
UpgradeBandwidthAfterDiscoverySucceeds) {
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* nearby_connection =
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
EXPECT_TRUE(nearby_connection);
// Upgrading bandwidth will succeed.
base::RunLoop run_loop;
EXPECT_CALL(nearby_connections_, InitiateBandwidthUpgrade)
.WillOnce([&](const std::string& service_id,
const std::string& endpoint_id,
NearbyConnectionsMojom::InitiateBandwidthUpgradeCallback
callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kRemoteEndpointId, endpoint_id);
std::move(callback).Run(Status::kSuccess);
run_loop.Quit();
});
nearby_connections_manager_->UpgradeBandwidth(kRemoteEndpointId);
run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest,
UpgradeBandwidthBeforeStartDiscoveryOrAdvertising) {
EXPECT_CALL(nearby_connections_, InitiateBandwidthUpgrade).Times(0);
nearby_connections_manager_->UpgradeBandwidth(kRemoteEndpointId);
}
TEST_F(NearbyConnectionsManagerImplTest,
DoNotUpgradeBandwidthIfWebRtcDisabled) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kNearbySharingWebRtc);
mojo::Remote<ConnectionLifecycleListener> listener_remote;
testing::NiceMock<MockIncomingConnectionListener>
incoming_connection_listener;
StartAdvertising(listener_remote, incoming_connection_listener);
// Bandwidth upgrade should not be attempted if WebRTC is disabled.
EXPECT_CALL(nearby_connections_, InitiateBandwidthUpgrade).Times(0);
nearby_connections_manager_->UpgradeBandwidth(kRemoteEndpointId);
}
// Regression test for b/303675257.
TEST_F(NearbyConnectionsManagerImplTest,
DisconnectListenerDeletesNearbyConnectionsManager) {
// StartDiscovery will succeed.
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// RequestConnection will succeed.
mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
mojo::Remote<PayloadListener> payload_listener_remote;
NearbyConnection* nearby_connection =
Connect(connection_listener_remote, payload_listener_remote,
ConnectionResponse::kAccepted);
ASSERT_TRUE(nearby_connection);
EXPECT_NE(nullptr, nearby_connections_manager_);
// Close should invoke disconnection callback.
nearby_connection->SetDisconnectionListener(base::BindLambdaForTesting(
[&]() { nearby_connections_manager_.reset(); }));
nearby_connection->Close();
EXPECT_EQ(nullptr, nearby_connections_manager_);
}
TEST_F(NearbyConnectionsManagerImplTest, RequestConnectionV3Initiated) {
nearby::presence::PresenceDevice presence_device = CreatePresenceDevice();
base::RunLoop request_connection_run_loop;
EXPECT_CALL(nearby_connections_, RequestConnectionV3)
.WillOnce(
[&](const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
ConnectionOptionsPtr options,
mojo::PendingRemote<ConnectionListenerV3> listener,
NearbyConnectionsMojom::RequestConnectionV3Callback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(remote_device->endpoint_id, kRemoteEndpointId);
std::move(callback).Run(Status::kSuccess);
request_connection_run_loop.Quit();
});
nearby_connections_manager_->ConnectV3(
presence_device, NearbyConnectionsManager::DataUsage::kOffline,
base::DoNothing());
request_connection_run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, OnConnectionTimedOutV3) {
mojo::Remote<ConnectionListenerV3> connection_listener_v3_remote;
nearby::presence::PresenceDevice presence_device = CreatePresenceDevice();
EXPECT_CALL(nearby_connections_, RequestConnectionV3)
.WillOnce(
[&](const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
ConnectionOptionsPtr options,
mojo::PendingRemote<ConnectionListenerV3> listener,
NearbyConnectionsMojom::RequestConnectionV3Callback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(remote_device->endpoint_id, kRemoteEndpointId);
connection_listener_v3_remote.Bind(std::move(listener));
std::move(callback).Run(Status::kSuccess);
});
EXPECT_CALL(nearby_connections_, DisconnectFromDeviceV3)
.WillOnce(
[&](const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
NearbyConnectionsMojom::DisconnectFromDeviceV3Callback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(remote_device->endpoint_id, kRemoteEndpointId);
std::move(callback).Run(Status::kSuccess);
});
base::RunLoop on_connection_timed_out_run_loop;
NearbyConnection* nearby_connection = nullptr;
nearby_connections_manager_->ConnectV3(
presence_device, NearbyConnectionsManager::DataUsage::kOffline,
base::BindLambdaForTesting([&](NearbyConnection* connection) {
nearby_connection = connection;
on_connection_timed_out_run_loop.Quit();
}));
task_environment_.FastForwardBy(kInitiateNearbyConnectionTimeout);
on_connection_timed_out_run_loop.Run();
EXPECT_FALSE(nearby_connection);
histogram_tester()->ExpectTimeBucketCount(
"Nearby.Connections.V3.ConnectionResult.Success.Latency",
kConnectV3ToSuccessfulConnectionLatency, 0);
}
TEST_F(NearbyConnectionsManagerImplTest, RequestConnectionV3Accept) {
mojo::Remote<ConnectionListenerV3> connection_listener_v3_remote;
mojo::Remote<PayloadListenerV3> payload_listener_v3_remote;
nearby::presence::PresenceDevice presence_device = CreatePresenceDevice();
ConnectV3(presence_device, connection_listener_v3_remote,
payload_listener_v3_remote,
nearby::connections::mojom::InitialConnectionInfoV3::New(
kAuthenticationToken, kRawAuthenticationToken,
/*is_incoming_connection=*/false,
nearby::connections::mojom::AuthenticationStatus::kSuccess),
/*on_connection_result_status=*/Status::kSuccess);
histogram_tester()->ExpectBucketCount(
"Nearby.Connections.V3.Connection.Result", Status::kSuccess, 1);
histogram_tester()->ExpectTotalCount(
"Nearby.Connections.V3.Connection.Result", 1);
histogram_tester()->ExpectTimeBucketCount(
"Nearby.Connections.V3.ConnectionResult.Success.Latency",
kConnectV3ToSuccessfulConnectionLatency, 1);
}
TEST_F(NearbyConnectionsManagerImplTest, RequestConnectionV3Reject) {
mojo::Remote<ConnectionListenerV3> connection_listener_v3_remote;
mojo::Remote<PayloadListenerV3> payload_listener_v3_remote;
nearby::presence::PresenceDevice presence_device = CreatePresenceDevice();
ConnectV3(presence_device, connection_listener_v3_remote,
payload_listener_v3_remote,
nearby::connections::mojom::InitialConnectionInfoV3::New(
kAuthenticationToken, kRawAuthenticationToken,
/*is_incoming_connection=*/false,
nearby::connections::mojom::AuthenticationStatus::kFailure),
/*on_connection_result_status=*/Status::kSuccess);
}
TEST_F(NearbyConnectionsManagerImplTest, OnConnectionResultV3Rejected) {
mojo::Remote<ConnectionListenerV3> connection_listener_v3_remote;
mojo::Remote<PayloadListenerV3> payload_listener_v3_remote;
nearby::presence::PresenceDevice presence_device = CreatePresenceDevice();
ConnectV3(presence_device, connection_listener_v3_remote,
payload_listener_v3_remote,
nearby::connections::mojom::InitialConnectionInfoV3::New(
kAuthenticationToken, kRawAuthenticationToken,
/*is_incoming_connection=*/false,
nearby::connections::mojom::AuthenticationStatus::kFailure),
/*on_connection_result_status=*/Status::kError);
histogram_tester()->ExpectBucketCount(
"Nearby.Connections.V3.Connection.Result", Status::kError, 1);
histogram_tester()->ExpectTotalCount(
"Nearby.Connections.V3.Connection.Result", 1);
histogram_tester()->ExpectTimeBucketCount(
"Nearby.Connections.V3.ConnectionResult.Success.Latency",
kConnectV3ToSuccessfulConnectionLatency, 0);
}
TEST_F(NearbyConnectionsManagerImplTest, DisconnectV3) {
mojo::Remote<ConnectionListenerV3> connection_listener_v3_remote;
mojo::Remote<PayloadListenerV3> payload_listener_v3_remote;
nearby::presence::PresenceDevice presence_device = CreatePresenceDevice();
ConnectV3(presence_device, connection_listener_v3_remote,
payload_listener_v3_remote,
nearby::connections::mojom::InitialConnectionInfoV3::New(
kAuthenticationToken, kRawAuthenticationToken,
/*is_incoming_connection=*/false,
nearby::connections::mojom::AuthenticationStatus::kSuccess),
/*on_connection_result_status=*/Status::kSuccess);
histogram_tester()->ExpectTimeBucketCount(
"Nearby.Connections.V3.ConnectionResult.Success.Latency",
kConnectV3ToSuccessfulConnectionLatency, 1);
base::RunLoop disconnect_run_loop;
EXPECT_CALL(nearby_connections_, DisconnectFromDeviceV3)
.WillOnce(
[&](const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
NearbyConnectionsMojom::DisconnectFromDeviceV3Callback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(remote_device->endpoint_id, kRemoteEndpointId);
std::move(callback).Run(Status::kSuccess);
disconnect_run_loop.Quit();
});
nearby_connections_manager_->DisconnectV3(presence_device);
disconnect_run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, OnConnectionRequestedV3) {
nearby::presence::PresenceDevice presence_device = CreatePresenceDevice();
base::RunLoop request_connection_run_loop;
EXPECT_CALL(nearby_connections_, RequestConnectionV3)
.WillOnce(
[&](const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
ConnectionOptionsPtr options,
mojo::PendingRemote<ConnectionListenerV3> listener,
NearbyConnectionsMojom::RequestConnectionV3Callback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(remote_device->endpoint_id, kRemoteEndpointId);
std::move(callback).Run(Status::kError);
request_connection_run_loop.Quit();
});
nearby_connections_manager_->ConnectV3(
presence_device, NearbyConnectionsManager::DataUsage::kOffline,
base::DoNothing());
request_connection_run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, OnBandwidthChangedV3) {
mojo::Remote<ConnectionListenerV3> connection_listener_v3_remote;
mojo::Remote<PayloadListenerV3> payload_listener_v3_remote;
nearby::presence::PresenceDevice presence_device = CreatePresenceDevice();
base::RunLoop request_connection_run_loop;
EXPECT_CALL(nearby_connections_, RequestConnectionV3)
.WillOnce(
[&](const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
ConnectionOptionsPtr options,
mojo::PendingRemote<ConnectionListenerV3> listener,
NearbyConnectionsMojom::RequestConnectionV3Callback callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(remote_device->endpoint_id, kRemoteEndpointId);
connection_listener_v3_remote.Bind(std::move(listener));
std::move(callback).Run(Status::kSuccess);
request_connection_run_loop.Quit();
});
ash::nearby::presence::mojom::PresenceDevicePtr presence_device_mojom =
ash::nearby::presence::BuildPresenceMojomDevice(presence_device);
nearby_connections_manager_->ConnectV3(
presence_device, NearbyConnectionsManager::DataUsage::kOffline,
base::DoNothing());
request_connection_run_loop.Run();
testing::NiceMock<MockBandwidthUpgradeListener> bandwidth_listener;
nearby_connections_manager_->RegisterBandwidthUpgradeListener(
bandwidth_listener.GetWeakPtr());
base::RunLoop bandwidth_run_loop;
EXPECT_CALL(bandwidth_listener,
OnBandwidthUpgradeV3(
testing::An<nearby::presence::PresenceDevice>(), testing::_))
.WillOnce([&](nearby::presence::PresenceDevice remote_device,
const Medium medium) {
EXPECT_EQ(remote_device.GetEndpointId(), kRemoteEndpointId);
EXPECT_EQ(medium, Medium::kWebRtc);
bandwidth_run_loop.Quit();
});
// `OnBandwidthChangedV3()` needs to be called twice. The first one is always
// called for the first Medium connected to and does not propagate to the
// listener. The second call is when the bandwidth truly changed and is
// propagated through to the listener.
connection_listener_v3_remote->OnBandwidthChangedV3(
presence_device.GetEndpointId(),
nearby::connections::mojom::BandwidthInfo::New(BandwidthQuality::kMedium,
Medium::kBluetooth));
histogram_tester()->ExpectTotalCount(
"Nearby.Connections.V3.Medium.ChangedToMedium", 0);
connection_listener_v3_remote->OnBandwidthChangedV3(
presence_device.GetEndpointId(),
nearby::connections::mojom::BandwidthInfo::New(BandwidthQuality::kHigh,
Medium::kWebRtc));
bandwidth_run_loop.Run();
histogram_tester()->ExpectBucketCount(
"Nearby.Connections.V3.Medium.ChangedToMedium", Medium::kWebRtc, 1);
}
TEST_F(NearbyConnectionsManagerImplTest, PayloadListenerV3RemoteCallbacks) {
mojo::Remote<ConnectionListenerV3> connection_listener_v3_remote;
mojo::Remote<PayloadListenerV3> payload_listener_v3_remote;
const std::vector<uint8_t> byte_payload(std::begin(kBytePayload),
std::end(kBytePayload));
nearby::presence::PresenceDevice presence_device = CreatePresenceDevice();
ConnectV3(presence_device, connection_listener_v3_remote,
payload_listener_v3_remote,
nearby::connections::mojom::InitialConnectionInfoV3::New(
kAuthenticationToken, kRawAuthenticationToken,
/*is_incoming_connection=*/false,
nearby::connections::mojom::AuthenticationStatus::kSuccess),
/*on_connection_result_status=*/Status::kSuccess);
testing::NiceMock<MockPayloadStatusListener> payload_listener;
nearby_connections_manager_->RegisterPayloadStatusListener(
kPayloadId, payload_listener.GetWeakPtr());
base::RunLoop payload_transfer_update_run_loop;
EXPECT_CALL(payload_listener, OnStatusUpdate(testing::_, testing::_))
.WillOnce([&](MockPayloadStatusListener::PayloadTransferUpdatePtr update,
std::optional<Medium> upgraded_medium) {
EXPECT_EQ(update->payload_id, kPayloadId);
EXPECT_EQ(update->status, PayloadStatus::kSuccess);
EXPECT_EQ(update->total_bytes, kTotalSize);
EXPECT_EQ(update->bytes_transferred, kTotalSize);
payload_transfer_update_run_loop.Quit();
});
payload_listener_v3_remote->OnPayloadReceivedV3(
kRemoteEndpointId,
Payload::New(kPayloadId,
PayloadContent::NewBytes(BytesPayload::New(byte_payload))));
payload_listener_v3_remote->OnPayloadTransferUpdateV3(
kRemoteEndpointId,
PayloadTransferUpdate::New(kPayloadId, PayloadStatus::kSuccess,
kTotalSize, /*bytes_transferred=*/kTotalSize));
payload_transfer_update_run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest, InjectBluetoothEndpoint_Success) {
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
const std::vector<uint8_t> endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
base::RunLoop run_loop;
base::OnceCallback<void(nearby::connections::mojom::Status)> callback =
base::BindLambdaForTesting(
[&run_loop](nearby::connections::mojom::Status status) {
EXPECT_EQ(status, nearby::connections::mojom::Status::kSuccess);
run_loop.Quit();
});
EXPECT_CALL(
nearby_connections_,
InjectBluetoothEndpoint(testing::Eq(kServiceId), testing::Eq(kEndpointId),
testing::Eq(endpoint_info),
testing::Eq(kBluetoothMacAddress), testing::_))
.WillOnce([&](const std::string& service_id,
const std::string& endpoint_id,
const std::vector<uint8_t> endpoint_info,
const std::vector<uint8_t> bluetooth_mac_address,
base::OnceCallback<void(nearby::connections::mojom::Status)>
callback) {
EXPECT_EQ(kServiceId, service_id);
EXPECT_EQ(kEndpointId, endpoint_id);
EXPECT_EQ(std::vector<uint8_t>(std::begin(kEndpointInfo),
std::end(kEndpointInfo)),
endpoint_info);
EXPECT_EQ(kBluetoothMacAddress, bluetooth_mac_address);
std::move(callback).Run(nearby::connections::mojom::Status::kSuccess);
});
nearby_connections_manager_->InjectBluetoothEndpoint(
kServiceId, kEndpointId, endpoint_info, kBluetoothMacAddress,
std::move(callback));
run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest,
InjectBluetoothEndpoint_Error_EndpointIdShort) {
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
const std::vector<uint8_t> endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
base::RunLoop run_loop;
base::OnceCallback<void(nearby::connections::mojom::Status)> callback =
base::BindLambdaForTesting(
[&run_loop](nearby::connections::mojom::Status status) {
EXPECT_EQ(status, nearby::connections::mojom::Status::kError);
run_loop.Quit();
});
// Provide 2-byte endpoint id instead of required 4 byte endpoint id.
const std::string short_endpoint_id = "BS";
nearby_connections_manager_->InjectBluetoothEndpoint(
kServiceId, short_endpoint_id, endpoint_info, kBluetoothMacAddress,
std::move(callback));
run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest,
InjectBluetoothEndpoint_Error_EndpointInfoSize0) {
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// Provide endpoint info size 0 to expect an error.
const std::vector<uint8_t> endpoint_info;
base::RunLoop run_loop;
base::OnceCallback<void(nearby::connections::mojom::Status)> callback =
base::BindLambdaForTesting(
[&run_loop](nearby::connections::mojom::Status status) {
EXPECT_EQ(status, nearby::connections::mojom::Status::kError);
run_loop.Quit();
});
nearby_connections_manager_->InjectBluetoothEndpoint(
kServiceId, kEndpointId, endpoint_info, kBluetoothMacAddress,
std::move(callback));
run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest,
InjectBluetoothEndpoint_Error_EndpointInfoSize131) {
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
// Provide endpoint info size >130 to expect an error.
const std::vector<uint8_t> endpoint_info(131, 0);
base::RunLoop run_loop;
base::OnceCallback<void(nearby::connections::mojom::Status)> callback =
base::BindLambdaForTesting(
[&run_loop](nearby::connections::mojom::Status status) {
EXPECT_EQ(status, nearby::connections::mojom::Status::kError);
run_loop.Quit();
});
nearby_connections_manager_->InjectBluetoothEndpoint(
kServiceId, kEndpointId, endpoint_info, kBluetoothMacAddress,
std::move(callback));
run_loop.Run();
}
TEST_F(NearbyConnectionsManagerImplTest,
InjectBluetoothEndpoint_Error_BluetoothMacAddressSizeNot6) {
mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
testing::NiceMock<MockDiscoveryListener> discovery_listener;
StartDiscovery(discovery_listener_remote, discovery_listener);
const std::vector<uint8_t> endpoint_info(std::begin(kEndpointInfo),
std::end(kEndpointInfo));
base::RunLoop run_loop;
base::OnceCallback<void(nearby::connections::mojom::Status)> callback =
base::BindLambdaForTesting(
[&run_loop](nearby::connections::mojom::Status status) {
EXPECT_EQ(status, nearby::connections::mojom::Status::kError);
run_loop.Quit();
});
// Provide a bluetooth mac address with size !=6 to expect an error.
const std::vector<uint8_t> err_bluetooth_mac_address(7, 0);
nearby_connections_manager_->InjectBluetoothEndpoint(
kServiceId, kEndpointId, endpoint_info, err_bluetooth_mac_address,
std::move(callback));
run_loop.Run();
}