#include "components/sync_preferences/pref_service_syncable.h"
#include <stdint.h>
#include <memory>
#include "base/functional/callback_helpers.h"
#include "base/json/json_reader.h"
#include "base/json/json_string_value_serializer.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "build/chromeos_buildflags.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_notifier_impl.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/prefs/testing_pref_store.h"
#include "components/sync/base/client_tag_hash.h"
#include "components/sync/base/data_type.h"
#include "components/sync/base/features.h"
#include "components/sync/model/sync_change.h"
#include "components/sync/model/sync_change_processor.h"
#include "components/sync/model/sync_data.h"
#include "components/sync/model/syncable_service.h"
#include "components/sync/protocol/entity_specifics.pb.h"
#include "components/sync/protocol/preference_specifics.pb.h"
#include "components/sync_preferences/pref_model_associator.h"
#include "components/sync_preferences/pref_model_associator_client.h"
#include "components/sync_preferences/pref_service_syncable_factory.h"
#include "components/sync_preferences/pref_service_syncable_observer.h"
#include "components/sync_preferences/syncable_prefs_database.h"
#include "components/sync_preferences/synced_pref_observer.h"
#include "components/sync_preferences/test_syncable_prefs_database.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "testing/gmock/include/gmock/gmock-matchers.h"
#endif
DataType;
DataTypeSet;
SyncChange;
SyncData;
Eq;
IsEmpty;
Matches;
NotNull;
UnorderedElementsAre;
PrefRegistrySyncable;
namespace sync_preferences {
namespace {
const char kExampleUrl0[] = …;
const char kExampleUrl1[] = …;
const char kExampleUrl2[] = …;
const char kStringPrefName[] = …;
const char kListPrefName[] = …;
const char kMergeableListPrefName[] = …;
const char kMergeableDictPrefName[] = …;
const char kUnsyncedPreferenceName[] = …;
const char kUnsyncedPreferenceDefaultValue[] = …;
const char kDefaultCharsetPrefName[] = …;
const char kNonDefaultCharsetValue[] = …;
const char kDefaultCharsetValue[] = …;
const char kBrowserPrefName[] = …;
const char kBrowserPriorityPrefName[] = …;
#if BUILDFLAG(IS_CHROMEOS_ASH)
const char kOsPrefName[] = "os_pref";
const char kOsPriorityPrefName[] = "os_priority_pref";
#endif
const TestSyncablePrefsDatabase::PrefsMap kSyncablePrefsDatabase = …;
std::optional<base::Value> FindValue(
const std::string& name,
const syncer::SyncChangeList& list,
std::optional<syncer::SyncChange::SyncChangeType> change_type =
std::nullopt) { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
constexpr DataTypeSet kAllPreferenceDataTypes = {
syncer::PREFERENCES, syncer::PRIORITY_PREFERENCES, syncer::OS_PREFERENCES,
syncer::OS_PRIORITY_PREFERENCES};
MATCHER_P(MatchesDataType, data_type, "") {
const syncer::SyncChange& sync_change = arg;
return Matches(data_type)(sync_change.sync_data().GetDataType());
}
#endif
class TestSyncProcessorStub : public syncer::SyncChangeProcessor { … };
class TestSyncedPrefObserver : public SyncedPrefObserver { … };
class TestPrefServiceSyncableObserver : public PrefServiceSyncableObserver { … };
syncer::SyncChange MakeRemoteChange(const std::string& name,
base::ValueView value,
SyncChange::SyncChangeType change_type,
syncer::DataType data_type) { … }
syncer::SyncChange MakeRemoteChange(const std::string& name,
base::ValueView value,
SyncChange::SyncChangeType type) { … }
SyncData CreateRemoteSyncData(const std::string& name, base::ValueView value) { … }
class PrefServiceSyncableTest : public testing::Test { … };
TEST_F(PrefServiceSyncableTest, CreatePrefSyncData) { … }
TEST_F(PrefServiceSyncableTest, ModelAssociationDoNotSyncDefaults) { … }
TEST_F(PrefServiceSyncableTest, ModelAssociationEmptyCloud) { … }
TEST_F(PrefServiceSyncableTest, ModelAssociationCloudHasData) { … }
TEST_F(PrefServiceSyncableTest, ModelAssociationWithDataTypeMismatch) { … }
class TestPrefModelAssociatorClient : public PrefModelAssociatorClient { … };
class PrefServiceSyncableMergeTest : public testing::Test { … };
TEST_F(PrefServiceSyncableMergeTest, ShouldMergeSelectedListValues) { … }
TEST_F(PrefServiceSyncableMergeTest, ManagedListPreferences) { … }
TEST_F(PrefServiceSyncableMergeTest, ShouldMergeSelectedDictionaryValues) { … }
TEST_F(PrefServiceSyncableMergeTest, KeepPriorityPreferencesSeparately) { … }
TEST_F(PrefServiceSyncableMergeTest, ShouldIgnoreUpdatesToNotSyncablePrefs) { … }
TEST_F(PrefServiceSyncableTest, FailModelAssociation) { … }
TEST_F(PrefServiceSyncableTest, UpdatedPreferenceWithDefaultValue) { … }
TEST_F(PrefServiceSyncableTest, UpdatedPreferenceWithValue) { … }
TEST_F(PrefServiceSyncableTest, AddAndUpdatePreference) { … }
TEST_F(PrefServiceSyncableTest, StopAndRestartSync) { … }
TEST_F(PrefServiceSyncableTest, UpdatedSyncNodeActionUpdate) { … }
TEST_F(PrefServiceSyncableTest, UpdatedSyncNodeActionUpdateTypeMismatch) { … }
TEST_F(PrefServiceSyncableTest, UpdatedSyncNodeActionAdd) { … }
TEST_F(PrefServiceSyncableTest, UpdatedSyncNodeUnknownPreference) { … }
TEST_F(PrefServiceSyncableTest, ManagedPreferences) { … }
TEST_F(PrefServiceSyncableTest, DynamicManagedPreferences) { … }
TEST_F(PrefServiceSyncableTest, DynamicManagedPreferencesWithSyncChange) { … }
TEST_F(PrefServiceSyncableTest, DynamicManagedDefaultPreferences) { … }
TEST_F(PrefServiceSyncableTest, DeletePreference) { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
class PrefServiceSyncableChromeOsTest : public testing::Test {
public:
PrefServiceSyncableChromeOsTest()
: pref_registry_(base::MakeRefCounted<PrefRegistrySyncable>()),
pref_notifier_(new PrefNotifierImpl),
user_prefs_(base::MakeRefCounted<TestingPrefStore>()),
standalone_browser_prefs_(base::MakeRefCounted<TestingPrefStore>()),
managed_prefs_(base::MakeRefCounted<TestingPrefStore>()),
supervised_user_prefs_(base::MakeRefCounted<TestingPrefStore>()),
extension_prefs_(base::MakeRefCounted<TestingPrefStore>()),
command_line_prefs_(base::MakeRefCounted<TestingPrefStore>()),
recommended_prefs_(base::MakeRefCounted<TestingPrefStore>()),
client_(base::MakeRefCounted<TestPrefModelAssociatorClient>()) {}
void CreatePrefService() {
pref_registry_->RegisterStringPref(kUnsyncedPreferenceName, std::string());
pref_registry_->RegisterStringPref(kBrowserPrefName, std::string(),
PrefRegistrySyncable::SYNCABLE_PREF);
pref_registry_->RegisterStringPref(
kBrowserPriorityPrefName, std::string(),
PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
pref_registry_->RegisterStringPref(kOsPrefName, std::string(),
PrefRegistrySyncable::SYNCABLE_OS_PREF);
pref_registry_->RegisterStringPref(
kOsPriorityPrefName, std::string(),
PrefRegistrySyncable::SYNCABLE_OS_PRIORITY_PREF);
prefs_ = std::make_unique<PrefServiceSyncable>(
std::unique_ptr<PrefNotifierImpl>(pref_notifier_),
std::make_unique<PrefValueStore>(
managed_prefs_.get(), supervised_user_prefs_.get(),
extension_prefs_.get(), standalone_browser_prefs_.get(),
command_line_prefs_.get(), user_prefs_.get(),
recommended_prefs_.get(), pref_registry_->defaults().get(),
pref_notifier_),
user_prefs_, standalone_browser_prefs_, pref_registry_, client_,
base::DoNothing(),
false);
}
void InitSyncForType(DataType type) {
syncer::SyncDataList empty_data;
std::optional<syncer::ModelError> error =
prefs_->GetSyncableService(type)->MergeDataAndStartSyncing(
type, empty_data, std::make_unique<TestSyncProcessorStub>(nullptr));
EXPECT_FALSE(error.has_value());
}
void InitSyncForAllTypes() {
for (DataType type : kAllPreferenceDataTypes) {
InitSyncForType(type);
}
}
DataTypeSet GetRegisteredDataTypes(const std::string& pref_name) {
DataTypeSet registered_types;
for (DataType type : kAllPreferenceDataTypes) {
if (static_cast<PrefModelAssociator*>(prefs_->GetSyncableService(type))
->IsPrefRegistered(pref_name)) {
registered_types.Put(type);
}
}
return registered_types;
}
SyncData MakeRemoteSyncData(const std::string& name,
base::ValueView value,
syncer::DataType data_type) {
std::string serialized;
JSONStringValueSerializer json(&serialized);
EXPECT_TRUE(json.Serialize(value));
sync_pb::EntitySpecifics entity;
sync_pb::PreferenceSpecifics* pref =
PrefModelAssociator::GetMutableSpecifics(data_type, &entity);
pref->set_name(name);
pref->set_value(serialized);
return SyncData::CreateRemoteData(
entity, syncer::ClientTagHash::FromUnhashed(data_type, name));
}
protected:
scoped_refptr<PrefRegistrySyncable> pref_registry_;
raw_ptr<PrefNotifierImpl, DanglingUntriaged>
pref_notifier_;
scoped_refptr<TestingPrefStore> user_prefs_;
scoped_refptr<TestingPrefStore> standalone_browser_prefs_;
scoped_refptr<TestingPrefStore> managed_prefs_;
scoped_refptr<TestingPrefStore> supervised_user_prefs_;
scoped_refptr<TestingPrefStore> extension_prefs_;
scoped_refptr<TestingPrefStore> command_line_prefs_;
scoped_refptr<TestingPrefStore> recommended_prefs_;
scoped_refptr<TestPrefModelAssociatorClient> client_;
std::unique_ptr<PrefServiceSyncable> prefs_;
};
TEST_F(PrefServiceSyncableChromeOsTest, IsPrefRegistered) {
CreatePrefService();
EXPECT_TRUE(GetRegisteredDataTypes(kUnsyncedPreferenceName).empty());
EXPECT_EQ(DataTypeSet({syncer::PREFERENCES}),
GetRegisteredDataTypes(kBrowserPrefName));
EXPECT_EQ(DataTypeSet({syncer::PRIORITY_PREFERENCES}),
GetRegisteredDataTypes(kBrowserPriorityPrefName));
EXPECT_EQ(DataTypeSet({syncer::OS_PREFERENCES}),
GetRegisteredDataTypes(kOsPrefName));
EXPECT_EQ(DataTypeSet({syncer::OS_PRIORITY_PREFERENCES}),
GetRegisteredDataTypes(kOsPriorityPrefName));
}
TEST_F(PrefServiceSyncableChromeOsTest, IsSyncing) {
CreatePrefService();
InitSyncForType(syncer::PREFERENCES);
EXPECT_TRUE(prefs_->IsSyncing());
EXPECT_FALSE(prefs_->IsPrioritySyncing());
EXPECT_FALSE(prefs_->AreOsPrefsSyncing());
EXPECT_FALSE(prefs_->AreOsPriorityPrefsSyncing());
}
TEST_F(PrefServiceSyncableChromeOsTest, IsPrioritySyncing) {
CreatePrefService();
InitSyncForType(syncer::PRIORITY_PREFERENCES);
EXPECT_FALSE(prefs_->IsSyncing());
EXPECT_TRUE(prefs_->IsPrioritySyncing());
EXPECT_FALSE(prefs_->AreOsPrefsSyncing());
EXPECT_FALSE(prefs_->AreOsPriorityPrefsSyncing());
}
TEST_F(PrefServiceSyncableChromeOsTest, AreOsPrefsSyncing) {
CreatePrefService();
InitSyncForType(syncer::OS_PREFERENCES);
EXPECT_FALSE(prefs_->IsSyncing());
EXPECT_FALSE(prefs_->IsPrioritySyncing());
EXPECT_TRUE(prefs_->AreOsPrefsSyncing());
EXPECT_FALSE(prefs_->AreOsPriorityPrefsSyncing());
}
TEST_F(PrefServiceSyncableChromeOsTest, AreOsPriorityPrefsSyncing) {
CreatePrefService();
InitSyncForType(syncer::OS_PRIORITY_PREFERENCES);
EXPECT_FALSE(prefs_->IsSyncing());
EXPECT_FALSE(prefs_->IsPrioritySyncing());
EXPECT_FALSE(prefs_->AreOsPrefsSyncing());
EXPECT_TRUE(prefs_->AreOsPriorityPrefsSyncing());
}
TEST_F(PrefServiceSyncableChromeOsTest, IsPrefSynced_OsPref) {
CreatePrefService();
InitSyncForAllTypes();
auto* associator = static_cast<PrefModelAssociator*>(
prefs_->GetSyncableService(syncer::OS_PREFERENCES));
EXPECT_FALSE(associator->IsPrefSyncedForTesting(kOsPrefName));
syncer::SyncChangeList list;
list.push_back(MakeRemoteChange(kOsPrefName, base::Value("value"),
SyncChange::ACTION_ADD,
syncer::OS_PREFERENCES));
associator->ProcessSyncChanges(FROM_HERE, list);
EXPECT_TRUE(associator->IsPrefSyncedForTesting(kOsPrefName));
}
TEST_F(PrefServiceSyncableChromeOsTest, IsPrefSynced_OsPriorityPref) {
CreatePrefService();
InitSyncForAllTypes();
auto* associator = static_cast<PrefModelAssociator*>(
prefs_->GetSyncableService(syncer::OS_PRIORITY_PREFERENCES));
EXPECT_FALSE(associator->IsPrefSyncedForTesting(kOsPriorityPrefName));
syncer::SyncChangeList list;
list.push_back(MakeRemoteChange(kOsPriorityPrefName, base::Value("value"),
SyncChange::ACTION_ADD,
syncer::OS_PRIORITY_PREFERENCES));
associator->ProcessSyncChanges(FROM_HERE, list);
EXPECT_TRUE(associator->IsPrefSyncedForTesting(kOsPriorityPrefName));
}
TEST_F(PrefServiceSyncableChromeOsTest, SyncedPrefObserver_OsPref) {
CreatePrefService();
InitSyncForAllTypes();
TestSyncedPrefObserver observer;
prefs_->AddSyncedPrefObserver(kOsPrefName, &observer);
prefs_->SetString(kOsPrefName, "value");
EXPECT_EQ(kOsPrefName, observer.last_pref_);
EXPECT_EQ(1, observer.changed_count_);
prefs_->RemoveSyncedPrefObserver(kOsPrefName, &observer);
}
TEST_F(PrefServiceSyncableChromeOsTest, SyncedPrefObserver_OsPriorityPref) {
CreatePrefService();
InitSyncForAllTypes();
TestSyncedPrefObserver observer;
prefs_->AddSyncedPrefObserver(kOsPriorityPrefName, &observer);
prefs_->SetString(kOsPriorityPrefName, "value");
EXPECT_EQ(kOsPriorityPrefName, observer.last_pref_);
EXPECT_EQ(1, observer.changed_count_);
prefs_->RemoveSyncedPrefObserver(kOsPriorityPrefName, &observer);
}
TEST_F(PrefServiceSyncableChromeOsTest,
UpdatesFromOldClientsAreIgnored_Startup) {
CreatePrefService();
TestSyncedPrefObserver observer;
prefs_->AddSyncedPrefObserver(kOsPrefName, &observer);
syncer::SyncDataList list;
list.push_back(CreateRemoteSyncData(kOsPrefName, base::Value("new_value")));
auto* browser_associator = static_cast<PrefModelAssociator*>(
prefs_->GetSyncableService(syncer::PREFERENCES));
syncer::SyncChangeList outgoing_changes;
browser_associator->MergeDataAndStartSyncing(
syncer::PREFERENCES, list,
std::make_unique<TestSyncProcessorStub>(&outgoing_changes));
EXPECT_TRUE(outgoing_changes.empty());
EXPECT_NE("new_value", prefs_->GetString(kOsPrefName));
EXPECT_FALSE(browser_associator->IsPrefSyncedForTesting(kOsPrefName));
EXPECT_EQ(0, observer.changed_count_);
prefs_->RemoveSyncedPrefObserver(kOsPrefName, &observer);
}
TEST_F(PrefServiceSyncableChromeOsTest,
UpdatesFromOldClientsAreIgnored_Update) {
CreatePrefService();
InitSyncForAllTypes();
TestSyncedPrefObserver observer;
prefs_->AddSyncedPrefObserver(kOsPrefName, &observer);
syncer::SyncChangeList list;
list.push_back(MakeRemoteChange(kOsPrefName, base::Value("new_value"),
SyncChange::ACTION_ADD, syncer::PREFERENCES));
prefs_->GetSyncableService(syncer::PREFERENCES)
->ProcessSyncChanges(FROM_HERE, list);
EXPECT_NE("new_value", prefs_->GetString(kOsPrefName));
EXPECT_EQ(0, observer.changed_count_);
prefs_->RemoveSyncedPrefObserver(kOsPrefName, &observer);
}
TEST_F(PrefServiceSyncableChromeOsTest,
SyncedPrefObserver_OsPrefIsChangedFromSync) {
CreatePrefService();
prefs_->SetString(kOsPrefName, "default_value");
TestSyncedPrefObserver observer;
prefs_->AddSyncedPrefObserver(kOsPrefName, &observer);
TestPrefServiceSyncableObserver pref_service_sync_observer;
pref_service_sync_observer.SetSyncedPrefObserver(&observer);
prefs_->AddObserver(&pref_service_sync_observer);
syncer::SyncDataList list;
list.push_back(MakeRemoteSyncData(kOsPrefName, base::Value("new_value"),
syncer::OS_PREFERENCES));
syncer::SyncChangeList outgoing_changes;
prefs_->GetSyncableService(syncer::OS_PREFERENCES)
->MergeDataAndStartSyncing(
syncer::OS_PREFERENCES, list,
std::make_unique<TestSyncProcessorStub>(&outgoing_changes));
EXPECT_EQ(kOsPrefName, observer.synced_pref_);
EXPECT_EQ(1, observer.sync_started_count_);
EXPECT_TRUE(pref_service_sync_observer.is_syncing_changed());
prefs_->RemoveObserver(&pref_service_sync_observer);
prefs_->RemoveSyncedPrefObserver(kOsPrefName, &observer);
}
TEST_F(PrefServiceSyncableChromeOsTest,
SyncedPrefObserver_OsPrefIsNotChangedFromSync) {
CreatePrefService();
prefs_->SetString(kOsPrefName, "default_value");
TestSyncedPrefObserver observer;
prefs_->AddSyncedPrefObserver(kOsPrefName, &observer);
TestPrefServiceSyncableObserver pref_service_sync_observer;
pref_service_sync_observer.SetSyncedPrefObserver(&observer);
prefs_->AddObserver(&pref_service_sync_observer);
syncer::SyncDataList list;
list.push_back(MakeRemoteSyncData(kOsPrefName, base::Value("new_value"),
syncer::OS_PREFERENCES));
syncer::SyncChangeList outgoing_changes;
prefs_->GetSyncableService(syncer::OS_PREFERENCES)
->MergeDataAndStartSyncing(
syncer::OS_PREFERENCES, list,
std::make_unique<TestSyncProcessorStub>(&outgoing_changes));
EXPECT_EQ(kOsPrefName, observer.synced_pref_);
EXPECT_EQ(1, observer.sync_started_count_);
EXPECT_TRUE(pref_service_sync_observer.is_syncing_changed());
prefs_->RemoveObserver(&pref_service_sync_observer);
prefs_->RemoveSyncedPrefObserver(kOsPrefName, &observer);
}
TEST_F(PrefServiceSyncableChromeOsTest, SyncedPrefObserver_EmptyCloud) {
CreatePrefService();
prefs_->SetString(kOsPrefName, "new_value");
TestSyncedPrefObserver observer;
prefs_->AddSyncedPrefObserver(kOsPrefName, &observer);
syncer::SyncChangeList outgoing_changes;
prefs_->GetSyncableService(syncer::OS_PREFERENCES)
->MergeDataAndStartSyncing(
syncer::OS_PREFERENCES, syncer::SyncDataList(),
std::make_unique<TestSyncProcessorStub>(&outgoing_changes));
EXPECT_EQ("", observer.synced_pref_);
EXPECT_EQ(0, observer.sync_started_count_);
prefs_->RemoveSyncedPrefObserver(kOsPrefName, &observer);
}
TEST_F(PrefServiceSyncableChromeOsTest,
StandaloneBrowserPrefsNotLeakedInIncognito) {
CreatePrefService();
prefs_->SetStandaloneBrowserPref(kOsPrefName, base::Value("test_value"));
scoped_refptr<TestingPrefStore> incognito_extension_pref_store =
base::MakeRefCounted<TestingPrefStore>();
std::unique_ptr<PrefServiceSyncable> incognito_prefs =
prefs_->CreateIncognitoPrefService(incognito_extension_pref_store.get(),
{});
{
const PrefService::Preference* main_profile_pref =
prefs_->FindPreference(kOsPrefName);
ASSERT_TRUE(main_profile_pref);
EXPECT_TRUE(main_profile_pref->IsStandaloneBrowserControlled());
EXPECT_EQ(*main_profile_pref->GetValue(), base::Value("test_value"));
}
{
const PrefService::Preference* incognito_pref =
incognito_prefs->FindPreference(kOsPrefName);
ASSERT_TRUE(incognito_pref);
EXPECT_FALSE(incognito_pref->IsStandaloneBrowserControlled());
EXPECT_EQ(*incognito_pref->GetValue(), base::Value(""));
}
incognito_prefs->SetStandaloneBrowserPref(kOsPrefName,
base::Value("test_value"));
{
const PrefService::Preference* incognito_pref =
incognito_prefs->FindPreference(kOsPrefName);
ASSERT_TRUE(incognito_pref);
EXPECT_FALSE(incognito_pref->IsStandaloneBrowserControlled());
EXPECT_EQ(*incognito_pref->GetValue(), base::Value(""));
}
}
#endif
class PrefServiceSyncableFactoryTest : public PrefServiceSyncableTest { … };
TEST_F(PrefServiceSyncableFactoryTest,
ShouldCreateSyncServiceWithoutDualLayerStoreIfFeatureDisabled) { … }
TEST_F(PrefServiceSyncableFactoryTest,
ShouldCreateSyncServiceWithDualLayerStoreIfFeatureEnabled) { … }
}
}