blob: e4da30f2e8e60fc3b7b1686551b782bc0f6e378d [file] [log] [blame]
// 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.
#import "ios/chrome/browser/synced_set_up/utils/utils.h"
#import <optional>
#import <string>
#import "base/test/task_environment.h"
#import "base/time/time.h"
#import "base/values.h"
#import "components/sync/protocol/sync_enums.pb.h"
#import "components/sync_device_info/device_info.h"
#import "components/sync_device_info/device_info_util.h"
#import "components/sync_device_info/fake_device_info_tracker.h"
#import "components/sync_preferences/cross_device_pref_tracker/cross_device_pref_tracker.h"
#import "components/sync_preferences/cross_device_pref_tracker/prefs/cross_device_pref_names.h"
#import "components/sync_preferences/cross_device_pref_tracker/timestamped_pref_value.h"
#import "ios/chrome/app/profile/profile_init_stage.h"
#import "ios/chrome/app/profile/profile_state.h"
#import "ios/chrome/app/profile/profile_state_test_utils.h"
#import "ios/chrome/browser/scoped_ui_blocker/ui_bundled/ui_blocker_manager.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
namespace {
// Test implementation of `CrossDevicePrefTracker`.
class TestCrossDevicePrefTracker
: public sync_preferences::CrossDevicePrefTracker {
public:
TestCrossDevicePrefTracker() = default;
~TestCrossDevicePrefTracker() override = default;
// `KeyedService` overrides.
void Shutdown() override {}
// `CrossDevicePrefTracker` overrides.
void AddObserver(Observer* observer) override {}
void RemoveObserver(Observer* observer) override {}
std::vector<sync_preferences::TimestampedPrefValue> GetValues(
std::string_view pref_name,
const DeviceFilter& filter) const override {
auto it = pref_values_.find(pref_name);
if (it == pref_values_.end()) {
return {};
}
std::vector<sync_preferences::TimestampedPrefValue> result;
for (const auto& timestamped_value : it->second) {
sync_preferences::TimestampedPrefValue copied_value;
copied_value.value = timestamped_value.value.Clone();
copied_value.last_observed_change_time =
timestamped_value.last_observed_change_time;
copied_value.device_sync_cache_guid =
timestamped_value.device_sync_cache_guid;
result.push_back(std::move(copied_value));
}
return result;
}
std::optional<sync_preferences::TimestampedPrefValue> GetMostRecentValue(
std::string_view pref_name,
const DeviceFilter& filter) const override {
return std::nullopt;
}
// Testing Method for injecting pref values into the tracker.
void AddSyncedPrefValue(std::string_view pref_name,
sync_preferences::TimestampedPrefValue& value) {
pref_values_[pref_name].push_back(std::move(value));
}
private:
// Testing member. Map containing TimestampedPrefValues mapped to their
// associated pref's name.
std::map<std::string_view,
std::vector<sync_preferences::TimestampedPrefValue>>
pref_values_;
};
} // namespace
// Test suite for Synced Set Up utility functions.
class SyncedSetUpUtilsTest : public PlatformTest {
public:
void InitializeProfileState() {
profile_state_ = [[ProfileState alloc] initWithAppState:nil];
ASSERT_NSEQ(profile_state_.foregroundActiveScene, nil);
}
// Creates a DeviceInfo object.
std::unique_ptr<syncer::DeviceInfo> CreateDeviceInfoForTesting(
std::string guid,
syncer::DeviceInfo::FormFactor form_factor,
syncer::DeviceInfo::OsType os_type,
base::Time last_updated_timestamp = base::Time::Now()) {
return CreateFakeDeviceInfo(guid, "Device Name", std::nullopt,
sync_pb::SyncEnums::TYPE_UNSET, os_type,
form_factor, "manufacturer", "model",
std::string(), last_updated_timestamp);
}
// Helper for creating a DeviceInfo object.
std::unique_ptr<syncer::DeviceInfo> CreateFakeDeviceInfo(
const std::string& guid,
const std::string& name = "name",
const std::optional<syncer::DeviceInfo::SharingInfo>& sharing_info =
std::nullopt,
sync_pb::SyncEnums_DeviceType device_type =
sync_pb::SyncEnums_DeviceType_TYPE_UNSET,
syncer::DeviceInfo::OsType os_type = syncer::DeviceInfo::OsType::kUnknown,
syncer::DeviceInfo::FormFactor form_factor =
syncer::DeviceInfo::FormFactor::kUnknown,
const std::string& manufacturer_name = "manufacturer",
const std::string& model_name = "model",
const std::string& full_hardware_class = std::string(),
base::Time last_updated_timestamp = base::Time::Now()) {
return std::make_unique<syncer::DeviceInfo>(
guid, name, "chrome_version", "user_agent", device_type, os_type,
form_factor, "device_id", manufacturer_name, model_name,
full_hardware_class, last_updated_timestamp,
syncer::DeviceInfoUtil::GetPulseInterval(),
/*send_tab_to_self_receiving_enabled=*/
false,
sync_pb::
SyncEnums_SendTabReceivingType_SEND_TAB_RECEIVING_TYPE_CHROME_OR_UNSPECIFIED,
sharing_info,
/*paask_info=*/std::nullopt,
/*fcm_registration_token=*/std::string(),
/*interested_data_types=*/syncer::DataTypeSet(),
/*auto_sign_out_last_signin_timestamp=*/std::nullopt);
}
// Helper for configuring a TimestampedPrefValue.
void ConfigureTimestampedPrefValue(
sync_preferences::TimestampedPrefValue& timestamped_value,
base::Value value,
std::string device_sync_cache_guid,
base::Time last_observed_change_time = base::Time::Now()) {
timestamped_value.value = value.Clone();
timestamped_value.last_observed_change_time = last_observed_change_time;
timestamped_value.device_sync_cache_guid = device_sync_cache_guid;
}
// Helper function to create and connect a scene with a specific activation
// level.
SceneState* ConnectSceneWithActivationLevel(SceneActivationLevel level) {
SceneState* scene = [[SceneState alloc] initWithAppState:nil];
scene.activationLevel = level;
[profile_state_ sceneStateConnected:scene];
if (level == SceneActivationLevelForegroundActive) {
EXPECT_EQ(profile_state_.foregroundActiveScene, scene);
} else {
EXPECT_EQ(profile_state_.foregroundActiveScene, nil);
}
return scene;
}
protected:
ProfileState* profile_state_;
base::test::TaskEnvironment task_environment_;
TestCrossDevicePrefTracker pref_tracker_;
syncer::FakeDeviceInfoTracker device_info_tracker_;
};
// Test that a device with a matching form factor is chosen as the best fit
// device.
TEST_F(SyncedSetUpUtilsTest, TestMatchPrefsByFormFactor) {
// Local device (iOS phone).
std::unique_ptr<syncer::DeviceInfo> local_device = CreateDeviceInfoForTesting(
"local_device", syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kIOS);
// Android tablet.
std::unique_ptr<syncer::DeviceInfo> android_tablet =
CreateDeviceInfoForTesting("android_tablet",
syncer::DeviceInfo::FormFactor::kTablet,
syncer::DeviceInfo::OsType::kAndroid);
// Android phone (match).
std::unique_ptr<syncer::DeviceInfo> android_phone =
CreateDeviceInfoForTesting("android_phone",
syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kAndroid);
// iOS tablet.
std::unique_ptr<syncer::DeviceInfo> ios_tablet = CreateDeviceInfoForTesting(
"ios_tablet", syncer::DeviceInfo::FormFactor::kTablet,
syncer::DeviceInfo::OsType::kIOS);
device_info_tracker_.Add(local_device.get());
device_info_tracker_.SetLocalCacheGuid(local_device.get()->guid());
device_info_tracker_.Add(android_tablet.get());
device_info_tracker_.Add(android_phone.get());
device_info_tracker_.Add(ios_tablet.get());
// Configure some `TimestampedPrefValue` objects associated with the tracked
// device GUID's and add them to the pref tracker.
sync_preferences::TimestampedPrefValue local_device_magic_stack_enabled;
ConfigureTimestampedPrefValue(local_device_magic_stack_enabled,
base::Value(true), local_device.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
local_device_magic_stack_enabled);
sync_preferences::TimestampedPrefValue android_tablet_magic_stack_enabled;
sync_preferences::TimestampedPrefValue android_tablet_most_visited_enabled;
ConfigureTimestampedPrefValue(android_tablet_magic_stack_enabled,
base::Value(true),
android_tablet.get()->guid());
ConfigureTimestampedPrefValue(android_tablet_most_visited_enabled,
base::Value(false),
android_tablet.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
android_tablet_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
android_tablet_most_visited_enabled);
sync_preferences::TimestampedPrefValue android_phone_magic_stack_enabled;
sync_preferences::TimestampedPrefValue android_phone_most_visited_enabled;
ConfigureTimestampedPrefValue(android_phone_magic_stack_enabled,
base::Value(false),
android_phone.get()->guid());
ConfigureTimestampedPrefValue(android_phone_most_visited_enabled,
base::Value(true), android_phone.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
android_phone_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
android_phone_most_visited_enabled);
sync_preferences::TimestampedPrefValue ios_tablet_magic_stack_enabled;
sync_preferences::TimestampedPrefValue ios_tablet_most_visited_enabled;
ConfigureTimestampedPrefValue(ios_tablet_magic_stack_enabled,
base::Value(false), ios_tablet.get()->guid());
ConfigureTimestampedPrefValue(ios_tablet_most_visited_enabled,
base::Value(false), ios_tablet.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
ios_tablet_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
ios_tablet_most_visited_enabled);
// Expect that the prefs from the Android phone are returned.
std::map<std::string_view, base::Value> expected_result;
expected_result.insert(
{GetTrackedPrefName(prefs::kCrossDeviceMagicStackHomeModuleEnabled),
android_phone_magic_stack_enabled.value.Clone()});
expected_result.insert(
{GetTrackedPrefName(prefs::kCrossDeviceMostVisitedHomeModuleEnabled),
android_phone_most_visited_enabled.value.Clone()});
std::map<std::string_view, base::Value> result = GetRemoteDevicePrefs(
&pref_tracker_, &device_info_tracker_, local_device.get());
ASSERT_TRUE(!result.empty());
ASSERT_EQ(result.size(), expected_result.size());
// Compare the resultant map to the expected map.
for (const auto& [pref_name, pref_value] : expected_result) {
auto it = result.find(pref_name);
ASSERT_NE(it, result.end());
EXPECT_EQ(it->second, pref_value);
}
}
// Test that a device with a matching OS is chosen as the best fit device if
// there is no device with a matching form factor.
TEST_F(SyncedSetUpUtilsTest, TestMatchPrefsByOsType) {
// Local device (iOS phone).
std::unique_ptr<syncer::DeviceInfo> local_device = CreateDeviceInfoForTesting(
"local_device", syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kIOS);
// Android tablet.
std::unique_ptr<syncer::DeviceInfo> android_tablet =
CreateDeviceInfoForTesting("android_tablet",
syncer::DeviceInfo::FormFactor::kTablet,
syncer::DeviceInfo::OsType::kAndroid);
// iOS tablet (match).
std::unique_ptr<syncer::DeviceInfo> ios_tablet = CreateDeviceInfoForTesting(
"ios_tablet", syncer::DeviceInfo::FormFactor::kTablet,
syncer::DeviceInfo::OsType::kIOS);
device_info_tracker_.Add(local_device.get());
device_info_tracker_.SetLocalCacheGuid(local_device.get()->guid());
device_info_tracker_.Add(android_tablet.get());
device_info_tracker_.Add(ios_tablet.get());
// Configure some `TimestampedPrefValue` objects associated with the tracked
// device GUID's and add them to the pref tracker.
sync_preferences::TimestampedPrefValue local_device_magic_stack_enabled;
ConfigureTimestampedPrefValue(local_device_magic_stack_enabled,
base::Value(true), local_device.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
local_device_magic_stack_enabled);
sync_preferences::TimestampedPrefValue android_tablet_magic_stack_enabled;
sync_preferences::TimestampedPrefValue android_tablet_most_visited_enabled;
ConfigureTimestampedPrefValue(android_tablet_magic_stack_enabled,
base::Value(true),
android_tablet.get()->guid());
ConfigureTimestampedPrefValue(android_tablet_most_visited_enabled,
base::Value(false),
android_tablet.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
android_tablet_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
android_tablet_most_visited_enabled);
sync_preferences::TimestampedPrefValue ios_tablet_magic_stack_enabled;
sync_preferences::TimestampedPrefValue ios_tablet_most_visited_enabled;
ConfigureTimestampedPrefValue(ios_tablet_magic_stack_enabled,
base::Value(false), ios_tablet.get()->guid());
ConfigureTimestampedPrefValue(ios_tablet_most_visited_enabled,
base::Value(false), ios_tablet.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
ios_tablet_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
ios_tablet_most_visited_enabled);
// Expect that the prefs from the iOS tablet are returned.
std::map<std::string_view, base::Value> expected_result;
expected_result.insert(
{GetTrackedPrefName(prefs::kCrossDeviceMagicStackHomeModuleEnabled),
ios_tablet_magic_stack_enabled.value.Clone()});
expected_result.insert(
{GetTrackedPrefName(prefs::kCrossDeviceMostVisitedHomeModuleEnabled),
ios_tablet_most_visited_enabled.value.Clone()});
std::map<std::string_view, base::Value> result = GetRemoteDevicePrefs(
&pref_tracker_, &device_info_tracker_, local_device.get());
ASSERT_TRUE(!result.empty());
ASSERT_EQ(result.size(), expected_result.size());
// compare the returned map to the expected map
for (const auto& [pref_name, pref_value] : expected_result) {
auto it = result.find(pref_name);
ASSERT_NE(it, result.end());
EXPECT_EQ(it->second, pref_value);
}
}
// Test that a device with the highest volume of observed pref changes is chosen
// as the best fit device if the synced devices score the same against the
// current device on form factor and OS.
TEST_F(SyncedSetUpUtilsTest, TestMatchPrefsByObservedChangeCount) {
// Local device (iOS phone).
std::unique_ptr<syncer::DeviceInfo> local_device = CreateDeviceInfoForTesting(
"local_device", syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kIOS);
// Android phone.
std::unique_ptr<syncer::DeviceInfo> android_phone =
CreateDeviceInfoForTesting("android_phone",
syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kAndroid);
// iOS phone.
std::unique_ptr<syncer::DeviceInfo> ios_phone = CreateDeviceInfoForTesting(
"ios_phone", syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kIOS);
// iOS phone (match).
std::unique_ptr<syncer::DeviceInfo> ios_phone_2 = CreateDeviceInfoForTesting(
"ios_phone_2", syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kIOS);
device_info_tracker_.Add(local_device.get());
device_info_tracker_.SetLocalCacheGuid(local_device.get()->guid());
device_info_tracker_.Add(android_phone.get());
device_info_tracker_.Add(ios_phone.get());
device_info_tracker_.Add(ios_phone_2.get());
// Configure some `TimestampedPrefValue` objects associated with the tracked
// device GUID's and add them to the pref tracker.
sync_preferences::TimestampedPrefValue local_device_magic_stack_enabled;
ConfigureTimestampedPrefValue(local_device_magic_stack_enabled,
base::Value(true), local_device.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
local_device_magic_stack_enabled);
sync_preferences::TimestampedPrefValue android_phone_magic_stack_enabled;
sync_preferences::TimestampedPrefValue android_phone_most_visited_enabled;
ConfigureTimestampedPrefValue(android_phone_magic_stack_enabled,
base::Value(true), android_phone.get()->guid());
ConfigureTimestampedPrefValue(android_phone_most_visited_enabled,
base::Value(false),
android_phone.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
android_phone_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
android_phone_most_visited_enabled);
sync_preferences::TimestampedPrefValue ios_phone_1_magic_stack_enabled;
sync_preferences::TimestampedPrefValue ios_phone_1_most_visited_enabled;
ConfigureTimestampedPrefValue(ios_phone_1_magic_stack_enabled,
base::Value(false), ios_phone.get()->guid());
ConfigureTimestampedPrefValue(ios_phone_1_most_visited_enabled,
base::Value(false), ios_phone.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
ios_phone_1_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
ios_phone_1_most_visited_enabled);
sync_preferences::TimestampedPrefValue ios_phone_2_magic_stack_enabled;
sync_preferences::TimestampedPrefValue ios_phone_2_most_visited_enabled;
sync_preferences::TimestampedPrefValue ios_phone_2_price_tracking_enabled;
ConfigureTimestampedPrefValue(ios_phone_2_magic_stack_enabled,
base::Value(true), ios_phone_2.get()->guid());
ConfigureTimestampedPrefValue(ios_phone_2_most_visited_enabled,
base::Value(true), ios_phone_2.get()->guid());
ConfigureTimestampedPrefValue(ios_phone_2_price_tracking_enabled,
base::Value(true), ios_phone_2.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
ios_phone_2_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
ios_phone_2_most_visited_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDevicePriceTrackingHomeModuleEnabled,
ios_phone_2_price_tracking_enabled);
// Expect that the prefs from the second iOS phone with more changes are
// returned.
std::map<std::string_view, base::Value> expected_result;
expected_result.insert(
{GetTrackedPrefName(prefs::kCrossDeviceMagicStackHomeModuleEnabled),
ios_phone_2_magic_stack_enabled.value.Clone()});
expected_result.insert(
{GetTrackedPrefName(prefs::kCrossDeviceMostVisitedHomeModuleEnabled),
ios_phone_2_most_visited_enabled.value.Clone()});
expected_result.insert(
{GetTrackedPrefName(prefs::kCrossDevicePriceTrackingHomeModuleEnabled),
ios_phone_2_price_tracking_enabled.value.Clone()});
std::map<std::string_view, base::Value> result = GetRemoteDevicePrefs(
&pref_tracker_, &device_info_tracker_, local_device.get());
ASSERT_TRUE(!result.empty());
ASSERT_EQ(result.size(), expected_result.size());
// Compare the resultant map to the expected map.
for (const auto& [pref_name, pref_value] : expected_result) {
auto it = result.find(pref_name);
ASSERT_NE(it, result.end());
EXPECT_EQ(it->second, pref_value);
}
}
// Tests that no new prefs to apply are returned if the local device has a
// higher volume of observed pref changes than the otherwise best fitting
// device.
TEST_F(SyncedSetUpUtilsTest, TestKeepLocalPrefsByChangeActivity) {
// Local device (iOS phone).
std::unique_ptr<syncer::DeviceInfo> local_device = CreateDeviceInfoForTesting(
"local_device", syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kIOS);
// Android phone.
std::unique_ptr<syncer::DeviceInfo> android_phone =
CreateDeviceInfoForTesting("android_phone",
syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kAndroid);
// iOS phone.
std::unique_ptr<syncer::DeviceInfo> ios_phone = CreateDeviceInfoForTesting(
"ios_phone", syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kIOS);
// iOS phone (match).
std::unique_ptr<syncer::DeviceInfo> ios_phone_2 = CreateDeviceInfoForTesting(
"ios_phone_2", syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kIOS);
device_info_tracker_.Add(local_device.get());
device_info_tracker_.SetLocalCacheGuid(local_device.get()->guid());
device_info_tracker_.Add(android_phone.get());
device_info_tracker_.Add(ios_phone.get());
device_info_tracker_.Add(ios_phone_2.get());
// Configure some `TimestampedPrefValue` objects associated with the tracked
// device GUID's and add them to the pref tracker.
sync_preferences::TimestampedPrefValue local_device_magic_stack_enabled;
sync_preferences::TimestampedPrefValue local_device_most_visited_enabled;
sync_preferences::TimestampedPrefValue local_device_price_tracking_enabled;
sync_preferences::TimestampedPrefValue local_device_safety_check_enabled;
ConfigureTimestampedPrefValue(local_device_magic_stack_enabled,
base::Value(true), local_device.get()->guid());
ConfigureTimestampedPrefValue(local_device_most_visited_enabled,
base::Value(true), local_device.get()->guid());
ConfigureTimestampedPrefValue(local_device_price_tracking_enabled,
base::Value(true), local_device.get()->guid());
ConfigureTimestampedPrefValue(local_device_safety_check_enabled,
base::Value(true), local_device.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
local_device_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
local_device_most_visited_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDevicePriceTrackingHomeModuleEnabled,
local_device_price_tracking_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceSafetyCheckHomeModuleEnabled,
local_device_safety_check_enabled);
sync_preferences::TimestampedPrefValue android_phone_magic_stack_enabled;
sync_preferences::TimestampedPrefValue android_phone_most_visited_enabled;
ConfigureTimestampedPrefValue(android_phone_magic_stack_enabled,
base::Value(true), android_phone.get()->guid());
ConfigureTimestampedPrefValue(android_phone_most_visited_enabled,
base::Value(false),
android_phone.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
android_phone_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
android_phone_most_visited_enabled);
sync_preferences::TimestampedPrefValue ios_phone_1_magic_stack_enabled;
sync_preferences::TimestampedPrefValue ios_phone_1_most_visited_enabled;
ConfigureTimestampedPrefValue(ios_phone_1_magic_stack_enabled,
base::Value(false), ios_phone.get()->guid());
ConfigureTimestampedPrefValue(ios_phone_1_most_visited_enabled,
base::Value(false), ios_phone.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
ios_phone_1_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
ios_phone_1_most_visited_enabled);
sync_preferences::TimestampedPrefValue ios_phone_2_magic_stack_enabled;
sync_preferences::TimestampedPrefValue ios_phone_2_most_visited_enabled;
sync_preferences::TimestampedPrefValue ios_phone_2_price_tracking_enabled;
ConfigureTimestampedPrefValue(ios_phone_2_magic_stack_enabled,
base::Value(true), ios_phone_2.get()->guid());
ConfigureTimestampedPrefValue(ios_phone_2_most_visited_enabled,
base::Value(true), ios_phone_2.get()->guid());
ConfigureTimestampedPrefValue(ios_phone_2_price_tracking_enabled,
base::Value(true), ios_phone_2.get()->guid());
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled,
ios_phone_2_magic_stack_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMostVisitedHomeModuleEnabled,
ios_phone_2_most_visited_enabled);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDevicePriceTrackingHomeModuleEnabled,
ios_phone_2_price_tracking_enabled);
// Expect that no new prefs are returned.
std::map<std::string_view, base::Value> result = GetRemoteDevicePrefs(
&pref_tracker_, &device_info_tracker_, local_device.get());
EXPECT_TRUE(result.empty());
}
// Tests that if a pref has multiple observed changes only the most recently
// observed pref change is retrieved.
TEST_F(SyncedSetUpUtilsTest, TestReturnsMostRecentObservedPrefChanges) {
const base::Time kNow = base::Time::Now();
// Local device (iOS phone).
std::unique_ptr<syncer::DeviceInfo> local_device = CreateDeviceInfoForTesting(
"local_device", syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kIOS);
// Android phone (match).
std::unique_ptr<syncer::DeviceInfo> android_phone =
CreateDeviceInfoForTesting("android_phone",
syncer::DeviceInfo::FormFactor::kPhone,
syncer::DeviceInfo::OsType::kAndroid);
device_info_tracker_.Add(local_device.get());
device_info_tracker_.SetLocalCacheGuid(local_device.get()->guid());
device_info_tracker_.Add(android_phone.get());
// Configure some `TimestampedPrefValue` objects for the same pref associated
// with the Android phone GUID's and add them to the pref tracker. These
// represent the same pref being changed several times over a period of time.
sync_preferences::TimestampedPrefValue magic_stack_enabled_day;
sync_preferences::TimestampedPrefValue magic_stack_enabled_now;
sync_preferences::TimestampedPrefValue magic_stack_enabled_week;
ConfigureTimestampedPrefValue(magic_stack_enabled_day, base::Value(true),
android_phone.get()->guid(),
kNow - base::Days(1));
ConfigureTimestampedPrefValue(magic_stack_enabled_now, base::Value(false),
android_phone.get()->guid(), kNow);
ConfigureTimestampedPrefValue(magic_stack_enabled_week, base::Value(true),
android_phone.get()->guid(),
kNow - base::Days(7));
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled, magic_stack_enabled_day);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled, magic_stack_enabled_now);
pref_tracker_.AddSyncedPrefValue(
prefs::kCrossDeviceMagicStackHomeModuleEnabled, magic_stack_enabled_week);
// Expect that the pref is returned with its most recently set value.
std::map<std::string_view, base::Value> expected_result;
expected_result.insert(
{GetTrackedPrefName(prefs::kCrossDeviceMagicStackHomeModuleEnabled),
magic_stack_enabled_now.value.Clone()});
std::map<std::string_view, base::Value> result = GetRemoteDevicePrefs(
&pref_tracker_, &device_info_tracker_, local_device.get());
ASSERT_TRUE(!result.empty());
ASSERT_EQ(result.size(), expected_result.size());
// Compare the resultant map to the expected map.
for (const auto& [pref_name, pref_value] : expected_result) {
auto it = result.find(pref_name);
ASSERT_NE(it, result.end());
EXPECT_EQ(it->second, pref_value);
}
}
// Tests that the active scene is returned when all preconditions are met (main
// provider active, profile ready, no blockers).
TEST_F(SyncedSetUpUtilsTest, ReturnsActiveSceneWhenAllPreconditionsMet) {
InitializeProfileState();
SetProfileStateInitStage(profile_state_, ProfileInitStage::kFinal);
SceneState* scene =
ConnectSceneWithActivationLevel(SceneActivationLevelForegroundActive);
SceneState* result = GetEligibleSceneForSyncedSetUp(profile_state_);
EXPECT_EQ(result, scene);
}
// Tests that `nil` is returned if the input `ProfileState` is `null`.
TEST_F(SyncedSetUpUtilsTest, ReturnsNilWhenProfileStateIsNull) {
InitializeProfileState();
SceneState* result = GetEligibleSceneForSyncedSetUp(nil);
EXPECT_EQ(result, nil);
}
// Tests that `nil` is returned if the profile initialization is not complete.
TEST_F(SyncedSetUpUtilsTest, ReturnsNilWhenProfileIsNotFinalized) {
InitializeProfileState();
SetProfileStateInitStage(profile_state_, ProfileInitStage::kProfileLoaded);
ConnectSceneWithActivationLevel(SceneActivationLevelForegroundActive);
SceneState* result = GetEligibleSceneForSyncedSetUp(profile_state_);
EXPECT_EQ(result, nil);
}
// Tests that `nil` is returned if a UI blocker is present.
TEST_F(SyncedSetUpUtilsTest, ReturnsNilWhenUIBlockerIsPresent) {
InitializeProfileState();
SetProfileStateInitStage(profile_state_, ProfileInitStage::kFinal);
SceneState* scene =
ConnectSceneWithActivationLevel(SceneActivationLevelForegroundActive);
[profile_state_ incrementBlockingUICounterForTarget:scene];
SceneState* result = GetEligibleSceneForSyncedSetUp(profile_state_);
EXPECT_EQ(result, nil);
}
// Tests that `nil` is returned if there is no foreground active scene.
TEST_F(SyncedSetUpUtilsTest, ReturnsNilWhenNoForegroundActiveSceneExists) {
InitializeProfileState();
SetProfileStateInitStage(profile_state_, ProfileInitStage::kFinal);
ConnectSceneWithActivationLevel(SceneActivationLevelForegroundInactive);
SceneState* result = GetEligibleSceneForSyncedSetUp(profile_state_);
EXPECT_EQ(result, nil);
}