blob: 6d31d69abdf6b6998eed4d7271a818591b2412df [file] [log] [blame]
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/sync_preferences/pref_model_associator.h"
#include <memory>
#include <unordered_set>
#include "base/json/json_string_value_serializer.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/gtest_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "components/prefs/mock_pref_change_callback.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/prefs/testing_pref_store.h"
#include "components/signin/public/base/signin_switches.h"
#include "components/sync/base/features.h"
#include "components/sync/protocol/entity_specifics.pb.h"
#include "components/sync/protocol/preference_specifics.pb.h"
#include "components/sync/test/fake_sync_change_processor.h"
#include "components/sync_preferences/pref_model_associator_client.h"
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "components/sync_preferences/preferences_merge_helper.h"
#include "components/sync_preferences/syncable_prefs_database.h"
#include "components/sync_preferences/test_syncable_prefs_database.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace sync_preferences {
namespace {
using testing::NotNull;
const char kStringPrefName[] = "pref.string";
const char kListPrefName[] = "pref.list";
const char kDictionaryPrefName[] = "pref.dictionary";
const char kCustomMergePrefName[] = "pref.custom";
const char kStringPriorityPrefName[] = "priority.pref.string";
#if BUILDFLAG(IS_CHROMEOS)
const char kStringOsPrefName[] = "os.pref.string";
const char kStringOsPriorityPrefName[] = "os.priority.pref.string";
#endif // BUILDFLAG(IS_CHROMEOS)
// Assigning an id of 0 to all the test prefs.
const TestSyncablePrefsDatabase::PrefsMap kSyncablePrefsDatabase = {
{kStringPrefName,
{0, syncer::PREFERENCES, PrefSensitivity::kNone, MergeBehavior::kNone}},
{kListPrefName,
{0, syncer::PREFERENCES, PrefSensitivity::kNone,
MergeBehavior::kMergeableListWithRewriteOnUpdate}},
{kDictionaryPrefName,
{0, syncer::PREFERENCES, PrefSensitivity::kNone,
MergeBehavior::kMergeableDict}},
{kCustomMergePrefName,
{0, syncer::PREFERENCES, PrefSensitivity::kNone, MergeBehavior::kCustom}},
{kStringPriorityPrefName,
{0, syncer::PRIORITY_PREFERENCES, PrefSensitivity::kNone,
MergeBehavior::kNone}},
#if BUILDFLAG(IS_CHROMEOS)
{kStringOsPrefName,
{0, syncer::OS_PREFERENCES, PrefSensitivity::kNone, MergeBehavior::kNone}},
{kStringOsPriorityPrefName,
{0, syncer::OS_PRIORITY_PREFERENCES, PrefSensitivity::kNone,
MergeBehavior::kNone}},
#endif // BUILDFLAG(IS_CHROMEOS)
};
// Creates SyncData for a remote pref change.
syncer::SyncData CreateRemoteSyncData(const std::string& name,
base::ValueView value) {
std::string serialized;
JSONStringValueSerializer json(&serialized);
EXPECT_TRUE(json.Serialize(value));
sync_pb::EntitySpecifics specifics;
sync_pb::PreferenceSpecifics* pref_specifics = specifics.mutable_preference();
pref_specifics->set_name(name);
pref_specifics->set_value(serialized);
return syncer::SyncData::CreateRemoteData(
specifics,
syncer::ClientTagHash::FromUnhashed(syncer::DataType::PREFERENCES, name));
}
class TestPrefModelAssociatorClient : public PrefModelAssociatorClient {
public:
TestPrefModelAssociatorClient()
: syncable_prefs_database_(kSyncablePrefsDatabase) {}
TestPrefModelAssociatorClient(const TestPrefModelAssociatorClient&) = delete;
TestPrefModelAssociatorClient& operator=(
const TestPrefModelAssociatorClient&) = delete;
// PrefModelAssociatorClient implementation.
base::Value MaybeMergePreferenceValues(
std::string_view pref_name,
const base::Value& local_value,
const base::Value& server_value) const override {
if (pref_name == kCustomMergePrefName) {
return local_value.Clone();
}
return base::Value();
}
private:
~TestPrefModelAssociatorClient() override = default;
const SyncablePrefsDatabase& GetSyncablePrefsDatabase() const override {
return syncable_prefs_database_;
}
TestSyncablePrefsDatabase syncable_prefs_database_;
};
scoped_refptr<user_prefs::PrefRegistrySyncable> CreatePrefRegistry() {
scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry =
base::MakeRefCounted<user_prefs::PrefRegistrySyncable>();
pref_registry->RegisterStringPref(
kStringPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
pref_registry->RegisterListPref(
kListPrefName, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
pref_registry->RegisterDictionaryPref(
kDictionaryPrefName, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
pref_registry->RegisterStringPref(
kCustomMergePrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
return pref_registry;
}
std::unique_ptr<PrefServiceSyncable> CreatePrefService(
scoped_refptr<PrefModelAssociatorClient> client,
scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry,
scoped_refptr<PersistentPrefStore> user_prefs) {
PrefServiceMockFactory factory;
factory.SetPrefModelAssociatorClient(client);
factory.set_user_prefs(user_prefs);
return factory.CreateSyncable(pref_registry.get());
}
class AbstractPreferenceMergeTest : public testing::Test {
protected:
AbstractPreferenceMergeTest() = default;
void SetContentPattern(base::Value::Dict& patterns_dict,
const std::string& expression,
int setting) {
base::Value::Dict* expression_dict = patterns_dict.EnsureDict(expression);
expression_dict->Set("setting", setting);
}
void SetPrefToEmpty(const std::string& pref_name) {
std::unique_ptr<base::Value> empty_value;
const PrefService::Preference* pref =
pref_service_->FindPreference(pref_name);
ASSERT_TRUE(pref);
base::Value::Type type = pref->GetType();
if (type == base::Value::Type::DICT) {
pref_service_->SetDict(pref_name, base::Value::Dict());
} else if (type == base::Value::Type::LIST) {
pref_service_->SetList(pref_name, base::Value::List());
} else {
FAIL();
}
}
const scoped_refptr<TestPrefModelAssociatorClient> client_ =
base::MakeRefCounted<TestPrefModelAssociatorClient>();
const scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry_ =
CreatePrefRegistry();
const scoped_refptr<TestingPrefStore> user_prefs_ =
base::MakeRefCounted<TestingPrefStore>();
const std::unique_ptr<PrefServiceSyncable> pref_service_ =
CreatePrefService(client_, pref_registry_, user_prefs_);
const raw_ptr<PrefModelAssociator> pref_sync_service_ =
static_cast<PrefModelAssociator*>(
pref_service_->GetSyncableService(syncer::PREFERENCES));
};
using CustomPreferenceMergeTest = AbstractPreferenceMergeTest;
TEST_F(CustomPreferenceMergeTest, ClientMergesCustomPreference) {
pref_service_->SetString(kCustomMergePrefName, "local");
const PrefService::Preference* pref =
pref_service_->FindPreference(kCustomMergePrefName);
base::Value local_value(pref->GetValue()->Clone());
base::Value server_value("server");
base::Value merged_value(helper::MergePreference(
client_.get(), pref->name(), *pref->GetValue(), server_value));
// TestPrefModelAssociatorClient should have chosen local value instead of the
// default server value.
EXPECT_EQ(merged_value, local_value);
}
class ListPreferenceMergeTest : public AbstractPreferenceMergeTest {
protected:
ListPreferenceMergeTest()
: server_url0_("http://example.com/server0"),
server_url1_("http://example.com/server1"),
local_url0_("http://example.com/local0"),
local_url1_("http://example.com/local1") {
server_url_list_.Append(server_url0_);
server_url_list_.Append(server_url1_);
}
std::string server_url0_;
std::string server_url1_;
std::string local_url0_;
std::string local_url1_;
base::Value::List server_url_list_;
};
TEST_F(ListPreferenceMergeTest, NotListOrDictionary) {
pref_service_->SetString(kStringPrefName, local_url0_);
const PrefService::Preference* pref =
pref_service_->FindPreference(kStringPrefName);
base::Value server_value(server_url0_);
base::Value merged_value(helper::MergePreference(
client_.get(), pref->name(), *pref->GetValue(), server_value));
EXPECT_EQ(merged_value, server_value);
}
TEST_F(ListPreferenceMergeTest, LocalEmpty) {
SetPrefToEmpty(kListPrefName);
const PrefService::Preference* pref =
pref_service_->FindPreference(kListPrefName);
base::Value merged_value(
helper::MergePreference(client_.get(), pref->name(), *pref->GetValue(),
base::Value(server_url_list_.Clone())));
EXPECT_EQ(merged_value, server_url_list_);
}
TEST_F(ListPreferenceMergeTest, ServerNull) {
{
ScopedListPrefUpdate update(pref_service_.get(), kListPrefName);
update->Append(local_url0_);
}
const PrefService::Preference* pref =
pref_service_->FindPreference(kListPrefName);
base::Value merged_value(helper::MergePreference(
client_.get(), pref->name(), *pref->GetValue(), base::Value()));
const base::Value::List& local_list_value =
pref_service_->GetList(kListPrefName);
EXPECT_EQ(merged_value, local_list_value);
}
TEST_F(ListPreferenceMergeTest, ServerEmpty) {
base::Value::List empty_value;
{
ScopedListPrefUpdate update(pref_service_.get(), kListPrefName);
update->Append(local_url0_);
}
const PrefService::Preference* pref =
pref_service_->FindPreference(kListPrefName);
base::Value merged_value(
helper::MergePreference(client_.get(), pref->name(), *pref->GetValue(),
base::Value(empty_value.Clone())));
const base::Value::List& local_list_value =
pref_service_->GetList(kListPrefName);
EXPECT_EQ(merged_value, local_list_value);
}
TEST_F(ListPreferenceMergeTest, ServerCorrupt) {
{
ScopedListPrefUpdate update(pref_service_.get(), kListPrefName);
update->Append(local_url0_);
}
const PrefService::Preference* pref =
pref_service_->FindPreference(kListPrefName);
base::Value merged_value(
helper::MergePreference(client_.get(), pref->name(), *pref->GetValue(),
base::Value("corrupt-type")));
const base::Value::List& local_list_value =
pref_service_->GetList(kListPrefName);
EXPECT_EQ(merged_value, local_list_value);
}
TEST_F(ListPreferenceMergeTest, Merge) {
{
ScopedListPrefUpdate update(pref_service_.get(), kListPrefName);
update->Append(local_url0_);
update->Append(local_url1_);
}
const PrefService::Preference* pref =
pref_service_->FindPreference(kListPrefName);
base::Value merged_value(
helper::MergePreference(client_.get(), pref->name(), *pref->GetValue(),
base::Value(server_url_list_.Clone())));
auto expected = base::Value::List()
.Append(server_url0_)
.Append(server_url1_)
.Append(local_url0_)
.Append(local_url1_);
EXPECT_EQ(merged_value, expected);
}
TEST_F(ListPreferenceMergeTest, Duplicates) {
{
ScopedListPrefUpdate update(pref_service_.get(), kListPrefName);
update->Append(local_url0_);
update->Append(server_url0_);
update->Append(server_url1_);
}
const PrefService::Preference* pref =
pref_service_->FindPreference(kListPrefName);
base::Value merged_value(
helper::MergePreference(client_.get(), pref->name(), *pref->GetValue(),
base::Value(server_url_list_.Clone())));
auto expected = base::Value::List()
.Append(server_url0_)
.Append(server_url1_)
.Append(local_url0_);
EXPECT_EQ(merged_value, expected);
}
TEST_F(ListPreferenceMergeTest, Equals) {
{
ScopedListPrefUpdate update(pref_service_.get(), kListPrefName);
update->Append(server_url0_);
update->Append(server_url1_);
}
base::Value::List original = server_url_list_.Clone();
const PrefService::Preference* pref =
pref_service_->FindPreference(kListPrefName);
base::Value merged_value(
helper::MergePreference(client_.get(), pref->name(), *pref->GetValue(),
base::Value(server_url_list_.Clone())));
EXPECT_EQ(merged_value, original);
}
class DictionaryPreferenceMergeTest : public AbstractPreferenceMergeTest {
protected:
DictionaryPreferenceMergeTest()
: expression0_("expression0"),
expression1_("expression1"),
expression2_("expression2"),
expression3_("expression3"),
expression4_("expression4") {
SetContentPattern(server_patterns_.GetDict(), expression0_, 1);
SetContentPattern(server_patterns_.GetDict(), expression1_, 2);
SetContentPattern(server_patterns_.GetDict(), expression2_, 1);
}
std::string expression0_;
std::string expression1_;
std::string expression2_;
std::string expression3_;
std::string expression4_;
base::Value server_patterns_{base::Value::Type::DICT};
};
TEST_F(DictionaryPreferenceMergeTest, LocalEmpty) {
SetPrefToEmpty(kDictionaryPrefName);
const PrefService::Preference* pref =
pref_service_->FindPreference(kDictionaryPrefName);
base::Value merged_value(helper::MergePreference(
client_.get(), pref->name(), *pref->GetValue(), server_patterns_));
EXPECT_EQ(merged_value, server_patterns_);
}
TEST_F(DictionaryPreferenceMergeTest, ServerNull) {
{
ScopedDictPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
SetContentPattern(*update, expression3_, 1);
}
const PrefService::Preference* pref =
pref_service_->FindPreference(kDictionaryPrefName);
base::Value merged_value(helper::MergePreference(
client_.get(), pref->name(), *pref->GetValue(), base::Value()));
const base::Value::Dict& local_dict_value =
pref_service_->GetDict(kDictionaryPrefName);
EXPECT_EQ(merged_value, local_dict_value);
}
TEST_F(DictionaryPreferenceMergeTest, ServerEmpty) {
{
ScopedDictPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
SetContentPattern(*update, expression3_, 1);
}
const PrefService::Preference* pref =
pref_service_->FindPreference(kDictionaryPrefName);
base::Value merged_value(helper::MergePreference(
client_.get(), pref->name(), *pref->GetValue(), base::Value()));
const base::Value::Dict& local_dict_value =
pref_service_->GetDict(kDictionaryPrefName);
EXPECT_EQ(merged_value, local_dict_value);
}
TEST_F(DictionaryPreferenceMergeTest, ServerCorrupt) {
{
ScopedDictPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
SetContentPattern(*update, expression3_, 1);
}
const PrefService::Preference* pref =
pref_service_->FindPreference(kDictionaryPrefName);
base::Value merged_value(
helper::MergePreference(client_.get(), pref->name(), *pref->GetValue(),
base::Value("corrupt-type")));
const base::Value::Dict& local_dict_value =
pref_service_->GetDict(kDictionaryPrefName);
EXPECT_EQ(merged_value, local_dict_value);
}
TEST_F(DictionaryPreferenceMergeTest, MergeNoConflicts) {
{
ScopedDictPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
SetContentPattern(*update, expression3_, 1);
}
base::Value merged_value(helper::MergePreference(
client_.get(), kDictionaryPrefName,
*pref_service_->FindPreference(kDictionaryPrefName)->GetValue(),
server_patterns_));
base::Value::Dict expected;
SetContentPattern(expected, expression0_, 1);
SetContentPattern(expected, expression1_, 2);
SetContentPattern(expected, expression2_, 1);
SetContentPattern(expected, expression3_, 1);
EXPECT_EQ(merged_value, expected);
}
TEST_F(DictionaryPreferenceMergeTest, MergeConflicts) {
{
ScopedDictPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
SetContentPattern(*update, expression0_, 2);
SetContentPattern(*update, expression2_, 1);
SetContentPattern(*update, expression3_, 1);
SetContentPattern(*update, expression4_, 2);
}
base::Value merged_value(helper::MergePreference(
client_.get(), kDictionaryPrefName,
*pref_service_->FindPreference(kDictionaryPrefName)->GetValue(),
server_patterns_));
base::Value::Dict expected;
SetContentPattern(expected, expression0_, 1);
SetContentPattern(expected, expression1_, 2);
SetContentPattern(expected, expression2_, 1);
SetContentPattern(expected, expression3_, 1);
SetContentPattern(expected, expression4_, 2);
EXPECT_EQ(merged_value, expected);
}
TEST_F(DictionaryPreferenceMergeTest, MergeValueToDictionary) {
base::Value::Dict local_dict_value;
local_dict_value.Set("key", 0);
base::Value::Dict server_dict_value;
server_dict_value.SetByDottedPath("key.subkey", 0);
// TODO(crbug.com/40754070): Migrate MergePreference() to
// take a base::Value::Dict.
base::Value merged_value(helper::MergePreference(
client_.get(), kDictionaryPrefName, base::Value(local_dict_value.Clone()),
base::Value(server_dict_value.Clone())));
EXPECT_EQ(merged_value, server_dict_value);
}
TEST_F(DictionaryPreferenceMergeTest, Equal) {
{
ScopedDictPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
SetContentPattern(*update, expression0_, 1);
SetContentPattern(*update, expression1_, 2);
SetContentPattern(*update, expression2_, 1);
}
base::Value merged_value(helper::MergePreference(
client_.get(), kDictionaryPrefName,
*pref_service_->FindPreference(kDictionaryPrefName)->GetValue(),
server_patterns_));
EXPECT_EQ(merged_value, server_patterns_);
}
TEST_F(DictionaryPreferenceMergeTest, ConflictButServerWins) {
{
ScopedDictPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
SetContentPattern(*update, expression0_, 2);
SetContentPattern(*update, expression1_, 2);
SetContentPattern(*update, expression2_, 1);
}
base::Value merged_value(helper::MergePreference(
client_.get(), kDictionaryPrefName,
*pref_service_->FindPreference(kDictionaryPrefName)->GetValue(),
server_patterns_));
EXPECT_EQ(merged_value, server_patterns_);
}
class IndividualPreferenceMergeTest : public AbstractPreferenceMergeTest {
protected:
IndividualPreferenceMergeTest()
: url0_("http://example.com/server0"),
url1_("http://example.com/server1"),
expression0_("expression0"),
expression1_("expression1") {
server_url_list_.Append(url0_);
SetContentPattern(server_patterns_.GetDict(), expression0_, 1);
}
bool MergeListPreference(const char* pref) {
{
ScopedListPrefUpdate update(pref_service_.get(), pref);
update->Append(url1_);
}
base::Value merged_value(helper::MergePreference(
client_.get(), pref, *pref_service_->GetUserPrefValue(pref),
base::Value(server_url_list_.Clone())));
auto expected = base::Value::List().Append(url0_).Append(url1_);
return merged_value == expected;
}
bool MergeDictionaryPreference(const char* pref) {
{
ScopedDictPrefUpdate update(pref_service_.get(), pref);
SetContentPattern(*update, expression1_, 1);
}
base::Value merged_value(helper::MergePreference(
client_.get(), pref, *pref_service_->GetUserPrefValue(pref),
server_patterns_));
base::Value::Dict expected;
SetContentPattern(expected, expression0_, 1);
SetContentPattern(expected, expression1_, 1);
return merged_value == expected;
}
std::string url0_;
std::string url1_;
std::string expression0_;
std::string expression1_;
std::string content_type0_;
base::Value::List server_url_list_;
base::Value server_patterns_{base::Value::Type::DICT};
};
TEST_F(IndividualPreferenceMergeTest, ListPreference) {
EXPECT_TRUE(MergeListPreference(kListPrefName));
}
class SyncablePrefsDatabaseTest : public testing::Test {
protected:
SyncablePrefsDatabaseTest()
: client_(base::MakeRefCounted<TestPrefModelAssociatorClient>()),
pref_registry_(
base::MakeRefCounted<user_prefs::PrefRegistrySyncable>()) {
PrefServiceMockFactory factory;
factory.SetPrefModelAssociatorClient(client_);
pref_service_ = factory.CreateSyncable(pref_registry_.get());
}
scoped_refptr<TestPrefModelAssociatorClient> client_;
scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry_;
std::unique_ptr<PrefServiceSyncable> pref_service_;
};
TEST_F(SyncablePrefsDatabaseTest, ShouldAllowRegisteringSyncablePrefs) {
pref_registry_->RegisterStringPref(
kStringPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
EXPECT_THAT(pref_service_->FindPreference(kStringPrefName), NotNull());
pref_registry_->RegisterListPref(
kListPrefName, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
EXPECT_THAT(pref_service_->FindPreference(kListPrefName), NotNull());
pref_registry_->RegisterDictionaryPref(
kDictionaryPrefName, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
EXPECT_THAT(pref_service_->FindPreference(kDictionaryPrefName), NotNull());
}
TEST_F(SyncablePrefsDatabaseTest, ShouldAllowRegisteringSyncablePriorityPrefs) {
pref_registry_->RegisterStringPref(
kStringPriorityPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
EXPECT_THAT(pref_service_->FindPreference(kStringPriorityPrefName),
NotNull());
}
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(SyncablePrefsDatabaseTest, ShouldAllowRegisteringSyncableOSPrefs) {
pref_registry_->RegisterStringPref(
kStringOsPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
EXPECT_THAT(pref_service_->FindPreference(kStringOsPrefName), NotNull());
}
TEST_F(SyncablePrefsDatabaseTest,
ShouldAllowRegisteringSyncableOSPriorityPrefs) {
pref_registry_->RegisterStringPref(
kStringOsPriorityPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PRIORITY_PREF);
EXPECT_THAT(pref_service_->FindPreference(kStringOsPriorityPrefName),
NotNull());
}
#endif
using SyncablePrefsDatabaseDeathTest = SyncablePrefsDatabaseTest;
TEST_F(SyncablePrefsDatabaseDeathTest, ShouldFailRegisteringIllegalPrefs) {
const std::string kIllegalStringPrefName = "not-allowed_string_pref";
const std::string kIllegalListPrefName = "not-allowed_list_pref";
const std::string kIllegalDictPrefName = "not-allowed_dict_pref";
const std::string kExpectedErrorMessageHint = "syncable prefs allowlist";
EXPECT_DCHECK_DEATH_WITH(pref_registry_->RegisterStringPref(
kIllegalStringPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF),
kExpectedErrorMessageHint);
EXPECT_DCHECK_DEATH_WITH(pref_registry_->RegisterListPref(
kIllegalListPrefName,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF),
kExpectedErrorMessageHint);
EXPECT_DCHECK_DEATH_WITH(pref_registry_->RegisterDictionaryPref(
kIllegalDictPrefName,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF),
kExpectedErrorMessageHint);
}
TEST_F(SyncablePrefsDatabaseDeathTest,
ShouldFailRegisteringIllegalPriorityPrefs) {
const std::string kIllegalStringPrefName = "not-allowed_string_pref";
const std::string kIllegalListPrefName = "not-allowed_list_pref";
const std::string kIllegalDictPrefName = "not-allowed_dict_pref";
const std::string kExpectedErrorMessageHint = "syncable prefs allowlist";
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterStringPref(
kIllegalStringPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF),
kExpectedErrorMessageHint);
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterListPref(
kIllegalListPrefName,
user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF),
kExpectedErrorMessageHint);
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterDictionaryPref(
kIllegalDictPrefName,
user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF),
kExpectedErrorMessageHint);
}
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(SyncablePrefsDatabaseDeathTest, ShouldFailRegisteringIllegalOSPrefs) {
const std::string kIllegalStringPrefName = "not-allowed_string_pref";
const std::string kIllegalListPrefName = "not-allowed_list_pref";
const std::string kIllegalDictPrefName = "not-allowed_dict_pref";
const std::string kExpectedErrorMessageHint = "syncable prefs allowlist";
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterStringPref(
kIllegalStringPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF),
kExpectedErrorMessageHint);
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterListPref(
kIllegalListPrefName,
user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF),
kExpectedErrorMessageHint);
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterDictionaryPref(
kIllegalDictPrefName,
user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF),
kExpectedErrorMessageHint);
}
TEST_F(SyncablePrefsDatabaseDeathTest,
ShouldFailRegisteringIllegalOSPriorityPrefs) {
const std::string kIllegalStringPrefName = "not-allowed_string_pref";
const std::string kIllegalListPrefName = "not-allowed_list_pref";
const std::string kIllegalDictPrefName = "not-allowed_dict_pref";
const std::string kExpectedErrorMessageHint = "syncable prefs allowlist";
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterStringPref(
kIllegalStringPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PRIORITY_PREF),
kExpectedErrorMessageHint);
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterListPref(
kIllegalListPrefName,
user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PRIORITY_PREF),
kExpectedErrorMessageHint);
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterDictionaryPref(
kIllegalDictPrefName,
user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PRIORITY_PREF),
kExpectedErrorMessageHint);
}
#endif
TEST_F(SyncablePrefsDatabaseDeathTest,
ShouldFailRegisteringPrefsWithInvalidType) {
const std::string kExpectedErrorMessageHint = "has incorrect data";
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterStringPref(
kStringPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF),
kExpectedErrorMessageHint);
EXPECT_DCHECK_DEATH_WITH(pref_registry_->RegisterStringPref(
kStringPriorityPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF),
kExpectedErrorMessageHint);
#if BUILDFLAG(IS_CHROMEOS)
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterStringPref(
kStringOsPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PRIORITY_PREF),
kExpectedErrorMessageHint);
EXPECT_DCHECK_DEATH_WITH(
pref_registry_->RegisterStringPref(
kStringOsPriorityPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF),
kExpectedErrorMessageHint);
#endif
}
class PrefModelAssociatorWithPreferencesAccountStorageTest
: public testing::Test {
protected:
PrefModelAssociatorWithPreferencesAccountStorageTest()
: feature_list_(switches::kEnablePreferencesAccountStorage),
client_(base::MakeRefCounted<TestPrefModelAssociatorClient>()),
pref_registry_(
base::MakeRefCounted<user_prefs::PrefRegistrySyncable>()),
local_pref_store_(base::MakeRefCounted<TestingPrefStore>()),
account_pref_store_(base::MakeRefCounted<TestingPrefStore>()) {
pref_registry_->RegisterStringPref(
kStringPrefName, std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
PrefServiceMockFactory factory;
factory.SetPrefModelAssociatorClient(client_);
factory.set_user_prefs(local_pref_store_);
factory.SetAccountPrefStore(account_pref_store_);
pref_service_ = factory.CreateSyncable(pref_registry_);
pref_model_associator_ = static_cast<PrefModelAssociator*>(
pref_service_->GetSyncableService(syncer::PREFERENCES));
}
void MergeDataAndStartSyncing(const syncer::SyncDataList& initial_data) {
auto error = pref_model_associator_->MergeDataAndStartSyncing(
syncer::PREFERENCES, initial_data,
std::make_unique<syncer::FakeSyncChangeProcessor>());
EXPECT_FALSE(error.has_value());
}
base::test::ScopedFeatureList feature_list_;
scoped_refptr<TestPrefModelAssociatorClient> client_;
scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry_;
scoped_refptr<TestingPrefStore> local_pref_store_;
scoped_refptr<TestingPrefStore> account_pref_store_;
std::unique_ptr<PrefServiceSyncable> pref_service_;
raw_ptr<PrefModelAssociator> pref_model_associator_ = nullptr;
};
// Tests that no notification is issued if the effective value is unchanged upon
// initial merge.
TEST_F(PrefModelAssociatorWithPreferencesAccountStorageTest,
ShouldNotNotifyUponInitIfSameValueExistsInLocalStoreOnly) {
// Load value to local store before initial merge.
local_pref_store_->SetValue(kStringPrefName, base::Value("value"), 0);
ASSERT_EQ(pref_service_->GetString(kStringPrefName), "value");
// Listen to pref changes.
MockPrefChangeCallback observer(pref_service_.get());
PrefChangeRegistrar registrar;
registrar.Init(pref_service_.get());
registrar.Add(kStringPrefName, observer.GetCallback());
// No call should be made to the observer as the effective value of the pref
// is unchanged.
EXPECT_CALL(observer, OnPreferenceChanged).Times(0);
// Create initial sync data with the same pref value as that in the local
// store.
syncer::SyncDataList initial_data;
initial_data.push_back(
CreateRemoteSyncData(kStringPrefName, base::Value("value")));
MergeDataAndStartSyncing(initial_data);
}
// Tests that no notification is issued if the effective value is unchanged upon
// initial merge.
TEST_F(PrefModelAssociatorWithPreferencesAccountStorageTest,
ShouldNotNotifyUponInitIfSameValueExistsInAccountStoreOnly) {
// Load value to account store before initial merge.
account_pref_store_->SetValue(kStringPrefName, base::Value("value"), 0);
ASSERT_EQ(pref_service_->GetString(kStringPrefName), "value");
// Listen to pref changes.
MockPrefChangeCallback observer(pref_service_.get());
PrefChangeRegistrar registrar;
registrar.Init(pref_service_.get());
registrar.Add(kStringPrefName, observer.GetCallback());
// No call should be made to the observer as the effective value of the pref
// is unchanged.
EXPECT_CALL(observer, OnPreferenceChanged).Times(0);
// Create initial sync data with the same pref value as that in the local
// store.
syncer::SyncDataList initial_data;
initial_data.push_back(
CreateRemoteSyncData(kStringPrefName, base::Value("value")));
MergeDataAndStartSyncing(initial_data);
}
// Tests that notification is issued if the effective value changes upon
// initial merge.
TEST_F(PrefModelAssociatorWithPreferencesAccountStorageTest,
ShouldNotifyUponInitIfDifferentValueExistsInLocalStoreOnly) {
// Load value to local store before initial merge.
local_pref_store_->SetValue(kStringPrefName, base::Value("value"), 0);
ASSERT_EQ(pref_service_->GetString(kStringPrefName), "value");
// Listen to pref changes.
MockPrefChangeCallback observer(pref_service_.get());
PrefChangeRegistrar registrar;
registrar.Init(pref_service_.get());
registrar.Add(kStringPrefName, observer.GetCallback());
// Observer should get notified since the effective value changes.
EXPECT_CALL(observer, OnPreferenceChanged);
// Create initial sync data with a different pref value than that in the
// local store.
syncer::SyncDataList initial_data;
initial_data.push_back(
CreateRemoteSyncData(kStringPrefName, base::Value("new value")));
MergeDataAndStartSyncing(initial_data);
ASSERT_EQ(pref_service_->GetString(kStringPrefName), "new value");
}
// Tests that notification is issued if the effective value changes upon
// initial merge.
TEST_F(PrefModelAssociatorWithPreferencesAccountStorageTest,
ShouldNotifyUponInitIfDifferentValueExistsInAccountStoreOnly) {
// Load value to account store before initial merge.
account_pref_store_->SetValue(kStringPrefName, base::Value("value"), 0);
ASSERT_EQ(pref_service_->GetString(kStringPrefName), "value");
// Listen to pref changes.
MockPrefChangeCallback observer(pref_service_.get());
PrefChangeRegistrar registrar;
registrar.Init(pref_service_.get());
registrar.Add(kStringPrefName, observer.GetCallback());
// Observer should get notified since the effective value changes.
EXPECT_CALL(observer, OnPreferenceChanged);
// Create initial sync data with a different pref value than that in the
// local store.
syncer::SyncDataList initial_data;
initial_data.push_back(
CreateRemoteSyncData(kStringPrefName, base::Value("new value")));
MergeDataAndStartSyncing(initial_data);
ASSERT_EQ(pref_service_->GetString(kStringPrefName), "new value");
}
} // namespace
} // namespace sync_preferences