#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",
base::Time::Now(),
base::Days(1),
false,
sync_pb::
SyncEnums_SendTabReceivingType_SEND_TAB_RECEIVING_TYPE_CHROME_OR_UNSPECIFIED,
std::nullopt,
std::nullopt,
std::string(),
syncer::DataTypeSet::All(),
std::nullopt);
}
std::unique_ptr<syncer::DeviceInfo> CreateDeviceInfoWithTime(
const std::string& guid,
syncer::DeviceInfo::OsType os_type,
base::Time last_updated) {
return std::make_unique<syncer::DeviceInfo>(
guid, "name", "chrome_version", "user_agent",
sync_pb::SyncEnums_DeviceType_TYPE_PHONE, os_type,
syncer::DeviceInfo::FormFactor::kPhone, "scoped_id", "manufacturer",
"model", "full_hardware_class", last_updated,
base::Days(1),
false,
sync_pb::
SyncEnums_SendTabReceivingType_SEND_TAB_RECEIVING_TYPE_CHROME_OR_UNSPECIFIED,
std::nullopt,
std::nullopt,
std::string(),
syncer::DataTypeSet::All(),
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) {
WriteCrossDeviceValue(profile()->GetPrefs(),
prefs::kCrossDeviceCrossPlatformPromosIOS16thActiveDay,
kIOSDevice, base::TimeToValue(base::Time::Now()));
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) {
WriteCrossDeviceValue(profile()->GetPrefs(),
prefs::kCrossDeviceCrossPlatformPromosIOS16thActiveDay,
kIOSDevice,
base::TimeToValue(base::Time::Now() - base::Days(30)));
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) {
WriteCrossDeviceValue(profile()->GetPrefs(),
prefs::kCrossDeviceCrossPlatformPromosIOS16thActiveDay,
kAndroidDevice, base::TimeToValue(base::Time::Now()));
device_info_tracker()->Add(
CreateDeviceInfo(kAndroidDevice, syncer::DeviceInfo::OsType::kAndroid,
syncer::DeviceInfo::FormFactor::kPhone));
EXPECT_FALSE(ios_promos_utils::IsUserActiveOnIOS(profile()));
}
TEST_F(IOSPromosUtilsTest, IsUserActiveOnAndroid_Recent) {
device_info_tracker()->Add(CreateDeviceInfoWithTime(
kAndroidDevice, syncer::DeviceInfo::OsType::kAndroid,
base::Time::Now() - base::Days(1)));
EXPECT_TRUE(ios_promos_utils::IsUserActiveOnAndroid(profile()));
}
TEST_F(IOSPromosUtilsTest, IsUserActiveOnAndroid_Old) {
device_info_tracker()->Add(CreateDeviceInfoWithTime(
kAndroidDevice, syncer::DeviceInfo::OsType::kAndroid,
base::Time::Now() - base::Days(30)));
EXPECT_FALSE(ios_promos_utils::IsUserActiveOnAndroid(profile()));
}
TEST_F(IOSPromosUtilsTest, IsUserActiveOnAndroid_NoDevice) {
device_info_tracker()->Add(
CreateDeviceInfoWithTime(kIOSDevice, syncer::DeviceInfo::OsType::kIOS,
base::Time::Now() - base::Days(1)));
EXPECT_FALSE(ios_promos_utils::IsUserActiveOnAndroid(profile()));
}
}