| // Copyright 2025 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/ui/promos/ios_promos_utils.h" |
| |
| #include "base/json/values_util.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/sync/device_info_sync_service_factory.h" |
| #include "chrome/browser/sync/prefs/cross_device_pref_tracker/cross_device_pref_tracker_factory.h" |
| #include "chrome/browser/sync/sync_service_factory.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/sync/test/test_sync_service.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_device_info_tracker.h" |
| #include "components/sync_preferences/cross_device_pref_tracker/cross_device_pref_tracker.h" |
| #include "components/sync_preferences/cross_device_pref_tracker/prefs/cross_device_pref_names.h" |
| #include "components/sync_preferences/features.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using syncer::DeviceInfo; |
| |
| namespace { |
| |
| const char* kIOSDevice = "ios_device"; |
| const char* kAndroidDevice = "android_device"; |
| |
| std::unique_ptr<KeyedService> CreateTestSyncService( |
| content::BrowserContext* context) { |
| return std::make_unique<syncer::TestSyncService>(); |
| } |
| |
| std::unique_ptr<KeyedService> CreateTestDeviceInfoSyncService( |
| content::BrowserContext* context) { |
| return std::make_unique<syncer::FakeDeviceInfoSyncService>(); |
| } |
| |
| std::unique_ptr<syncer::DeviceInfo> CreateDeviceInfo( |
| const std::string& guid, |
| syncer::DeviceInfo::OsType os_type, |
| syncer::DeviceInfo::FormFactor form_factor) { |
| return std::make_unique<syncer::DeviceInfo>( |
| guid, "name", "chrome_version", "user_agent", |
| sync_pb::SyncEnums_DeviceType_TYPE_LINUX, os_type, form_factor, |
| "scoped_id", "manufacturer", "model", "full_hardware_class", |
| /*last_updated_timestamp=*/base::Time::Now(), |
| /*pulse_interval=*/base::Days(1), |
| /*send_tab_to_self_receiving_enabled=*/ |
| false, |
| /*send_tab_to_self_receiving_type=*/ |
| sync_pb:: |
| SyncEnums_SendTabReceivingType_SEND_TAB_RECEIVING_TYPE_CHROME_OR_UNSPECIFIED, |
| /*sharing_info=*/std::nullopt, |
| /*paask_info=*/std::nullopt, |
| /*fcm_registration_token=*/std::string(), |
| /*interested_data_types=*/syncer::DataTypeSet::All(), |
| /*auto_sign_out_last_signin_timestamp=*/std::nullopt); |
| } |
| |
| class IOSPromosUtilsTest : public testing::Test { |
| public: |
| IOSPromosUtilsTest() { |
| feature_list_.InitAndEnableFeature( |
| sync_preferences::features::kEnableCrossDevicePrefTracker); |
| } |
| ~IOSPromosUtilsTest() override = default; |
| |
| void SetUp() override { |
| testing::Test::SetUp(); |
| TestingProfile::Builder builder; |
| builder.AddTestingFactory(SyncServiceFactory::GetInstance(), |
| base::BindRepeating(&CreateTestSyncService)); |
| builder.AddTestingFactory( |
| DeviceInfoSyncServiceFactory::GetInstance(), |
| base::BindRepeating(&CreateTestDeviceInfoSyncService)); |
| profile_ = builder.Build(); |
| CrossDevicePrefTrackerFactory::GetForProfile(profile()); |
| } |
| |
| void TearDown() override { |
| profile_.reset(); |
| testing::Test::TearDown(); |
| } |
| |
| protected: |
| Profile* profile() { return profile_.get(); } |
| |
| syncer::FakeDeviceInfoSyncService* device_info_sync_service() { |
| return static_cast<syncer::FakeDeviceInfoSyncService*>( |
| DeviceInfoSyncServiceFactory::GetForProfile(profile())); |
| } |
| |
| syncer::FakeDeviceInfoTracker* device_info_tracker() { |
| return device_info_sync_service()->GetDeviceInfoTracker(); |
| } |
| |
| void WriteCrossDeviceValue(PrefService* prefs, |
| std::string_view pref_name, |
| std::string_view device_guid, |
| base::Value&& value, |
| base::Time timestamp = base::Time::Now()) { |
| base::Value::Dict timestamped_value; |
| timestamped_value.Set("value", std::move(value)); |
| timestamped_value.Set("last_observed_change_time", |
| base::TimeToValue(timestamp)); |
| timestamped_value.Set("update_time", base::TimeToValue(timestamp)); |
| base::Value::Dict cross_device_value; |
| cross_device_value.Set(device_guid, std::move(timestamped_value)); |
| prefs->SetDict(pref_name, std::move(cross_device_value)); |
| } |
| |
| private: |
| content::BrowserTaskEnvironment task_environment_; |
| base::test::ScopedFeatureList feature_list_; |
| std::unique_ptr<TestingProfile> profile_; |
| }; |
| |
| TEST_F(IOSPromosUtilsTest, IsUserActiveOnIOS_Recent) { |
| // Set a recent timestamp for the iOS device. |
| WriteCrossDeviceValue(profile()->GetPrefs(), |
| prefs::kCrossDeviceCrossPlatformPromosIOS16thActiveDay, |
| kIOSDevice, base::TimeToValue(base::Time::Now())); |
| |
| // Fake an iOS device. |
| device_info_tracker()->Add( |
| CreateDeviceInfo(kIOSDevice, syncer::DeviceInfo::OsType::kIOS, |
| syncer::DeviceInfo::FormFactor::kPhone)); |
| |
| EXPECT_TRUE(ios_promos_utils::IsUserActiveOnIOS(profile())); |
| } |
| |
| TEST_F(IOSPromosUtilsTest, IsUserActiveOnIOS_Old) { |
| // Set an old timestamp for the iOS device. |
| WriteCrossDeviceValue(profile()->GetPrefs(), |
| prefs::kCrossDeviceCrossPlatformPromosIOS16thActiveDay, |
| kIOSDevice, |
| base::TimeToValue(base::Time::Now() - base::Days(30))); |
| |
| // Fake an iOS device. |
| device_info_tracker()->Add( |
| CreateDeviceInfo(kIOSDevice, syncer::DeviceInfo::OsType::kIOS, |
| syncer::DeviceInfo::FormFactor::kPhone)); |
| |
| EXPECT_FALSE(ios_promos_utils::IsUserActiveOnIOS(profile())); |
| } |
| |
| TEST_F(IOSPromosUtilsTest, IsUserActiveOnIOS_NoIOSDevice) { |
| // Set a recent timestamp for a non-iOS device. |
| WriteCrossDeviceValue(profile()->GetPrefs(), |
| prefs::kCrossDeviceCrossPlatformPromosIOS16thActiveDay, |
| kAndroidDevice, base::TimeToValue(base::Time::Now())); |
| |
| // Fake a non-iOS device. |
| device_info_tracker()->Add( |
| CreateDeviceInfo(kAndroidDevice, syncer::DeviceInfo::OsType::kAndroid, |
| syncer::DeviceInfo::FormFactor::kPhone)); |
| |
| EXPECT_FALSE(ios_promos_utils::IsUserActiveOnIOS(profile())); |
| } |
| |
| } // namespace |