blob: 3f87c7e835db1f0eed9afd9886b466be1368e26b [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sharing/sharing_message_sender.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/test/mock_callback.h"
#include "chrome/browser/sharing/fake_device_info.h"
#include "chrome/browser/sharing/proto/sharing_message.pb.h"
#include "chrome/browser/sharing/sharing_fcm_sender.h"
#include "chrome/browser/sharing/sharing_metrics.h"
#include "chrome/browser/sharing/sharing_sync_preference.h"
#include "chrome/browser/sharing/sharing_utils.h"
#include "components/send_tab_to_self/target_device_info.h"
#include "components/sync/protocol/device_info_specifics.pb.h"
#include "components/sync_device_info/device_info.h"
#include "components/sync_device_info/fake_device_info_sync_service.h"
#include "components/sync_device_info/fake_local_device_info_provider.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Test constants.
const char kReceiverGUID[] = "kReceiverGUID";
const char kReceiverDeviceName[] = "receiver_device";
const char kP256dh[] = "p256dh";
const char kAuthSecret[] = "auth_secret";
const char kFCMToken[] = "vapid_fcm_token";
const char kAuthorizedEntity[] = "authorized_entity";
const char kSenderVapidFcmToken[] = "sender_vapid_fcm_token";
const char kSenderVapidP256dh[] = "sender_vapid_p256dh";
const char kSenderVapidAuthSecret[] = "sender_vapid_auth_secret";
const char kSenderSenderIdFcmToken[] = "sender_sender_id_fcm_token";
const char kSenderSenderIdP256dh[] = "sender_sender_id_p256dh";
const char kSenderSenderIdAuthSecret[] = "sender_sender_id_auth_secret";
const char kSenderMessageID[] = "sender_message_id";
constexpr base::TimeDelta kTimeToLive = base::Seconds(10);
namespace {
class MockSharingFCMSender : public SharingFCMSender {
public:
MockSharingFCMSender(
SharingSyncPreference* sync_preference,
syncer::DeviceInfoTracker* device_info_tracker,
syncer::LocalDeviceInfoProvider* local_device_info_provider)
: SharingFCMSender(
/*web_push_sender=*/nullptr,
/*sharing_message_bridge=*/nullptr,
sync_preference,
/*vapid_key_manager=*/nullptr,
/*gcm_driver=*/nullptr,
device_info_tracker,
local_device_info_provider,
/*sync_service=*/nullptr) {}
MockSharingFCMSender(const MockSharingFCMSender&) = delete;
MockSharingFCMSender& operator=(const MockSharingFCMSender&) = delete;
~MockSharingFCMSender() override = default;
MOCK_METHOD4(SendMessageToFcmTarget,
void(const chrome_browser_sharing::FCMChannelConfiguration&
fcm_configuration,
base::TimeDelta time_to_live,
SharingMessage message,
SendMessageCallback callback));
};
class MockSendMessageDelegate
: public SharingMessageSender::SendMessageDelegate {
public:
MockSendMessageDelegate() = default;
MockSendMessageDelegate(const MockSendMessageDelegate&) = delete;
MockSendMessageDelegate& operator=(const MockSendMessageDelegate&) = delete;
~MockSendMessageDelegate() override = default;
MOCK_METHOD4(DoSendMessageToDevice,
void(const SharingTargetDeviceInfo& device,
base::TimeDelta time_to_live,
chrome_browser_sharing::SharingMessage message,
SendMessageCallback callback));
};
syncer::DeviceInfo::SharingInfo CreateLocalSharingInfo() {
return syncer::DeviceInfo::SharingInfo(
{kSenderVapidFcmToken, kSenderVapidP256dh, kSenderVapidAuthSecret},
{kSenderSenderIdFcmToken, kSenderSenderIdP256dh,
kSenderSenderIdAuthSecret},
std::set<sync_pb::SharingSpecificFields::EnabledFeatures>());
}
syncer::DeviceInfo::SharingInfo CreateSharingInfo() {
return syncer::DeviceInfo::SharingInfo(
{kFCMToken, kP256dh, kAuthSecret},
{"sender_id_fcm_token", "sender_id_p256dh", "sender_id_auth_secret"},
std::set<sync_pb::SharingSpecificFields::EnabledFeatures>{
sync_pb::SharingSpecificFields::CLICK_TO_CALL_V2});
}
SharingTargetDeviceInfo CreateFakeSharingTargetDeviceInfo(
const std::string& guid,
const std::string& client_name) {
return SharingTargetDeviceInfo(guid, client_name,
SharingDevicePlatform::kUnknown,
/*pulse_interval=*/base::TimeDelta(),
syncer::DeviceInfo::FormFactor::kUnknown,
/*last_updated_timestamp=*/base::Time());
}
} // namespace
class SharingMessageSenderTest : public testing::Test {
public:
SharingMessageSenderTest() {
SharingSyncPreference::RegisterProfilePrefs(prefs_.registry());
auto mock_sharing_fcm_sender = std::make_unique<MockSharingFCMSender>(
&sharing_sync_preference_,
fake_device_info_sync_service_.GetDeviceInfoTracker(),
fake_device_info_sync_service_.GetLocalDeviceInfoProvider());
mock_sharing_fcm_sender_ = mock_sharing_fcm_sender.get();
sharing_message_sender_.RegisterSendDelegate(
SharingMessageSender::DelegateType::kFCM,
std::move(mock_sharing_fcm_sender));
sharing_sync_preference_.SetFCMRegistration(
SharingSyncPreference::FCMRegistration(kAuthorizedEntity,
base::Time::Now()));
fake_device_info_sync_service_.GetLocalDeviceInfoProvider()
->GetMutableDeviceInfo()
->set_sharing_info(CreateLocalSharingInfo());
}
SharingMessageSenderTest(const SharingMessageSenderTest&) = delete;
SharingMessageSenderTest& operator=(const SharingMessageSenderTest&) = delete;
~SharingMessageSenderTest() override = default;
SharingTargetDeviceInfo SetupReceiverDevice() {
fake_device_info_sync_service_.GetDeviceInfoTracker()->Add(
CreateFakeDeviceInfo(kReceiverGUID, kReceiverDeviceName,
CreateSharingInfo()));
return CreateFakeSharingTargetDeviceInfo(kReceiverGUID,
kReceiverDeviceName);
}
protected:
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
sync_preferences::TestingPrefServiceSyncable prefs_;
syncer::FakeDeviceInfoSyncService fake_device_info_sync_service_;
SharingSyncPreference sharing_sync_preference_{
&prefs_, &fake_device_info_sync_service_};
SharingMessageSender sharing_message_sender_{
fake_device_info_sync_service_.GetLocalDeviceInfoProvider()};
raw_ptr<MockSharingFCMSender> mock_sharing_fcm_sender_;
};
MATCHER_P(ProtoEquals, message, "") {
if (!arg)
return false;
std::string expected_serialized, actual_serialized;
message.SerializeToString(&expected_serialized);
arg->SerializeToString(&actual_serialized);
return expected_serialized == actual_serialized;
}
} // namespace
TEST_F(SharingMessageSenderTest, MessageSent_AckTimedout) {
SharingTargetDeviceInfo device_info = SetupReceiverDevice();
base::MockCallback<SharingMessageSender::ResponseCallback> mock_callback;
EXPECT_CALL(mock_callback,
Run(testing::Eq(SharingSendMessageResult::kAckTimeout),
testing::Eq(nullptr)));
auto simulate_timeout =
[&](const chrome_browser_sharing::FCMChannelConfiguration&
fcm_configuration,
base::TimeDelta time_to_live,
chrome_browser_sharing::SharingMessage message,
SharingFCMSender::SendMessageCallback callback) {
// FCM message sent successfully.
std::move(callback).Run(SharingSendMessageResult::kSuccessful,
kSenderMessageID, SharingChannelType::kUnknown);
task_environment_.FastForwardBy(kTimeToLive);
// Callback already run with result timeout, ack received for same
// message id is ignored.
sharing_message_sender_.OnAckReceived(kSenderMessageID,
/*response=*/nullptr);
};
EXPECT_CALL(
*mock_sharing_fcm_sender_,
SendMessageToFcmTarget(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Invoke(simulate_timeout));
sharing_message_sender_.SendMessageToDevice(
device_info, kTimeToLive, chrome_browser_sharing::SharingMessage(),
SharingMessageSender::DelegateType::kFCM, mock_callback.Get());
}
TEST_F(SharingMessageSenderTest, SendMessageToDevice_InternalError) {
SharingTargetDeviceInfo device_info = SetupReceiverDevice();
base::MockCallback<SharingMessageSender::ResponseCallback> mock_callback;
EXPECT_CALL(mock_callback,
Run(testing::Eq(SharingSendMessageResult::kInternalError),
testing::Eq(nullptr)));
auto simulate_internal_error =
[&](const chrome_browser_sharing::FCMChannelConfiguration&
fcm_configuration,
base::TimeDelta time_to_live,
chrome_browser_sharing::SharingMessage message,
SharingFCMSender::SendMessageCallback callback) {
// FCM message not sent successfully.
std::move(callback).Run(SharingSendMessageResult::kInternalError,
std::nullopt, SharingChannelType::kUnknown);
// Callback already run with result timeout, ack received for same
// message id is ignored.
sharing_message_sender_.OnAckReceived(kSenderMessageID,
/*response=*/nullptr);
};
EXPECT_CALL(
*mock_sharing_fcm_sender_,
SendMessageToFcmTarget(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Invoke(simulate_internal_error));
sharing_message_sender_.SendMessageToDevice(
device_info, kTimeToLive, chrome_browser_sharing::SharingMessage(),
SharingMessageSender::DelegateType::kFCM, mock_callback.Get());
}
TEST_F(SharingMessageSenderTest, MessageSent_AckReceived) {
SharingTargetDeviceInfo device_info = SetupReceiverDevice();
chrome_browser_sharing::SharingMessage sent_message;
sent_message.mutable_click_to_call_message()->set_phone_number("999999");
chrome_browser_sharing::ResponseMessage expected_response_message;
base::MockCallback<SharingMessageSender::ResponseCallback> mock_callback;
EXPECT_CALL(mock_callback,
Run(testing::Eq(SharingSendMessageResult::kSuccessful),
ProtoEquals(expected_response_message)));
auto simulate_expected_ack_message_received =
[&](const chrome_browser_sharing::FCMChannelConfiguration&
fcm_configuration,
base::TimeDelta time_to_live,
chrome_browser_sharing::SharingMessage message,
SharingFCMSender::SendMessageCallback callback) {
// FCM message sent successfully.
std::move(callback).Run(SharingSendMessageResult::kSuccessful,
kSenderMessageID, SharingChannelType::kUnknown);
// Check sender info details.
const syncer::DeviceInfo* local_device =
fake_device_info_sync_service_.GetLocalDeviceInfoProvider()
->GetLocalDeviceInfo();
ASSERT_EQ(local_device->guid(), message.sender_guid());
ASSERT_EQ(
send_tab_to_self::GetSharingDeviceNames(local_device).full_name,
message.sender_device_name());
ASSERT_TRUE(local_device->sharing_info().has_value());
auto& fcm_ack_configuration = message.fcm_channel_configuration();
ASSERT_EQ(kSenderVapidFcmToken,
fcm_ack_configuration.vapid_fcm_token());
ASSERT_EQ(kSenderVapidP256dh, fcm_ack_configuration.vapid_p256dh());
ASSERT_EQ(kSenderVapidAuthSecret,
fcm_ack_configuration.vapid_auth_secret());
ASSERT_EQ(kSenderSenderIdFcmToken,
fcm_ack_configuration.sender_id_fcm_token());
ASSERT_EQ(kSenderSenderIdP256dh,
fcm_ack_configuration.sender_id_p256dh());
ASSERT_EQ(kSenderSenderIdAuthSecret,
fcm_ack_configuration.sender_id_auth_secret());
// Simulate ack message received.
std::unique_ptr<chrome_browser_sharing::ResponseMessage>
response_message =
std::make_unique<chrome_browser_sharing::ResponseMessage>();
response_message->CopyFrom(expected_response_message);
sharing_message_sender_.OnAckReceived(kSenderMessageID,
std::move(response_message));
};
EXPECT_CALL(
*mock_sharing_fcm_sender_,
SendMessageToFcmTarget(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Invoke(simulate_expected_ack_message_received));
sharing_message_sender_.SendMessageToDevice(
device_info, kTimeToLive, std::move(sent_message),
SharingMessageSender::DelegateType::kFCM, mock_callback.Get());
}
TEST_F(SharingMessageSenderTest, MessageSent_AckReceivedBeforeMessageId) {
SharingTargetDeviceInfo device_info = SetupReceiverDevice();
chrome_browser_sharing::SharingMessage sent_message;
sent_message.mutable_click_to_call_message()->set_phone_number("999999");
chrome_browser_sharing::ResponseMessage expected_response_message;
base::MockCallback<SharingMessageSender::ResponseCallback> mock_callback;
EXPECT_CALL(mock_callback,
Run(testing::Eq(SharingSendMessageResult::kSuccessful),
ProtoEquals(expected_response_message)));
auto simulate_expected_ack_message_received =
[&](const chrome_browser_sharing::FCMChannelConfiguration&
fcm_configuration,
base::TimeDelta time_to_live,
chrome_browser_sharing::SharingMessage message,
SharingFCMSender::SendMessageCallback callback) {
// Simulate ack message received.
std::unique_ptr<chrome_browser_sharing::ResponseMessage>
response_message =
std::make_unique<chrome_browser_sharing::ResponseMessage>();
response_message->CopyFrom(expected_response_message);
sharing_message_sender_.OnAckReceived(kSenderMessageID,
std::move(response_message));
// Call FCM send success after receiving the ACK.
std::move(callback).Run(SharingSendMessageResult::kSuccessful,
kSenderMessageID,
SharingChannelType::kFcmVapid);
};
EXPECT_CALL(
*mock_sharing_fcm_sender_,
SendMessageToFcmTarget(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Invoke(simulate_expected_ack_message_received));
sharing_message_sender_.SendMessageToDevice(
device_info, kTimeToLive, std::move(sent_message),
SharingMessageSender::DelegateType::kFCM, mock_callback.Get());
}
TEST_F(SharingMessageSenderTest, NonExistingDelegate) {
SharingMessageSender sharing_message_sender{
fake_device_info_sync_service_.GetLocalDeviceInfoProvider()};
SharingTargetDeviceInfo device_info =
CreateFakeSharingTargetDeviceInfo(kReceiverGUID, kReceiverDeviceName);
base::MockCallback<SharingMessageSender::ResponseCallback> mock_callback;
EXPECT_CALL(mock_callback,
Run(testing::Eq(SharingSendMessageResult::kInternalError),
testing::Eq(nullptr)));
sharing_message_sender.SendMessageToDevice(
device_info, kTimeToLive, chrome_browser_sharing::SharingMessage(),
SharingMessageSender::DelegateType::kFCM, mock_callback.Get());
}
TEST_F(SharingMessageSenderTest, RequestCancelled) {
SharingTargetDeviceInfo device_info = SetupReceiverDevice();
chrome_browser_sharing::SharingMessage sent_message;
sent_message.mutable_sms_fetch_request()->add_origins("https://a.com");
chrome_browser_sharing::ResponseMessage expected_response_message;
base::MockCallback<SharingMessageSender::ResponseCallback> mock_callback;
EXPECT_CALL(mock_callback,
Run(testing::Eq(SharingSendMessageResult::kCancelled),
testing::Eq(nullptr)));
EXPECT_CALL(
*mock_sharing_fcm_sender_,
SendMessageToFcmTarget(testing::_, testing::_, testing::_, testing::_));
base::OnceClosure cancel_callback =
sharing_message_sender_.SendMessageToDevice(
device_info, kTimeToLive, std::move(sent_message),
SharingMessageSender::DelegateType::kFCM, mock_callback.Get());
ASSERT_FALSE(cancel_callback.is_null());
std::move(cancel_callback).Run();
}