| // 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/sms/sms_remote_fetcher.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/browser/sharing/sharing_service_factory.h" |
| #include "chrome/browser/sharing/sms/sms_remote_fetcher_metrics.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/sharing_message/mock_sharing_service.h" |
| #include "components/sharing_message/proto/sharing_message.pb.h" |
| #include "components/sharing_message/sharing_constants.h" |
| #include "components/sharing_message/sharing_service.h" |
| #include "components/sharing_message/sharing_target_device_info.h" |
| #include "content/public/browser/sms_fetcher.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/test_web_contents_factory.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| using base::BindLambdaForTesting; |
| using components_sharing_message::ResponseMessage; |
| using components_sharing_message::SharingMessage; |
| using ::testing::_; |
| using ::testing::ByMove; |
| using ::testing::Return; |
| using ::testing::SaveArg; |
| |
| MockSharingService* CreateSharingService(content::BrowserContext* context) { |
| return static_cast<MockSharingService*>( |
| SharingServiceFactory::GetInstance()->SetTestingFactoryAndUse( |
| context, base::BindRepeating([](content::BrowserContext* context) { |
| return static_cast<std::unique_ptr<KeyedService>>( |
| std::make_unique<MockSharingService>()); |
| }))); |
| } |
| |
| url::Origin GetOriginForURL(const std::string& url) { |
| return url::Origin::Create(GURL(url)); |
| } |
| |
| 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()); |
| } |
| |
| TEST(SmsRemoteFetcherTest, NoDevicesAvailable) { |
| content::BrowserTaskEnvironment task_environment; |
| base::HistogramTester histogram_tester; |
| TestingProfile profile; |
| content::WebContents::CreateParams create_params(&profile); |
| auto web_contents = content::WebContents::Create(create_params); |
| |
| MockSharingService* service = CreateSharingService(&profile); |
| |
| std::vector<SharingTargetDeviceInfo> devices; |
| EXPECT_CALL(*service, GetDeviceCandidates(_)) |
| .WillOnce(Return(ByMove(std::move(devices)))); |
| |
| base::RunLoop loop; |
| |
| FetchRemoteSms( |
| web_contents.get(), std::vector<url::Origin>{GetOriginForURL("a.com")}, |
| BindLambdaForTesting( |
| [&loop](std::optional<std::vector<url::Origin>>, |
| std::optional<std::string> result, |
| std::optional<content::SmsFetchFailureType> failure_type) { |
| ASSERT_FALSE(result); |
| ASSERT_EQ(failure_type, |
| content::SmsFetchFailureType::kCrossDeviceFailure); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| histogram_tester.ExpectUniqueSample( |
| "Blink.Sms.Receive.CrossDeviceFailure", |
| static_cast<int>(WebOTPCrossDeviceFailure::kNoRemoteDevice), 1); |
| } |
| |
| TEST(SmsRemoteFetcherTest, OneDevice) { |
| content::BrowserTaskEnvironment task_environment; |
| base::HistogramTester histogram_tester; |
| TestingProfile profile; |
| content::WebContents::CreateParams create_params(&profile); |
| auto web_contents = content::WebContents::Create(create_params); |
| |
| MockSharingService* service = CreateSharingService(&profile); |
| |
| std::vector<SharingTargetDeviceInfo> devices; |
| |
| devices.push_back(CreateFakeSharingTargetDeviceInfo("guid", "name")); |
| |
| EXPECT_CALL(*service, GetDeviceCandidates(_)) |
| .WillOnce(Return(ByMove(std::move(devices)))); |
| base::RunLoop loop; |
| |
| EXPECT_CALL(*service, SendMessageToDevice(_, _, _, _)) |
| .WillOnce([&](const SharingTargetDeviceInfo& device_info, |
| base::TimeDelta response_timeout, |
| components_sharing_message::SharingMessage message, |
| SharingMessageSender::ResponseCallback callback) { |
| auto response = std::make_unique<ResponseMessage>(); |
| response->mutable_sms_fetch_response()->set_one_time_code("ABC"); |
| std::move(callback).Run(SharingSendMessageResult::kSuccessful, |
| std::move(response)); |
| return base::DoNothing(); |
| }); |
| |
| FetchRemoteSms( |
| web_contents.get(), std::vector<url::Origin>{GetOriginForURL("a.com")}, |
| BindLambdaForTesting( |
| [&loop](std::optional<std::vector<url::Origin>>, |
| std::optional<std::string> result, |
| std::optional<content::SmsFetchFailureType> failure_type) { |
| ASSERT_TRUE(result); |
| ASSERT_EQ("ABC", result); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| histogram_tester.ExpectUniqueSample( |
| "Blink.Sms.Receive.CrossDeviceFailure", |
| static_cast<int>(WebOTPCrossDeviceFailure::kNoFailure), 1); |
| } |
| |
| TEST(SmsRemoteFetcherTest, OneDeviceTimesOut) { |
| content::BrowserTaskEnvironment task_environment; |
| TestingProfile profile; |
| content::WebContents::CreateParams create_params(&profile); |
| auto web_contents = content::WebContents::Create(create_params); |
| |
| MockSharingService* service = CreateSharingService(&profile); |
| |
| std::vector<SharingTargetDeviceInfo> devices; |
| |
| devices.push_back(CreateFakeSharingTargetDeviceInfo("guid", "name")); |
| |
| EXPECT_CALL(*service, GetDeviceCandidates(_)) |
| .WillOnce(Return(ByMove(std::move(devices)))); |
| base::RunLoop loop; |
| |
| EXPECT_CALL(*service, SendMessageToDevice(_, _, _, _)) |
| .WillOnce([&](const SharingTargetDeviceInfo& device_info, |
| base::TimeDelta response_timeout, |
| components_sharing_message::SharingMessage message, |
| SharingMessageSender::ResponseCallback callback) { |
| std::move(callback).Run(SharingSendMessageResult::kAckTimeout, |
| std::make_unique<ResponseMessage>()); |
| return base::DoNothing(); |
| }); |
| |
| FetchRemoteSms( |
| web_contents.get(), std::vector<url::Origin>{GetOriginForURL("a.com")}, |
| BindLambdaForTesting( |
| [&loop](std::optional<std::vector<url::Origin>>, |
| std::optional<std::string> result, |
| std::optional<content::SmsFetchFailureType> failure_type) { |
| ASSERT_FALSE(result); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| } |
| |
| TEST(SmsRemoteFetcherTest, RequestCancelled) { |
| content::BrowserTaskEnvironment task_environment; |
| TestingProfile profile; |
| content::WebContents::CreateParams create_params(&profile); |
| auto web_contents = content::WebContents::Create(create_params); |
| |
| MockSharingService* service = CreateSharingService(&profile); |
| |
| std::vector<SharingTargetDeviceInfo> devices; |
| |
| devices.push_back(CreateFakeSharingTargetDeviceInfo("guid-abc", "name")); |
| |
| EXPECT_CALL(*service, GetDeviceCandidates(_)) |
| .WillOnce(Return(ByMove(std::move(devices)))); |
| base::RunLoop loop; |
| |
| base::MockOnceClosure mock_callback; |
| EXPECT_CALL(*service, SendMessageToDevice(_, _, _, _)) |
| .WillOnce([&](const SharingTargetDeviceInfo& device_info, |
| base::TimeDelta response_timeout, |
| components_sharing_message::SharingMessage message, |
| SharingMessageSender::ResponseCallback callback) { |
| std::move(callback).Run(SharingSendMessageResult::kCancelled, |
| std::make_unique<ResponseMessage>()); |
| return mock_callback.Get(); |
| }); |
| |
| base::OnceClosure cancel_callback = FetchRemoteSms( |
| web_contents.get(), std::vector<url::Origin>{GetOriginForURL("a.com")}, |
| BindLambdaForTesting( |
| [&loop](std::optional<std::vector<url::Origin>>, |
| std::optional<std::string> one_time_code, |
| std::optional<content::SmsFetchFailureType> failure_type) { |
| ASSERT_FALSE(one_time_code); |
| loop.Quit(); |
| })); |
| |
| EXPECT_CALL(mock_callback, Run); |
| std::move(cancel_callback).Run(); |
| |
| loop.Run(); |
| } |
| |
| TEST(SmsRemoteFetcherTest, NoSharingService) { |
| content::BrowserTaskEnvironment task_environment; |
| base::HistogramTester histogram_tester; |
| TestingProfile profile; |
| content::WebContents::CreateParams create_params(&profile); |
| auto web_contents = content::WebContents::Create(create_params); |
| |
| base::RunLoop loop; |
| |
| FetchRemoteSms( |
| web_contents.get(), std::vector<url::Origin>{GetOriginForURL("a.com")}, |
| BindLambdaForTesting( |
| [&loop](std::optional<std::vector<url::Origin>>, |
| std::optional<std::string> result, |
| std::optional<content::SmsFetchFailureType> failure_type) { |
| ASSERT_EQ(failure_type, |
| content::SmsFetchFailureType::kCrossDeviceFailure); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| histogram_tester.ExpectUniqueSample( |
| "Blink.Sms.Receive.CrossDeviceFailure", |
| WebOTPCrossDeviceFailure::kNoSharingService, 1); |
| } |
| |
| TEST(SmsRemoteFetcherTest, SendSharingMessageFailure) { |
| content::BrowserTaskEnvironment task_environment; |
| base::HistogramTester histogram_tester; |
| TestingProfile profile; |
| content::WebContents::CreateParams create_params(&profile); |
| auto web_contents = content::WebContents::Create(create_params); |
| |
| MockSharingService* service = CreateSharingService(&profile); |
| |
| std::vector<SharingTargetDeviceInfo> devices; |
| |
| devices.push_back(CreateFakeSharingTargetDeviceInfo("guid", "name")); |
| |
| EXPECT_CALL(*service, GetDeviceCandidates(_)) |
| .WillOnce(Return(ByMove(std::move(devices)))); |
| base::RunLoop loop; |
| |
| EXPECT_CALL(*service, SendMessageToDevice(_, _, _, _)) |
| .WillOnce([&](const SharingTargetDeviceInfo& device_info, |
| base::TimeDelta response_timeout, |
| components_sharing_message::SharingMessage message, |
| SharingMessageSender::ResponseCallback callback) { |
| std::move(callback).Run(SharingSendMessageResult::kAckTimeout, |
| std::make_unique<ResponseMessage>()); |
| return base::DoNothing(); |
| }); |
| |
| FetchRemoteSms( |
| web_contents.get(), std::vector<url::Origin>{GetOriginForURL("a.com")}, |
| BindLambdaForTesting( |
| [&loop](std::optional<std::vector<url::Origin>>, |
| std::optional<std::string> result, |
| std::optional<content::SmsFetchFailureType> failure_type) { |
| ASSERT_FALSE(result); |
| ASSERT_EQ(failure_type, |
| content::SmsFetchFailureType::kCrossDeviceFailure); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| histogram_tester.ExpectUniqueSample( |
| "Blink.Sms.Receive.CrossDeviceFailure", |
| WebOTPCrossDeviceFailure::kSharingMessageFailure, 1); |
| } |
| |
| TEST(SmsRemoteFetcherTest, UserDecline) { |
| content::BrowserTaskEnvironment task_environment; |
| base::HistogramTester histogram_tester; |
| TestingProfile profile; |
| content::WebContents::CreateParams create_params(&profile); |
| auto web_contents = content::WebContents::Create(create_params); |
| |
| MockSharingService* service = CreateSharingService(&profile); |
| |
| std::vector<SharingTargetDeviceInfo> devices; |
| |
| devices.push_back(CreateFakeSharingTargetDeviceInfo("guid", "name")); |
| |
| EXPECT_CALL(*service, GetDeviceCandidates(_)) |
| .WillOnce(Return(ByMove(std::move(devices)))); |
| base::RunLoop loop; |
| |
| EXPECT_CALL(*service, SendMessageToDevice(_, _, _, _)) |
| .WillOnce([&](const SharingTargetDeviceInfo& device_info, |
| base::TimeDelta response_timeout, |
| components_sharing_message::SharingMessage message, |
| SharingMessageSender::ResponseCallback callback) { |
| auto response = std::make_unique<ResponseMessage>(); |
| response->mutable_sms_fetch_response()->set_one_time_code("ABC"); |
| response->mutable_sms_fetch_response()->set_failure_type( |
| static_cast< |
| components_sharing_message::SmsFetchResponse::FailureType>( |
| content::SmsFetchFailureType::kPromptCancelled)); |
| std::move(callback).Run(SharingSendMessageResult::kSuccessful, |
| std::move(response)); |
| return base::DoNothing(); |
| }); |
| |
| FetchRemoteSms( |
| web_contents.get(), std::vector<url::Origin>{GetOriginForURL("a.com")}, |
| BindLambdaForTesting( |
| [&loop](std::optional<std::vector<url::Origin>>, |
| std::optional<std::string> result, |
| std::optional<content::SmsFetchFailureType> failure_type) { |
| ASSERT_FALSE(result); |
| ASSERT_EQ(failure_type, |
| content::SmsFetchFailureType::kPromptCancelled); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| histogram_tester.ExpectUniqueSample( |
| "Blink.Sms.Receive.CrossDeviceFailure", |
| WebOTPCrossDeviceFailure::kAPIFailureOnAndroid, 1); |
| } |