blob: 58b362a67ceaaf65a2f72b34a5418a6ca71e2c82 [file]
// Copyright 2023 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/dual_layer_user_pref_store.h"
#include <map>
#include <set>
#include <string_view>
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_util.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/values.h"
#include "components/prefs/testing_pref_store.h"
#include "components/sync/base/features.h"
#include "components/sync/test/test_sync_service.h"
#include "components/sync_preferences/pref_model_associator_client.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 {
constexpr char kPref1[] = "regular.pref1";
constexpr char kPref2[] = "regular.pref2";
constexpr char kPref3[] = "regular.pref3";
constexpr char kPrefName[] = "regular.pref";
constexpr char kPriorityPrefName[] = "priority.pref";
constexpr char kNonExistentPrefName[] = "nonexistent-pref";
constexpr char kNonSyncablePrefName[] = "nonsyncable.pref";
constexpr char kHistorySensitivePrefName[] = "sensitive.pref";
constexpr char kMergeableListPref[] = "mergeable.list.pref";
constexpr char kMergeableDictPref1[] = "mergeable.dict.pref1";
constexpr char kMergeableDictPref2[] = "mergeable.dict.pref2";
constexpr char kCustomMergePref[] = "custom.merge.pref";
constexpr char kAlwaysSyncingPriorityPrefName[] =
"always.syncing.priority.pref";
#if BUILDFLAG(IS_CHROMEOS)
constexpr char kOsPrefName[] = "os.pref";
constexpr char kOsPriorityPrefName[] = "os.priority.pref";
#endif // BUILDFLAG(IS_CHROMEOS)
// Assigning an id of 0 to all the test prefs.
const TestSyncablePrefsDatabase::PrefsMap kSyncablePrefsDatabase = {
{kPref1,
{0, syncer::PREFERENCES, PrefSensitivity::kNone, MergeBehavior::kNone}},
{kPref2,
{0, syncer::PREFERENCES, PrefSensitivity::kNone, MergeBehavior::kNone}},
{kPref3,
{0, syncer::PREFERENCES, PrefSensitivity::kNone, MergeBehavior::kNone}},
{kPrefName,
{0, syncer::PREFERENCES, PrefSensitivity::kNone, MergeBehavior::kNone}},
{kPriorityPrefName,
{0, syncer::PRIORITY_PREFERENCES, PrefSensitivity::kNone,
MergeBehavior::kNone}},
{kHistorySensitivePrefName,
{0, syncer::PREFERENCES, PrefSensitivity::kSensitiveRequiresHistory,
MergeBehavior::kNone}},
{kMergeableListPref,
{0, syncer::PREFERENCES, PrefSensitivity::kNone,
MergeBehavior::kMergeableListWithRewriteOnUpdate}},
{kMergeableDictPref1,
{0, syncer::PREFERENCES, PrefSensitivity::kNone,
MergeBehavior::kMergeableDict}},
{kMergeableDictPref2,
{0, syncer::PREFERENCES, PrefSensitivity::kNone,
MergeBehavior::kMergeableDict}},
{kCustomMergePref,
{0, syncer::PREFERENCES, PrefSensitivity::kNone, MergeBehavior::kCustom}},
{kAlwaysSyncingPriorityPrefName,
{0, syncer::PRIORITY_PREFERENCES,
PrefSensitivity::kExemptFromUserControlWhileSignedIn,
MergeBehavior::kNone}},
#if BUILDFLAG(IS_CHROMEOS)
{kOsPrefName,
{0, syncer::OS_PREFERENCES, PrefSensitivity::kNone, MergeBehavior::kNone}},
{kOsPriorityPrefName,
{0, syncer::OS_PRIORITY_PREFERENCES, PrefSensitivity::kNone,
MergeBehavior::kNone}},
#endif // BUILDFLAG(IS_CHROMEOS)
};
constexpr char kUserSelectedTypesPrefName[] =
"dual_layer_user_pref_store.user_selected_sync_types";
#if BUILDFLAG(IS_CHROMEOS)
constexpr char kUserSelectedOsTypesPrefName[] =
"dual_layer_user_pref_store.user_selected_os_sync_types";
#endif // BUILDFLAG(IS_CHROMEOS)
base::Value MakeDict(
const std::vector<std::pair<std::string, std::string>>& values) {
base::Value::Dict dict;
for (const auto& [key, value] : values) {
dict.SetByDottedPath(key, value);
}
return base::Value(std::move(dict));
}
testing::AssertionResult ValueInStoreIs(const PrefStore& store,
const std::string& pref,
const base::Value& expected_value) {
const base::Value* actual_value = nullptr;
if (!store.GetValue(pref, &actual_value)) {
return testing::AssertionFailure() << "Pref " << pref << " isn't present";
}
DCHECK(actual_value);
if (expected_value != *actual_value) {
return testing::AssertionFailure()
<< "Pref " << pref << " has value " << *actual_value
<< " but was expected to be " << expected_value;
}
return testing::AssertionSuccess();
}
testing::AssertionResult ValueInStoreIs(const PrefStore& store,
const std::string& pref,
const std::string& expected_value) {
return ValueInStoreIs(store, pref, base::Value(expected_value));
}
testing::AssertionResult ValueInStoreIsAbsent(const PrefStore& store,
const std::string& pref) {
const base::Value* actual_value = nullptr;
if (store.GetValue(pref, &actual_value)) {
DCHECK(actual_value);
return testing::AssertionFailure()
<< "Pref " << pref << " should be absent, but exists with value "
<< *actual_value;
}
return testing::AssertionSuccess();
}
testing::AssertionResult ValueInDictByDottedPathIs(
const base::Value::Dict& dict,
const std::string& key,
const base::Value& expected_value) {
if (const base::Value* actual_value = dict.FindByDottedPath(key);
actual_value && *actual_value == expected_value) {
return testing::AssertionSuccess();
}
return testing::AssertionFailure();
}
class MockPrefStoreObserver : public PrefStore::Observer {
public:
~MockPrefStoreObserver() override = default;
MOCK_METHOD(void, OnPrefValueChanged, (std::string_view), (override));
MOCK_METHOD(void, OnInitializationCompleted, (bool succeeded), (override));
};
class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
public:
MOCK_METHOD(void, OnError, (PersistentPrefStore::PrefReadError), (override));
};
class TestPrefModelAssociatorClient : public PrefModelAssociatorClient {
public:
TestPrefModelAssociatorClient()
: syncable_prefs_database_(kSyncablePrefsDatabase) {}
// PrefModelAssociatorClient implementation.
base::Value MaybeMergePreferenceValues(
std::string_view pref_name,
const base::Value& local_value,
const base::Value& server_value) const override {
return base::Value();
}
const SyncablePrefsDatabase& GetSyncablePrefsDatabase() const override {
return syncable_prefs_database_;
}
private:
~TestPrefModelAssociatorClient() override = default;
TestSyncablePrefsDatabase syncable_prefs_database_;
};
class DualLayerUserPrefStoreTestBase : public testing::Test {
public:
explicit DualLayerUserPrefStoreTestBase(bool initialize) {
local_store_ = base::MakeRefCounted<TestingPrefStore>();
account_store_ = base::MakeRefCounted<TestingPrefStore>();
pref_model_associator_client_ =
base::MakeRefCounted<TestPrefModelAssociatorClient>();
dual_layer_store_ = base::MakeRefCounted<DualLayerUserPrefStore>(
local_store_, account_store_, pref_model_associator_client_);
if (initialize) {
local_store_->NotifyInitializationCompleted();
account_store_->NotifyInitializationCompleted();
}
// Set preferences user selected type by default.
dual_layer_store_->SetUserSelectedTypesForTest(
{syncer::UserSelectableType::kPreferences});
}
TestingPrefStore* local_store() { return local_store_.get(); }
TestingPrefStore* account_store() { return account_store_.get(); }
DualLayerUserPrefStore* store() { return dual_layer_store_.get(); }
protected:
scoped_refptr<TestingPrefStore> local_store_;
scoped_refptr<TestingPrefStore> account_store_;
scoped_refptr<TestPrefModelAssociatorClient> pref_model_associator_client_;
scoped_refptr<DualLayerUserPrefStore> dual_layer_store_;
};
class DualLayerUserPrefStoreTest : public DualLayerUserPrefStoreTestBase {
public:
DualLayerUserPrefStoreTest() : DualLayerUserPrefStoreTestBase(true) {
// TODO(crbug.com/40256875): Add proper test setup to enable and disable
// data types appropriately.
dual_layer_store_->EnableType(syncer::PREFERENCES);
dual_layer_store_->EnableType(syncer::PRIORITY_PREFERENCES);
#if BUILDFLAG(IS_CHROMEOS)
dual_layer_store_->EnableType(syncer::OS_PREFERENCES);
dual_layer_store_->EnableType(syncer::OS_PRIORITY_PREFERENCES);
#endif // BUILDFLAG(IS_CHROMEOS)
}
};
class DualLayerUserPrefStoreInitializationTest
: public DualLayerUserPrefStoreTestBase {
public:
DualLayerUserPrefStoreInitializationTest()
: DualLayerUserPrefStoreTestBase(false) {}
};
TEST_F(DualLayerUserPrefStoreInitializationTest,
ForwardsInitializationSuccess) {
// The local store and the account store are *not* initialized yet.
ASSERT_FALSE(local_store()->IsInitializationComplete());
ASSERT_FALSE(account_store()->IsInitializationComplete());
// Accordingly, the dual-layer store is not initialized either.
EXPECT_FALSE(store()->IsInitializationComplete());
MockPrefStoreObserver observer;
store()->AddObserver(&observer);
local_store()->NotifyInitializationCompleted();
// Only when both the stores are successfully initialized, does the dual-layer
// store.
EXPECT_CALL(observer, OnInitializationCompleted(true));
account_store()->NotifyInitializationCompleted();
EXPECT_TRUE(store()->IsInitializationComplete());
EXPECT_EQ(store()->GetReadError(), PersistentPrefStore::PREF_READ_ERROR_NONE);
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreInitializationTest,
ForwardsInitializationFailure) {
// The local store and the account store are *not* initialized yet.
ASSERT_FALSE(local_store()->IsInitializationComplete());
ASSERT_FALSE(account_store()->IsInitializationComplete());
// Accordingly, the dual-layer store is not initialized either.
EXPECT_FALSE(store()->IsInitializationComplete());
MockPrefStoreObserver observer;
store()->AddObserver(&observer);
// The local store encounters some read error.
local_store()->set_read_error(
PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE);
local_store()->set_read_success(false);
// Since the local store reports the error, the dual-layer store should
// forward it accordingly.
EXPECT_CALL(observer, OnInitializationCompleted(false));
local_store()->NotifyInitializationCompleted();
account_store()->NotifyInitializationCompleted();
EXPECT_TRUE(store()->IsInitializationComplete());
EXPECT_EQ(store()->GetReadError(),
PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE);
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreInitializationTest,
ShouldForwardLocalPrefStoreReadError) {
local_store()->set_read_error(
PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED);
// Read error is forwarded.
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED,
store()->ReadPrefs());
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED,
store()->GetReadError());
}
TEST_F(DualLayerUserPrefStoreInitializationTest,
ShouldForwardAccountPrefStoreReadError) {
account_store()->set_read_error(
PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED);
// Read error is forwarded.
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED,
store()->ReadPrefs());
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED,
store()->GetReadError());
}
TEST_F(DualLayerUserPrefStoreInitializationTest,
ShouldForwardLocalPrefStoreAsyncReadError) {
local_store()->set_read_error(
PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED);
// The callee is expected to take the ownership, hence the assignment to a raw
// ptr.
auto* read_error_delegate =
new ::testing::StrictMock<MockReadErrorDelegate>();
EXPECT_CALL(*read_error_delegate,
OnError(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED));
store()->ReadPrefsAsync(read_error_delegate);
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED,
store()->GetReadError());
}
TEST_F(DualLayerUserPrefStoreInitializationTest,
ShouldForwardAccountPrefStoreAsyncReadError) {
account_store()->set_read_error(
PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED);
// The callee is expected to take the ownership, hence the assignment to a raw
// ptr.
auto* read_error_delegate =
new ::testing::StrictMock<MockReadErrorDelegate>();
EXPECT_CALL(*read_error_delegate,
OnError(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED));
store()->ReadPrefsAsync(read_error_delegate);
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED,
store()->GetReadError());
}
TEST_F(DualLayerUserPrefStoreInitializationTest,
ShouldReportInitializationCompleteAfterRead) {
EXPECT_FALSE(store()->IsInitializationComplete());
store()->ReadPrefs();
EXPECT_TRUE(store()->IsInitializationComplete());
}
TEST_F(DualLayerUserPrefStoreInitializationTest, HasReadErrorDelegate) {
EXPECT_FALSE(store()->HasReadErrorDelegate());
store()->ReadPrefsAsync(new MockReadErrorDelegate);
EXPECT_TRUE(store()->HasReadErrorDelegate());
}
TEST_F(DualLayerUserPrefStoreInitializationTest,
HasReadErrorDelegateWithNullDelegate) {
EXPECT_FALSE(store()->HasReadErrorDelegate());
store()->ReadPrefsAsync(nullptr);
// Returns true even though no instance was passed.
EXPECT_TRUE(store()->HasReadErrorDelegate());
}
TEST_F(DualLayerUserPrefStoreInitializationTest,
ShouldReportInitializationCompleteAsyncReadAsync) {
// Should report init completion after async read for underlying stores is
// complete.
local_store()->SetBlockAsyncRead(true);
account_store()->SetBlockAsyncRead(true);
EXPECT_FALSE(store()->IsInitializationComplete());
store()->ReadPrefsAsync(nullptr);
local_store()->SetBlockAsyncRead(false);
EXPECT_FALSE(store()->IsInitializationComplete());
account_store()->SetBlockAsyncRead(false);
EXPECT_TRUE(store()->IsInitializationComplete());
}
TEST_F(DualLayerUserPrefStoreTest, ReadsFromLocalStore) {
store()->GetLocalPrefStore()->SetValueSilently(kPrefName,
base::Value("local_value"), 0);
// No value is set in the account store, so the one from the local store
// should be returned.
EXPECT_TRUE(ValueInStoreIs(*store(), kPrefName, "local_value"));
// Reading the value should not have affected the account store.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPrefName));
}
TEST_F(DualLayerUserPrefStoreTest, ReadsFromAccountStore) {
store()->GetAccountPrefStore()->SetValueSilently(
kPrefName, base::Value("account_value"), 0);
// No value is set in the local store, so the one from the account store
// should be returned.
EXPECT_TRUE(ValueInStoreIs(*store(), kPrefName, "account_value"));
// Reading the value should not have affected the local store.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetLocalPrefStore(), kPrefName));
}
TEST_F(DualLayerUserPrefStoreTest, AccountTakesPrecedence) {
store()->GetAccountPrefStore()->SetValueSilently(
kPrefName, base::Value("account_value"), 0);
store()->GetLocalPrefStore()->SetValueSilently(kPrefName,
base::Value("local_value"), 0);
// Different values are set in both stores; the one from the account should
// take precedence.
EXPECT_TRUE(ValueInStoreIs(*store(), kPrefName, "account_value"));
}
TEST_F(DualLayerUserPrefStoreTest, ReadsFromBothStores) {
// Three prefs: One is set in both stores, one only in the local store, and
// one only in the account store.
store()->GetLocalPrefStore()->SetValueSilently(
kPref1, base::Value("local_value1"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref1, base::Value("account_value1"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref2, base::Value("local_value2"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref3, base::Value("account_value3"), 0);
base::Value::Dict expected_values;
// For the pref that exists in both stores, the account value should take
// precedence.
expected_values.SetByDottedPath(kPref1, "account_value1");
// For the prefs that only exist in one store, their value should be returned.
expected_values.SetByDottedPath(kPref2, "local_value2");
expected_values.SetByDottedPath(kPref3, "account_value3");
// TODO(crbug.com/40268520): Also set expectations for GetValue() since
// GetValues() isn't used outside of tests and may not test the real codepath.
EXPECT_THAT(store()->GetValues(), testing::IsSupersetOf(expected_values));
}
TEST_F(DualLayerUserPrefStoreTest, WritesToBothStores) {
// Three prefs: One is set in both stores, one only in the local store, and
// one only in the account store.
store()->GetLocalPrefStore()->SetValueSilently(
kPref1, base::Value("local_value1"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref1, base::Value("account_value1"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref2, base::Value("local_value2"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref3, base::Value("account_value3"), 0);
// Update all three prefs.
store()->SetValue(kPref1, base::Value("new_value1"), 0);
store()->SetValue(kPref2, base::Value("new_value2"), 0);
store()->SetValue(kPref3, base::Value("new_value3"), 0);
// The new values should be returned from the dual-layer store...
ASSERT_TRUE(ValueInStoreIs(*store(), kPref1, "new_value1"));
ASSERT_TRUE(ValueInStoreIs(*store(), kPref2, "new_value2"));
ASSERT_TRUE(ValueInStoreIs(*store(), kPref3, "new_value3"));
// ...but should also be stored in both the local and the account store.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPref1, "new_value1"));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPref2, "new_value2"));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPref3, "new_value3"));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPref1, "new_value1"));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPref2, "new_value2"));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPref3, "new_value3"));
}
TEST_F(DualLayerUserPrefStoreTest, RemovesFromBothStores) {
// Three prefs: One is set in both stores, one only in the local store, and
// one only in the account store.
store()->GetLocalPrefStore()->SetValueSilently(
kPref1, base::Value("local_value1"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref1, base::Value("account_value1"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref2, base::Value("local_value2"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref3, base::Value("account_value3"), 0);
// Remove all three prefs.
store()->RemoveValue(kPref1, 0);
store()->RemoveValue(kPref2, 0);
store()->RemoveValue(kPref3, 0);
// The values should now be absent from the dual-layer store...
ASSERT_TRUE(ValueInStoreIsAbsent(*store(), kPref1));
ASSERT_TRUE(ValueInStoreIsAbsent(*store(), kPref2));
ASSERT_TRUE(ValueInStoreIsAbsent(*store(), kPref3));
// ...as well as from both the local and the account store.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetLocalPrefStore(), kPref1));
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetLocalPrefStore(), kPref2));
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetLocalPrefStore(), kPref3));
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPref1));
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPref2));
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPref3));
}
TEST_F(DualLayerUserPrefStoreTest,
RemovesValuesByPrefixSilentlyFromBothStores) {
// Three prefs: One is set only in the local store, one only in the account
// store, and one is set in both stores.
store()->GetLocalPrefStore()->SetValueSilently(
kPref1, base::Value("local_value1"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref2, base::Value("account_value2"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref3, base::Value("local_value3"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref3, base::Value("account_value3"), 0);
// Remove `kPref1` from the local store.
store()->RemoveValuesByPrefixSilently(kPref1);
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kPref1));
// `kPref2` and `kPref3` are still there.
EXPECT_TRUE(ValueInStoreIs(*store(), kPref2, "account_value2"));
EXPECT_TRUE(ValueInStoreIs(*store(), kPref3, "account_value3"));
// Remove `kPref2` from the account store.
store()->RemoveValuesByPrefixSilently(kPref2);
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kPref2));
// `kPref3` is still there.
EXPECT_TRUE(ValueInStoreIs(*store(), kPref3, "account_value3"));
// Remove `kPref3` using a prefix `kPrefName`.
ASSERT_TRUE(base::StartsWith(kPref3, kPrefName));
store()->RemoveValuesByPrefixSilently(kPrefName);
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kPref3));
}
TEST_F(DualLayerUserPrefStoreTest,
RemoveValuesByPrefixSilentlyRemovesMultiplePrefs) {
// Three prefs: Each set in both the stores.
store()->GetLocalPrefStore()->SetValueSilently(
kPref1, base::Value("local_value1"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref1, base::Value("account_value1"), 0);
// `kPrefName` is a prefix of `kPref1` and is used to remove `kPref1`.
store()->GetLocalPrefStore()->SetValueSilently(
kPrefName, base::Value("local_value2"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPrefName, base::Value("account_value2"), 0);
// `kPriorityPrefName` does not have `kPref1` as prefix.
store()->GetLocalPrefStore()->SetValueSilently(
kPriorityPrefName, base::Value("local_value3"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPriorityPrefName, base::Value("account_value3"), 0);
// Remove `kPref1` using prefix `kPrefName`.
ASSERT_TRUE(base::StartsWith(kPref1, kPrefName));
ASSERT_FALSE(base::StartsWith(kPriorityPrefName, kPrefName));
store()->RemoveValuesByPrefixSilently(kPrefName);
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kPref1));
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kPrefName));
// `kPriorityPrefName` is still there.
EXPECT_TRUE(ValueInStoreIs(*store(), kPriorityPrefName, "account_value3"));
}
TEST_F(DualLayerUserPrefStoreTest, DoesNotReturnNonexistentPref) {
store()->SetValueSilently(kPrefName, MakeDict({{"key", "value"}}), 0);
// The existing pref can be queried.
ASSERT_TRUE(store()->GetValue(kPrefName, nullptr));
ASSERT_TRUE(store()->GetMutableValue(kPrefName, nullptr));
// But a non-existing pref can't.
EXPECT_FALSE(store()->GetValue(kNonExistentPrefName, nullptr));
EXPECT_FALSE(store()->GetMutableValue(kNonExistentPrefName, nullptr));
}
TEST_F(DualLayerUserPrefStoreTest, WritesMutableValueFromLocalToBothStores) {
const base::Value original_value = MakeDict({{"key", "value"}});
// A dictionary-type value is present in the local store.
store()->GetLocalPrefStore()->SetValueSilently(kPrefName,
original_value.Clone(), 0);
// GetMutableValue() should return that value. In practice, this API is used
// by ScopedDictPrefUpdate and ScopedListPrefUpdate.
base::Value* mutable_value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPrefName, &mutable_value));
ASSERT_TRUE(mutable_value);
ASSERT_EQ(*mutable_value, original_value);
// Update it!
mutable_value->GetDict().Set("key", "new_value");
const base::Value expected_value = mutable_value->Clone();
// After updating the value, clients have to call ReportValueChanged() to let
// the store know it has changed. The dual-layer store uses this to reconcile
// between the two underlying stores.
store()->ReportValueChanged(kPrefName, 0);
// The new value should of course be returned from the dual-layer store now,
// but it should also have been written to both of the underlying stores.
ASSERT_TRUE(ValueInStoreIs(*store(), kPrefName, expected_value));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPrefName, expected_value));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPrefName,
expected_value));
}
TEST_F(DualLayerUserPrefStoreTest, WritesMutableValueFromAccountToBothStores) {
const base::Value original_value = MakeDict({{"key", "value"}});
// A dictionary-type value is present in the account store.
store()->GetAccountPrefStore()->SetValueSilently(kPrefName,
original_value.Clone(), 0);
// GetMutableValue() should return that value. In practice, this API is used
// by ScopedDictPrefUpdate and ScopedListPrefUpdate.
base::Value* mutable_value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPrefName, &mutable_value));
ASSERT_TRUE(mutable_value);
ASSERT_EQ(*mutable_value, original_value);
// Update it!
mutable_value->GetDict().Set("key", "new_value");
const base::Value expected_value = mutable_value->Clone();
// After updating the value, clients have to call ReportValueChanged() to let
// the store know it has changed. The dual-layer store uses this to reconcile
// between the two underlying stores.
store()->ReportValueChanged(kPrefName, 0);
// The new value should of course be returned from the dual-layer store now,
// but it should also have been written to both of the underlying stores.
ASSERT_TRUE(ValueInStoreIs(*store(), kPrefName, expected_value));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPrefName, expected_value));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPrefName,
expected_value));
}
TEST_F(DualLayerUserPrefStoreTest, WritesMutableValueFromBothToBothStores) {
const base::Value original_local_value = MakeDict({{"key", "local_value"}});
const base::Value original_account_value =
MakeDict({{"key", "account_value"}});
// A dictionary-type value is present in both of the underlying stores.
store()->GetLocalPrefStore()->SetValueSilently(
kPrefName, original_local_value.Clone(), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPrefName, original_account_value.Clone(), 0);
// GetMutableValue() should return that value. In practice, this API is used
// by ScopedDictPrefUpdate and ScopedListPrefUpdate.
base::Value* mutable_value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPrefName, &mutable_value));
ASSERT_TRUE(mutable_value);
ASSERT_EQ(*mutable_value, original_account_value);
// Update it!
mutable_value->GetDict().Set("key", "new_value");
const base::Value expected_value = mutable_value->Clone();
// After updating the value, clients have to call ReportValueChanged() to let
// the store know it has changed. The dual-layer store uses this to reconcile
// between the two underlying stores.
store()->ReportValueChanged(kPrefName, 0);
// The new value should of course be returned from the dual-layer store now,
// but it should also have been written to both of the underlying stores.
ASSERT_TRUE(ValueInStoreIs(*store(), kPrefName, expected_value));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPrefName, expected_value));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPrefName,
expected_value));
}
TEST_F(DualLayerUserPrefStoreTest, ClearsMutableValueFromBothStores) {
// A dictionary-type value is present in both of the underlying stores.
const base::Value original_value = MakeDict({{"key", "value"}});
store()->SetValueSilently(kPrefName, original_value.Clone(), 0);
ASSERT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPrefName, original_value));
ASSERT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPrefName,
original_value));
// GetMutableValue() should return that value. In practice, this API is used
// by ScopedDictPrefUpdate and ScopedListPrefUpdate.
base::Value* mutable_value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPrefName, &mutable_value));
ASSERT_TRUE(mutable_value);
ASSERT_EQ(*mutable_value, original_value);
mutable_value->GetDict().Set("key", "new_value");
// While the mutable value is "pending" (hasn't been "released" via
// ReportValueChanged()), the pref gets cleared.
// This shouldn't usually happen in practice, but in theory it could.
store()->RemoveValue(kPrefName, 0);
// Now the client that called GetMutableValue() previously reports that it is
// done changing the value.
store()->ReportValueChanged(kPrefName, 0);
// The value should have been removed from both of the stores.
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kPrefName));
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetLocalPrefStore(), kPrefName));
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPrefName));
}
TEST_F(DualLayerUserPrefStoreTest, NotifiesOfPrefChanges) {
// Three prefs: One is set in both stores, one only in the local store, and
// one only in the account store.
store()->GetLocalPrefStore()->SetValueSilently(
kPref1, base::Value("local_value1"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref1, base::Value("account_value1"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref2, base::Value("local_value2"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref3, base::Value("account_value3"), 0);
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Update the prefs. In each case, there should be exactly one pref-change
// notification.
EXPECT_CALL(observer, OnPrefValueChanged(kPref1));
EXPECT_CALL(observer, OnPrefValueChanged(kPref2));
EXPECT_CALL(observer, OnPrefValueChanged(kPref3));
store()->SetValue(kPref1, base::Value("new_value1"), 0);
store()->SetValue(kPref2, base::Value("new_value2"), 0);
store()->SetValue(kPref3, base::Value("new_value3"), 0);
// Remove the prefs. Again, there should be one notification each.
EXPECT_CALL(observer, OnPrefValueChanged(kPref1));
EXPECT_CALL(observer, OnPrefValueChanged(kPref2));
EXPECT_CALL(observer, OnPrefValueChanged(kPref3));
store()->RemoveValue(kPref1, 0);
store()->RemoveValue(kPref2, 0);
store()->RemoveValue(kPref3, 0);
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreTest,
NotifiesOfPrefChangesOnlyIfEffectiveValueChanges) {
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Add a pref to both stores but with different values.
store()->GetLocalPrefStore()->SetValueSilently(
kPref1, base::Value("local_value1"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref1, base::Value("account_value1"), 0);
// Should not lead to a notification since the effective value hasn't changed.
store()->SetValue(kPref1, base::Value("account_value1"), 0);
// But should still update the local pref store.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPref1, "account_value1"));
// Add a pref to the local store only.
store()->GetLocalPrefStore()->SetValueSilently(
kPref2, base::Value("local_value2"), 0);
// Should not lead to a notification since the effective value hasn't changed.
store()->SetValue(kPref2, base::Value("local_value2"), 0);
// But should still update the account pref store.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPref2, "local_value2"));
// Add a pref to the account store only.
store()->GetAccountPrefStore()->SetValueSilently(
kPref3, base::Value("account_value3"), 0);
// Should not lead to a notification since the effective value hasn't changed.
store()->SetValue(kPref3, base::Value("account_value3"), 0);
// But should still update the local pref store.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPref3, "account_value3"));
// Add the same pref to both stores.
store()->SetValueSilently(kPrefName, base::Value("value"), 0);
EXPECT_CALL(observer, OnPrefValueChanged(kPrefName));
// Effective value changes, so expect a notification.
store()->SetValue(kPrefName, base::Value("new_value"), 0);
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreTest, NotifiesOfPrefChangesInUnderlyingStores) {
// Three prefs: One is set in both stores, one only in the local store, and
// one only in the account store.
store()->GetLocalPrefStore()->SetValueSilently(kPref1,
base::Value("local_value"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref2, base::Value("account_value"), 0);
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Update the prefs by writing directly to the underlying stores. (For the
// account store, that happens when a pref is updated from Sync. For the local
// store, it shouldn't happen in practice.)
// The dual-layer store should notify about these changes.
EXPECT_CALL(observer, OnPrefValueChanged(kPref1));
EXPECT_CALL(observer, OnPrefValueChanged(kPref2));
store()->GetLocalPrefStore()->SetValue(kPref1, base::Value("new_value1"), 0);
store()->GetAccountPrefStore()->SetValue(kPref2, base::Value("new_value2"),
0);
// Same with removals directly in the underlying stores.
EXPECT_CALL(observer, OnPrefValueChanged(kPref1));
EXPECT_CALL(observer, OnPrefValueChanged(kPref2));
store()->GetLocalPrefStore()->RemoveValue(kPref1, 0);
store()->GetAccountPrefStore()->RemoveValue(kPref2, 0);
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreTest,
NotifiesOfPrefChangesInUnderlyingStoresOnlyIfEffectiveValueChanges) {
// Two prefs: One is set only in the local store, the other set in both
// stores.
store()->GetLocalPrefStore()->SetValueSilently(
kPref1, base::Value("local_value1"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref2, base::Value("local_value2"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref2, base::Value("account_value2"), 0);
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Update the prefs by writing directly to the underlying stores.
// The dual-layer store should notify about these changes only when the
// the *effective* value changes, i.e. not when a pref is changed in the
// local store that also has a value in the account store.
EXPECT_CALL(observer, OnPrefValueChanged(kPref1));
store()->GetLocalPrefStore()->SetValue(kPref1, base::Value("new_value1"), 0);
// Should not lead to a notification since the effective value has not
// changed.
store()->GetLocalPrefStore()->SetValue(kPref2, base::Value("new_value2"), 0);
// Same with removals directly in the underlying stores.
EXPECT_CALL(observer, OnPrefValueChanged(kPref1));
store()->GetLocalPrefStore()->RemoveValue(kPref1, 0);
store()->GetLocalPrefStore()->RemoveValue(kPref2, 0);
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreTest, NotifiesOfRemoveOnlyIfPrefExists) {
// Add a single pref.
store()->SetValueSilently(kPref1, base::Value("value"), 0);
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Only the added pref should raise a notification.
EXPECT_CALL(observer, OnPrefValueChanged(kPref1));
store()->RemoveValue(kPref1, 0);
// `kPref2` was not added and should not raise any notification.
store()->RemoveValue(kPref2, 0);
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreTest, NotifiesOfMutableValuePrefChanges) {
// Three dictionary-valued prefs: One is set in both stores, one only in the
// local store, and one only in the account store.
store()->GetLocalPrefStore()->SetValueSilently(
kPref1, MakeDict({{"key1", "local_value1"}}), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref1, MakeDict({{"key1", "account_value1"}}), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref2, MakeDict({{"key2", "local_value2"}}), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref3, MakeDict({{"key3", "account_value3"}}), 0);
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Update the prefs via GetMutableValue() + ReportValueChanged(). In each
// case, there should be exactly one pref-change notification.
EXPECT_CALL(observer, OnPrefValueChanged(kPref1));
EXPECT_CALL(observer, OnPrefValueChanged(kPref2));
EXPECT_CALL(observer, OnPrefValueChanged(kPref3));
base::Value* value1 = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPref1, &value1));
value1->GetDict().Set("key1", "new_value1");
store()->ReportValueChanged(kPref1, 0);
base::Value* value2 = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPref2, &value2));
value2->GetDict().Set("key2", "new_value2");
store()->ReportValueChanged(kPref2, 0);
base::Value* value3 = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPref3, &value3));
value3->GetDict().Set("key3", "new_value3");
store()->ReportValueChanged(kPref3, 0);
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreTest, ShouldAddOnlySyncablePrefsToAccountStore) {
constexpr char kNewValue[] = "new_value";
store()->SetValue(kPrefName, base::Value(kNewValue), 0);
// Value should be set in both the stores.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPrefName, kNewValue));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPrefName, kNewValue));
store()->SetValue(kNonSyncablePrefName, base::Value(kNewValue), 0);
// No value should be set in the account store.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(),
kNonSyncablePrefName));
// Value is only set in the local store.
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(),
kNonSyncablePrefName, kNewValue));
}
TEST_F(DualLayerUserPrefStoreTest, ShouldCommitPendingWritesForBothStores) {
base::test::SingleThreadTaskEnvironment task_env;
::testing::StrictMock<base::MockOnceClosure> reply_callback;
::testing::StrictMock<base::MockOnceClosure> done_callback;
EXPECT_CALL(reply_callback, Run);
EXPECT_CALL(done_callback, Run);
store()->CommitPendingWrite(reply_callback.Get(), done_callback.Get());
task_env.RunUntilIdle();
EXPECT_TRUE(local_store()->committed());
EXPECT_TRUE(account_store()->committed());
}
// Tests that notifications are not sent out if the same value already exists in
// the local store, i.e. the effective value is unchanged.
TEST_F(
DualLayerUserPrefStoreTest,
ShouldNotNotifyIfEffectiveValueIsUnchangedUponSetValueInAccountStoreOnly) {
store()->GetLocalPrefStore()->SetValueSilently(kPrefName,
base::Value("value"), 0);
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
testing::StrictMock<MockPrefStoreObserver> account_store_observer;
store()->GetAccountPrefStore()->AddObserver(&account_store_observer);
// Effective value in the dual pref store is unchanged, so there shouldn't be
// any calls to the observer.
EXPECT_CALL(observer, OnPrefValueChanged).Times(0);
// Since a new pref is added to the account store, its observers are still
// notified.
EXPECT_CALL(account_store_observer, OnPrefValueChanged);
store()->SetValueInAccountStoreOnly(kPrefName, base::Value("value"), 0);
store()->GetAccountPrefStore()->RemoveObserver(&account_store_observer);
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreTest,
ShouldNotifyIfEffectiveValueChangesUponSetValueInAccountStoreOnly) {
store()->GetLocalPrefStore()->SetValueSilently(kPrefName,
base::Value("value"), 0);
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
testing::StrictMock<MockPrefStoreObserver> account_store_observer;
store()->GetAccountPrefStore()->AddObserver(&account_store_observer);
// Effective value is changing, so observers should be notified.
EXPECT_CALL(observer, OnPrefValueChanged);
EXPECT_CALL(account_store_observer, OnPrefValueChanged);
store()->SetValueInAccountStoreOnly(kPrefName, base::Value("new value"), 0);
store()->GetAccountPrefStore()->RemoveObserver(&account_store_observer);
store()->RemoveObserver(&observer);
}
class DualLayerUserPrefStoreTestForTypes
: public DualLayerUserPrefStoreTestBase {
public:
DualLayerUserPrefStoreTestForTypes() : DualLayerUserPrefStoreTestBase(true) {}
};
TEST_F(DualLayerUserPrefStoreTestForTypes,
ShouldClearAllSyncablePrefsOfTypeFromAccountStoreOnDisable) {
store()->EnableType(syncer::PREFERENCES);
store()->EnableType(syncer::PRIORITY_PREFERENCES);
store()->SetValue(kPrefName, base::Value("pref-value"), 0);
store()->SetValue(kPriorityPrefName, base::Value("priority-value"), 0);
ASSERT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPrefName, "pref-value"));
ASSERT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPriorityPrefName,
"priority-value"));
store()->DisableTypeAndClearAccountStore(syncer::PRIORITY_PREFERENCES);
// The regular pref remains untouched.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPrefName, "pref-value"));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPrefName, "pref-value"));
// Priority prefs are cleared from the account store.
EXPECT_TRUE(
ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPriorityPrefName));
// Local pref store is not affected.
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kPriorityPrefName,
"priority-value"));
// The value should no longer be there in the account store even if the type
// is enabled again.
store()->EnableType(syncer::PRIORITY_PREFERENCES);
EXPECT_TRUE(
ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPriorityPrefName));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kPriorityPrefName,
"priority-value"));
}
TEST_F(DualLayerUserPrefStoreTestForTypes,
ShouldNotifyObserversOnDisableIfEffectiveValueChanges) {
store()->EnableType(syncer::PREFERENCES);
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
account_store()->SetValueSilently(kPrefName, base::Value("account_value"), 0);
local_store()->SetValueSilently(kPrefName, base::Value("local_value"), 0);
EXPECT_CALL(observer, OnPrefValueChanged(kPrefName));
ASSERT_TRUE(ValueInStoreIs(*store(), kPrefName, "account_value"));
store()->DisableTypeAndClearAccountStore(syncer::PREFERENCES);
ASSERT_TRUE(ValueInStoreIs(*store(), kPrefName, "local_value"));
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreTestForTypes,
ShouldNotifyObserversOnDisableIfLocalValueDoesNotExist) {
store()->EnableType(syncer::PREFERENCES);
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
account_store()->SetValueSilently(kPrefName, base::Value("account_value"), 0);
EXPECT_CALL(observer, OnPrefValueChanged(kPrefName));
ASSERT_TRUE(ValueInStoreIs(*store(), kPrefName, "account_value"));
store()->DisableTypeAndClearAccountStore(syncer::PREFERENCES);
ASSERT_TRUE(ValueInStoreIsAbsent(*store(), kPrefName));
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreTestForTypes,
ShouldNotNotifyObserversOnDisableIfEffectiveValueDoesNotChange) {
store()->EnableType(syncer::PREFERENCES);
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
account_store()->SetValueSilently(kPrefName, base::Value("pref-value"), 0);
local_store()->SetValueSilently(kPrefName, base::Value("pref-value"), 0);
ASSERT_TRUE(ValueInStoreIs(*store(), kPrefName, "pref-value"));
store()->DisableTypeAndClearAccountStore(syncer::PREFERENCES);
ASSERT_TRUE(ValueInStoreIs(*store(), kPrefName, "pref-value"));
// `observer` was not notified of any pref change.
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreTestForTypes,
ShouldReturnAccountValueForNotActiveTypes) {
account_store()->SetValueSilently(kPrefName, base::Value("pref-value"), 0);
ASSERT_TRUE(ValueInStoreIs(*account_store(), kPrefName, "pref-value"));
// PREFERENCES type is not active.
ASSERT_EQ(0u, store()->GetActiveTypesForTest().count(syncer::PREFERENCES));
// `kPrefName` is read from the account store even if PREFERENCES type is not
// active.
{
const base::Value* value = nullptr;
ASSERT_TRUE(store()->GetValue(kPrefName, &value));
EXPECT_EQ(*value, base::Value("pref-value"));
}
{
base::Value* value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPrefName, &value));
EXPECT_EQ(*value, base::Value("pref-value"));
}
}
TEST_F(DualLayerUserPrefStoreTestForTypes,
ShouldClearAllPrefsFromAccountStoreOnDisableAllTypes) {
store()->EnableType(syncer::PREFERENCES);
account_store()->SetValue(kPrefName, base::Value("pref-value"), 0);
// Garbage value in account store.
account_store()->SetValue(kNonSyncablePrefName,
base::Value("non-syncable-pref-value"), 0);
ASSERT_TRUE(ValueInStoreIs(*store(), kPrefName, "pref-value"));
// Non-syncable prefs are not returned by the getters.
ASSERT_TRUE(ValueInStoreIsAbsent(*store(), kNonSyncablePrefName));
ASSERT_TRUE(ValueInStoreIs(*account_store(), kNonSyncablePrefName,
"non-syncable-pref-value"));
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Notification for syncable prefs.
EXPECT_CALL(observer, OnPrefValueChanged(kPrefName));
// No notification for garbage values.
EXPECT_CALL(observer, OnPrefValueChanged(kNonSyncablePrefName)).Times(0);
store()->DisableTypeAndClearAccountStore(syncer::PREFERENCES);
// All values get removed from the account store when all types are disabled.
EXPECT_TRUE(ValueInStoreIsAbsent(*account_store(), kPrefName));
EXPECT_TRUE(ValueInStoreIsAbsent(*account_store(), kNonSyncablePrefName));
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreTestForTypes,
ShouldSetAccountValueForNotActiveTypesIfAlreadyExists) {
account_store()->SetValueSilently(kPrefName, base::Value("account_value"), 0);
ASSERT_TRUE(ValueInStoreIs(*account_store(), kPrefName, "account_value"));
// PREFERENCES type is not active.
ASSERT_EQ(0u, store()->GetActiveTypesForTest().count(syncer::PREFERENCES));
// `kPrefName` is set to the account store even if PREFERENCES type is not
// active since it already exists in the account store.
{
store()->SetValue(kPrefName, base::Value("new_value1"), 0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kPrefName, "new_value1"));
}
{
store()->SetValueSilently(kPrefName, base::Value("new_value2"), 0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kPrefName, "new_value2"));
}
{
base::Value* value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPrefName, &value));
*value = base::Value("new_value3");
store()->ReportValueChanged(kPrefName, 0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kPrefName, "new_value3"));
}
}
class DualLayerUserPrefStoreTestWithoutUseSelectedTypes
: public DualLayerUserPrefStoreTestForTypes {
public:
DualLayerUserPrefStoreTestWithoutUseSelectedTypes() {
feature_list_.InitAndDisableFeature(
syncer::kSyncPreferencesUseSelectedTypes);
}
private:
base::test::ScopedFeatureList feature_list_;
};
TEST_F(DualLayerUserPrefStoreTestWithoutUseSelectedTypes,
ShouldAddOnlyEnabledTypePrefsToAccountStore) {
// Enable only PRIORITY_PREFERENCES
store()->EnableType(syncer::PRIORITY_PREFERENCES);
store()->SetValue(kPriorityPrefName, base::Value("priority-value"), 0);
store()->SetValue(kPrefName, base::Value("pref-value"), 0);
ASSERT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPriorityPrefName,
"priority-value"));
// Regular pref is only added to the local pref store.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPrefName));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPrefName, "pref-value"));
}
TEST_F(DualLayerUserPrefStoreTestWithoutUseSelectedTypes,
ShouldAddPrefsToAccountStoreOnlyAfterEnabled) {
store()->SetValue(kPrefName, base::Value("pref-value"), 0);
// Pref is only added to the local pref store.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPrefName));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPrefName, "pref-value"));
store()->EnableType(syncer::PREFERENCES);
// The pref is not copied to the account store on enable.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPrefName));
store()->SetValue(kPrefName, base::Value("new_value"), 0);
// Both stores are updated now.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPrefName, "new_value"));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPrefName, "new_value"));
}
TEST_F(DualLayerUserPrefStoreTestWithoutUseSelectedTypes,
ShouldNotSetAccountValueForNotActiveTypesIfNotAlreadyExists) {
ASSERT_TRUE(ValueInStoreIsAbsent(*account_store(), kPrefName));
// Preferences sync is toggled on.
store()->SetUserSelectedTypesForTest(
{syncer::UserSelectableType::kPreferences});
// PREFERENCES type is not active.
ASSERT_EQ(0u, store()->GetActiveTypesForTest().count(syncer::PREFERENCES));
// `kPrefName` is not set to the account store since PREFERENCES type is not
// active and the pref does not already exist in the account store.
{
store()->SetValue(kPrefName, base::Value("new_value1"), 0);
EXPECT_TRUE(ValueInStoreIsAbsent(*account_store(), kPrefName));
}
{
store()->SetValueSilently(kPrefName, base::Value("new_value2"), 0);
EXPECT_TRUE(ValueInStoreIsAbsent(*account_store(), kPrefName));
}
{
base::Value* value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPrefName, &value));
*value = base::Value("new_value3");
store()->ReportValueChanged(kPrefName, 0);
EXPECT_TRUE(ValueInStoreIsAbsent(*account_store(), kPrefName));
}
}
class DualLayerUserPrefStoreTestWithUseSelectedTypes
: public DualLayerUserPrefStoreTestForTypes {
private:
base::test::ScopedFeatureList feature_list_{
syncer::kSyncPreferencesUseSelectedTypes};
};
TEST_F(DualLayerUserPrefStoreTestWithUseSelectedTypes,
ShouldAddPrefsToAccountStoreIfPrefsIsToggledOn) {
// Preferences sync is toggled on.
store()->SetUserSelectedTypesForTest(
{syncer::UserSelectableType::kPreferences});
// Enable only PRIORITY_PREFERENCES
store()->EnableType(syncer::PRIORITY_PREFERENCES);
ASSERT_THAT(store()->GetActiveTypesForTest(),
testing::ElementsAre(syncer::PRIORITY_PREFERENCES));
store()->SetValue(kPriorityPrefName, base::Value("priority-value"), 0);
store()->SetValue(kPrefName, base::Value("pref-value"), 0);
ASSERT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPriorityPrefName,
"priority-value"));
// Regular pref is added to the account store even though it is not active
// yet.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPrefName, "pref-value"));
}
TEST_F(DualLayerUserPrefStoreTestWithUseSelectedTypes,
ShouldAddPrefsToAccountStoreOnlyAfterSelected) {
// Preferences sync is toggled off.
store()->SetUserSelectedTypesForTest({});
store()->SetValue(kPrefName, base::Value("value"), 0);
// Pref is only added to the local pref store.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPrefName));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPrefName, "value"));
// Preferences sync is toggled on.
store()->SetUserSelectedTypesForTest(
{syncer::UserSelectableType::kPreferences});
// The pref is not copied to the account store on enable.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPrefName));
store()->SetValue(kPrefName, base::Value("new_value"), 0);
// Both stores are updated now.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPrefName, "new_value"));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPrefName, "new_value"));
}
TEST_F(DualLayerUserPrefStoreTestWithUseSelectedTypes,
ShouldSetAccountValueForNotActiveTypesIfNotAlreadyExists) {
ASSERT_TRUE(ValueInStoreIsAbsent(*account_store(), kPrefName));
// Preferences sync is toggled on.
store()->SetUserSelectedTypesForTest(
{syncer::UserSelectableType::kPreferences});
// PREFERENCES type is not active.
ASSERT_EQ(0u, store()->GetActiveTypesForTest().count(syncer::PREFERENCES));
// `kPrefName` is set in the account store even though PREFERENCES type is not
// active and the pref does not already exist in the account store.
{
store()->SetValue(kPrefName, base::Value("new_value1"), 0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kPrefName, "new_value1"));
}
{
store()->SetValueSilently(kPrefName, base::Value("new_value2"), 0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kPrefName, "new_value2"));
}
{
base::Value* value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPrefName, &value));
*value = base::Value("new_value3");
store()->ReportValueChanged(kPrefName, 0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kPrefName, "new_value3"));
}
}
TEST_F(DualLayerUserPrefStoreTestWithUseSelectedTypes,
ShouldSetAccountValueForToggledOffTypesIfAlreadyExists) {
// Preferences sync is off.
store()->SetUserSelectedTypesForTest({});
// Set pre-existing account pref value.
account_store()->SetValue(kPref1, base::Value("value"), 0);
// Account pref value is still read.
EXPECT_TRUE(ValueInStoreIs(*store(), kPref1, "value"));
store()->SetValue(kPref1, base::Value("new_value"), 0);
store()->SetValue(kPref2, base::Value("new_value2"), 0);
// The new value is still written to the account store for `kPref1` even
// though preferences sync is off.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPref1, "new_value"));
// The same is not true for `kPref2` since it is not already present in the
// account store.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kPref2));
}
#if BUILDFLAG(IS_CHROMEOS)
// Test to verify that the user selected OS types are loaded from a pref in the
// local store.
TEST_F(DualLayerUserPrefStoreTestWithUseSelectedTypes,
ShouldGetUserSelectedOsTypesFromLocalStore) {
// Multiple types.
local_store()->SetValueSilently(kUserSelectedOsTypesPrefName,
base::Value(base::Value::List()
.Append("passwords")
.Append("osPreferences")
.Append("osApps")),
0);
// Only the interesting type (osPreferences) is returned.
EXPECT_EQ(store()->GetUserSelectedOsTypesForTest(),
syncer::UserSelectableOsTypeSet(
{syncer::UserSelectableOsType::kOsPreferences}));
// Empty list.
local_store()->SetValueSilently(kUserSelectedOsTypesPrefName,
base::Value(base::Value::List()), 0);
EXPECT_TRUE(store()->GetUserSelectedOsTypesForTest().empty());
}
// Test to verify that the user selected OS types are stored in a pref in the
// local store.
TEST_F(DualLayerUserPrefStoreTestWithUseSelectedTypes,
ShouldSetUserSelectedOsTypesToLocalStore) {
// Multiple types.
store()->SetUserSelectedOsTypesForTest(syncer::UserSelectableOsTypeSet(
{syncer::UserSelectableOsType::kOsPreferences}));
EXPECT_TRUE(
ValueInStoreIs(*local_store(), kUserSelectedOsTypesPrefName,
base::Value(base::Value::List().Append("osPreferences"))));
EXPECT_TRUE(
ValueInStoreIsAbsent(*account_store(), kUserSelectedOsTypesPrefName));
// Empty list.
store()->SetUserSelectedOsTypesForTest(syncer::UserSelectableOsTypeSet());
EXPECT_TRUE(ValueInStoreIs(*local_store(), kUserSelectedOsTypesPrefName,
base::Value(base::Value::List())));
}
// Test to verify that OS prefs are added to the account store only if the OS
// preferences type is enabled.
TEST_F(DualLayerUserPrefStoreTestWithUseSelectedTypes,
ShouldAddOsPrefsToAccountStoreIfOsPrefsIsToggledOn) {
// OS Preferences sync is toggled on.
store()->SetUserSelectedOsTypesForTest(
{syncer::UserSelectableOsType::kOsPreferences});
// Enable only OS_PRIORITY_PREFERENCES
store()->EnableType(syncer::OS_PRIORITY_PREFERENCES);
ASSERT_THAT(store()->GetActiveTypesForTest(),
testing::ElementsAre(syncer::OS_PRIORITY_PREFERENCES));
store()->SetValue(kOsPriorityPrefName, base::Value("priority-value"), 0);
store()->SetValue(kOsPrefName, base::Value("pref-value"), 0);
ASSERT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(),
kOsPriorityPrefName, "priority-value"));
// Regular pref is added to the account store even though it is not active
// yet.
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kOsPrefName,
"pref-value"));
}
TEST_F(DualLayerUserPrefStoreTestWithUseSelectedTypes,
ShouldNotAddOsPrefsToAccountStoreIfOsPrefsIsToggledOff) {
// OS Preferences sync is toggled off.
store()->SetUserSelectedOsTypesForTest({});
// Enable only OS_PRIORITY_PREFERENCES
store()->EnableType(syncer::OS_PRIORITY_PREFERENCES);
ASSERT_THAT(store()->GetActiveTypesForTest(),
testing::ElementsAre(syncer::OS_PRIORITY_PREFERENCES));
store()->SetValue(kOsPriorityPrefName, base::Value("priority-value"), 0);
store()->SetValue(kOsPrefName, base::Value("pref-value"), 0);
// OS prefs are not added to the account store since the OS pref type is
// disabled, they are only added to the local pref store.
ASSERT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kOsPriorityPrefName,
"priority-value"));
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(),
kOsPriorityPrefName));
ASSERT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kOsPrefName, "pref-value"));
EXPECT_TRUE(
ValueInStoreIsAbsent(*store()->GetAccountPrefStore(), kOsPrefName));
}
#endif // BUILDFLAG(IS_CHROMEOS)
class MergeTestPrefModelAssociatorClient : public PrefModelAssociatorClient {
public:
MergeTestPrefModelAssociatorClient()
: syncable_prefs_database_(kSyncablePrefsDatabase) {}
// PrefModelAssociatorClient implementation.
base::Value MaybeMergePreferenceValues(
std::string_view pref_name,
const base::Value& local_value,
const base::Value& server_value) const override {
if (auto it = custom_merge_values_.find(pref_name);
it != custom_merge_values_.end()) {
return it->second.Clone();
}
return base::Value();
}
const SyncablePrefsDatabase& GetSyncablePrefsDatabase() const override {
return syncable_prefs_database_;
}
void SetCustomMergeValue(const std::string& pref_name, base::Value value) {
custom_merge_values_[pref_name] = std::move(value);
}
private:
~MergeTestPrefModelAssociatorClient() override = default;
TestSyncablePrefsDatabase syncable_prefs_database_;
std::set<std::string> mergeable_dict_prefs_;
std::set<std::string> mergeable_list_prefs_;
std::map<std::string, base::Value, std::less<>> custom_merge_values_;
};
class DualLayerUserPrefStoreMergeTest : public testing::Test {
public:
DualLayerUserPrefStoreMergeTest() {
local_store_ = base::MakeRefCounted<TestingPrefStore>();
account_store_ = base::MakeRefCounted<TestingPrefStore>();
pref_model_associator_client_ =
base::MakeRefCounted<MergeTestPrefModelAssociatorClient>();
dual_layer_store_ = base::MakeRefCounted<DualLayerUserPrefStore>(
local_store_, account_store_, pref_model_associator_client_);
local_store_->NotifyInitializationCompleted();
account_store_->NotifyInitializationCompleted();
dual_layer_store_->AddObserver(&observer_);
// Set preferences user selected type by default.
// TODO(crbug.com/464008640): Also set kOsPreferences for
// OS_{,PRIORITY_}PREFERENCES.
dual_layer_store_->SetUserSelectedTypesForTest(
{syncer::UserSelectableType::kPreferences});
dual_layer_store_->EnableType(syncer::PREFERENCES);
dual_layer_store_->EnableType(syncer::PRIORITY_PREFERENCES);
#if BUILDFLAG(IS_CHROMEOS)
dual_layer_store_->EnableType(syncer::OS_PREFERENCES);
dual_layer_store_->EnableType(syncer::OS_PRIORITY_PREFERENCES);
#endif // BUILDFLAG(IS_CHROMEOS)
}
~DualLayerUserPrefStoreMergeTest() override {
dual_layer_store_->RemoveObserver(&observer_);
}
DualLayerUserPrefStore* store() { return dual_layer_store_.get(); }
protected:
scoped_refptr<TestingPrefStore> local_store_;
scoped_refptr<TestingPrefStore> account_store_;
scoped_refptr<MergeTestPrefModelAssociatorClient>
pref_model_associator_client_;
scoped_refptr<DualLayerUserPrefStore> dual_layer_store_;
testing::StrictMock<MockPrefStoreObserver> observer_;
};
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldUseAccountValueForNonMergeablePrefs) {
// String prefs.
base::Value account_value("account_value");
store()->GetAccountPrefStore()->SetValueSilently(kPref1,
account_value.Clone(), 0);
base::Value local_value("local_value");
store()->GetLocalPrefStore()->SetValueSilently(kPref1, local_value.Clone(),
0);
// Different values are set in both stores; the one from the account should
// take precedence.
// Uses GetValue().
{
const base::Value* result = nullptr;
ASSERT_TRUE(store()->GetValue(kPref1, &result));
EXPECT_EQ(*result, account_value);
}
// Uses GetMutableValue().
{
base::Value* result = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPref1, &result));
EXPECT_EQ(*result, account_value);
}
// Uses GetValues().
{
EXPECT_TRUE(
ValueInDictByDottedPathIs(store()->GetValues(), kPref1, account_value));
}
// List prefs.
base::Value account_list(base::Value::List().Append("account_value"));
store()->GetAccountPrefStore()->SetValueSilently(kPref2, account_list.Clone(),
0);
base::Value local_list(base::Value::List().Append("local_value"));
store()->GetLocalPrefStore()->SetValueSilently(kPref2, local_list.Clone(), 0);
// Different values are set in both stores; the one from the account should
// take precedence.
// Uses GetValue().
{
const base::Value* result = nullptr;
ASSERT_TRUE(store()->GetValue(kPref2, &result));
EXPECT_EQ(*result, account_list);
}
// Uses GetMutableValue().
{
base::Value* result = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPref2, &result));
EXPECT_EQ(*result, account_list);
}
// Uses GetValues().
{
EXPECT_TRUE(
ValueInDictByDottedPathIs(store()->GetValues(), kPref2, account_list));
}
// Dictionary prefs.
base::Value account_dict(base::Value::Dict()
.Set("account_key", "account_value")
.Set("common_key", "account_value"));
store()->GetAccountPrefStore()->SetValueSilently(kPref3, account_dict.Clone(),
0);
base::Value local_dict(base::Value::Dict()
.Set("local_key", "local_value")
.Set("common_key", "local_value"));
store()->GetLocalPrefStore()->SetValueSilently(kPref3, local_dict.Clone(), 0);
// Different values are set in both stores; the one from the account should
// take precedence.
// Uses GetValue().
{
const base::Value* result = nullptr;
ASSERT_TRUE(store()->GetValue(kPref3, &result));
EXPECT_EQ(*result, account_dict);
}
// Uses GetMutableValue().
{
base::Value* result = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kPref3, &result));
EXPECT_EQ(*result, account_dict);
}
// Uses GetValues().
{
EXPECT_TRUE(
ValueInDictByDottedPathIs(store()->GetValues(), kPref3, account_dict));
}
// The local and the account stores are left untouched.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPref1, local_value));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPref1, account_value));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPref2, local_list));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPref2, account_list));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kPref3, local_dict));
EXPECT_TRUE(
ValueInStoreIs(*store()->GetAccountPrefStore(), kPref3, account_dict));
}
TEST_F(DualLayerUserPrefStoreMergeTest, ShouldMergeMergeableListPref) {
base::Value account_list(
base::Value::List().Append("account_value").Append("common_value"));
store()->GetAccountPrefStore()->SetValueSilently(kMergeableListPref,
account_list.Clone(), 0);
base::Value local_list(
base::Value::List().Append("local_value").Append("common_value"));
store()->GetLocalPrefStore()->SetValueSilently(kMergeableListPref,
local_list.Clone(), 0);
// Different values are set in both stores; a merged view should be returned.
// The two lists should be de-duped, with account values coming first.
base::Value merged_list(base::Value::List()
.Append("account_value")
.Append("common_value")
.Append("local_value"));
// Uses GetValue().
{
const base::Value* result = nullptr;
ASSERT_TRUE(store()->GetValue(kMergeableListPref, &result));
EXPECT_EQ(*result, merged_list);
}
// Uses GetMutableValue().
{
base::Value* result = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kMergeableListPref, &result));
EXPECT_EQ(*result, merged_list);
}
// Uses GetValues().
{
EXPECT_TRUE(ValueInDictByDottedPathIs(store()->GetValues(),
kMergeableListPref, merged_list));
}
// The local and the account stores are left untouched.
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kMergeableListPref,
local_list));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(),
kMergeableListPref, account_list));
}
TEST_F(DualLayerUserPrefStoreMergeTest, ShouldMergeMergeableDictPref) {
base::Value account_dict(base::Value::Dict()
.Set("account_key", "account_value")
.Set("common_key", "account_value"));
store()->GetAccountPrefStore()->SetValueSilently(kMergeableDictPref1,
account_dict.Clone(), 0);
base::Value local_dict(base::Value::Dict()
.Set("local_key", "local_value")
.Set("common_key", "local_value"));
store()->GetLocalPrefStore()->SetValueSilently(kMergeableDictPref1,
local_dict.Clone(), 0);
// Different values are set in both stores; a merged view should be returned.
// In case of conflict, the value in account store takes precedence.
base::Value merged_dict(base::Value::Dict()
.Set("account_key", "account_value")
.Set("local_key", "local_value")
.Set("common_key", "account_value"));
// Uses GetValue().
{
const base::Value* result = nullptr;
ASSERT_TRUE(store()->GetValue(kMergeableDictPref1, &result));
EXPECT_EQ(*result, merged_dict);
}
// Uses GetMutableValue().
{
base::Value* result = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kMergeableDictPref1, &result));
EXPECT_EQ(*result, merged_dict);
}
// Uses GetValues().
{
EXPECT_TRUE(ValueInDictByDottedPathIs(store()->GetValues(),
kMergeableDictPref1, merged_dict));
}
// The local and the account stores are left untouched.
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kMergeableDictPref1,
local_dict));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(),
kMergeableDictPref1, account_dict));
}
TEST_F(DualLayerUserPrefStoreMergeTest, ShouldMergeSpecialCasedMergeablePref) {
base::Value account_value("account_value");
store()->GetAccountPrefStore()->SetValueSilently(kCustomMergePref,
account_value.Clone(), 0);
base::Value local_value("local_value");
store()->GetLocalPrefStore()->SetValueSilently(kCustomMergePref,
local_value.Clone(), 0);
base::Value merged_value("custom_merge_value");
pref_model_associator_client_->SetCustomMergeValue(kCustomMergePref,
merged_value.Clone());
// Different values are set in both stores; the merge should use the custom
// logic.
// Uses GetValue().
{
const base::Value* result = nullptr;
ASSERT_TRUE(store()->GetValue(kCustomMergePref, &result));
EXPECT_EQ(*result, merged_value);
}
// Uses GetMutableValue().
{
base::Value* result = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kCustomMergePref, &result));
EXPECT_EQ(*result, merged_value);
}
// Uses GetValues().
{
EXPECT_TRUE(ValueInDictByDottedPathIs(store()->GetValues(),
kCustomMergePref, merged_value));
}
// The local and the account stores are left untouched.
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kCustomMergePref,
local_value));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kCustomMergePref,
account_value));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldApplyUpdatesToBothStoresForNonMergeablePrefOnSetValue) {
// Set three prefs; one only in the account store, one only in the local store
// and one in both stores.
store()->GetAccountPrefStore()->SetValueSilently(
kPref1, base::Value("account_value1"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref2, base::Value("local_value2"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref3, base::Value("account_value3"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref3, base::Value("local_value3"), 0);
// Set an existing account pref. This should not raise a notification, but
// only writes to the local store.
store()->SetValue(kPref1, base::Value("account_value1"), 0);
EXPECT_TRUE(ValueInStoreIs(*store(), kPref1, base::Value("account_value1")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kPref1,
base::Value("account_value1")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPref1,
base::Value("account_value1")));
// Set an existing local pref. This should write to the account store, but not
// raise a notification.
store()->SetValue(kPref2, base::Value("local_value2"), 0);
EXPECT_TRUE(ValueInStoreIs(*store(), kPref2, base::Value("local_value2")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kPref2,
base::Value("local_value2")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPref2,
base::Value("local_value2")));
// Update the common pref. This writes to both stores and raises notification
// because the effective value changed.
EXPECT_CALL(observer_, OnPrefValueChanged(kPref3));
store()->SetValue(kPref3, base::Value("new_value3"), 0);
EXPECT_TRUE(ValueInStoreIs(*store(), kPref3, base::Value("new_value3")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kPref3,
base::Value("new_value3")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPref3,
base::Value("new_value3")));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldApplyUpdatesToBothStoresForNonMergeablePrefOnSetValueSilently) {
// Set three prefs; one only in the account store, one only in the local store
// and one in both stores.
store()->GetAccountPrefStore()->SetValueSilently(
kPref1, base::Value("account_value1"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref2, base::Value("local_value2"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref3, base::Value("account_value3"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref3, base::Value("local_value3"), 0);
// Set an existing account pref. This should write to the local store.
store()->SetValueSilently(kPref1, base::Value("account_value1"), 0);
EXPECT_TRUE(ValueInStoreIs(*store(), kPref1, base::Value("account_value1")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kPref1,
base::Value("account_value1")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPref1,
base::Value("account_value1")));
// Set an existing local pref. This should write to the account store.
store()->SetValueSilently(kPref2, base::Value("local_value2"), 0);
EXPECT_TRUE(ValueInStoreIs(*store(), kPref2, base::Value("local_value2")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kPref2,
base::Value("local_value2")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPref2,
base::Value("local_value2")));
// Update the common pref. This writes to both stores.
store()->SetValueSilently(kPref3, base::Value("new_value3"), 0);
EXPECT_TRUE(ValueInStoreIs(*store(), kPref3, base::Value("new_value3")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kPref3,
base::Value("new_value3")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPref3,
base::Value("new_value3")));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldApplyUpdatesToBothStoresForNonMergeablePrefOnReportValueChanged) {
// Set three prefs; one only in the account store, one only in the local store
// and one in both stores.
store()->GetAccountPrefStore()->SetValueSilently(
kPref1, base::Value("account_value1"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref2, base::Value("local_value2"), 0);
store()->GetAccountPrefStore()->SetValueSilently(
kPref3, base::Value("account_value3"), 0);
store()->GetLocalPrefStore()->SetValueSilently(
kPref3, base::Value("local_value3"), 0);
// Set an existing account pref. This writes to both stores and raises
// notification.
EXPECT_CALL(observer_, OnPrefValueChanged(kPref1));
store()->ReportValueChanged(kPref1, 0);
EXPECT_TRUE(ValueInStoreIs(*store(), kPref1, base::Value("account_value1")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kPref1,
base::Value("account_value1")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPref1,
base::Value("account_value1")));
// Set an existing local pref. This writes to both stores and raises
// notification.
EXPECT_CALL(observer_, OnPrefValueChanged(kPref2));
store()->ReportValueChanged(kPref2, 0);
EXPECT_TRUE(ValueInStoreIs(*store(), kPref2, base::Value("local_value2")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kPref2,
base::Value("local_value2")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPref2,
base::Value("local_value2")));
// Update the common pref. This writes to both stores and raises notification.
EXPECT_CALL(observer_, OnPrefValueChanged(kPref3));
store()->ReportValueChanged(kPref3, 0);
EXPECT_TRUE(ValueInStoreIs(*store(), kPref3, base::Value("account_value3")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kPref3,
base::Value("account_value3")));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(), kPref3,
base::Value("account_value3")));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldUpdateMergedPrefOnWriteToUnderlyingStoresUsingSetValue) {
store()->GetAccountPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("common_key", "account_value")),
0);
store()->GetLocalPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("local_key", "local_value")
.Set("common_key", "local_value")),
0);
ASSERT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("local_key", "local_value")
.Set("common_key", "account_value"))));
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableDictPref1));
// Update account value.
store()->GetAccountPrefStore()->SetValue(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "new_account_value")
.Set("common_key", "account_value")),
0);
// Updated account value should reflect in the merged view.
EXPECT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
// Updated value.
.Set("account_key", "new_account_value")
.Set("local_key", "local_value")
.Set("common_key", "account_value"))));
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableDictPref1));
// Add new key to local value.
store()->GetLocalPrefStore()->SetValue(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("local_key", "local_value")
// New entry.
.Set("new_local_key", "local_value")
.Set("common_key", "local_value")),
0);
// Updated local value should reflect in the merged view.
EXPECT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "new_account_value")
.Set("local_key", "local_value")
// New entry.
.Set("new_local_key", "local_value")
.Set("common_key", "account_value"))));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldUpdateMergedPrefOnWriteToUnderlyingStoresUsingSetValueSilently) {
store()->GetAccountPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("common_key", "account_value")),
0);
store()->GetLocalPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("local_key", "local_value")
.Set("common_key", "local_value")),
0);
ASSERT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("local_key", "local_value")
.Set("common_key", "account_value"))));
// Update account value.
store()->GetAccountPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
// Updated value.
.Set("account_key", "new_account_value")
.Set("common_key", "account_value")),
0);
// Updated account value should reflect in the merged view.
EXPECT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
// Updated value.
.Set("account_key", "new_account_value")
.Set("local_key", "local_value")
.Set("common_key", "account_value"))));
// Add new key to local value.
store()->GetLocalPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("local_key", "local_value")
.Set("new_local_key", "local_value")
.Set("common_key", "local_value")),
0);
// Updated local value should reflect in the merged view.
EXPECT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "new_account_value")
.Set("local_key", "local_value")
// New entry.
.Set("new_local_key", "local_value")
.Set("common_key", "account_value"))));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldUpdateMergedPrefOnWriteToUnderlyingStoresUsingMutableValue) {
store()->GetAccountPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("common_key", "account_value")),
0);
store()->GetLocalPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("local_key", "local_value")
.Set("common_key", "local_value")),
0);
ASSERT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("local_key", "local_value")
.Set("common_key", "account_value"))));
base::Value* account_value = nullptr;
store()->GetAccountPrefStore()->GetMutableValue(kMergeableDictPref1,
&account_value);
ASSERT_TRUE(account_value && account_value->is_dict());
// Update account value.
*account_value = base::Value(base::Value::Dict()
// Updated value.
.Set("account_key", "new_account_value")
.Set("common_key", "account_value"));
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableDictPref1));
store()->GetAccountPrefStore()->ReportValueChanged(kMergeableDictPref1, 0);
// Updated account value should reflect in the merged view.
EXPECT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
// Updated value.
.Set("account_key", "new_account_value")
.Set("local_key", "local_value")
.Set("common_key", "account_value"))));
base::Value* local_value = nullptr;
store()->GetLocalPrefStore()->GetMutableValue(kMergeableDictPref1,
&local_value);
ASSERT_TRUE(local_value && local_value->is_dict());
// Add new key to local value.
local_value->GetDict().Set("new_local_key", "local_value");
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableDictPref1));
store()->GetLocalPrefStore()->ReportValueChanged(kMergeableDictPref1, 0);
// Updated local value should reflect in the merged view.
EXPECT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "new_account_value")
.Set("local_key", "local_value")
// New entry.
.Set("new_local_key", "local_value")
.Set("common_key", "account_value"))));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldUpdateMergedPrefOnRemoveFromUnderlyingStores) {
store()->GetAccountPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("common_key", "account_value")),
0);
store()->GetLocalPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("local_key", "local_value")
.Set("common_key", "local_value")),
0);
ASSERT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("local_key", "local_value")
.Set("common_key", "account_value"))));
// Remove pref from the account store.
store()->GetAccountPrefStore()->RemoveValuesByPrefixSilently(
kMergeableDictPref1);
EXPECT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("local_key", "local_value")
// Value now being by the local store.
.Set("common_key", "local_value"))));
// Remove pref from the local store.
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableDictPref1));
store()->GetLocalPrefStore()->RemoveValue(kMergeableDictPref1, 0);
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kMergeableDictPref1));
}
TEST_F(DualLayerUserPrefStoreMergeTest, ShouldClearMergedPrefOnRemove) {
// Ensures that pref no longer exists in the merged pref store upon remove.
store()->GetAccountPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("common_key", "account_value")),
0);
store()->GetLocalPrefStore()->SetValueSilently(
kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("local_key", "local_value")
.Set("common_key", "local_value")),
0);
ASSERT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref1,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("local_key", "local_value")
.Set("common_key", "account_value"))));
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableDictPref1));
store()->RemoveValue(kMergeableDictPref1, 0);
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kMergeableDictPref1));
store()->GetAccountPrefStore()->SetValueSilently(
kMergeableDictPref2,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("common_key", "account_value")),
0);
store()->GetLocalPrefStore()->SetValueSilently(
kMergeableDictPref2,
base::Value(base::Value::Dict()
.Set("local_key", "local_value")
.Set("common_key", "local_value")),
0);
ASSERT_TRUE(
ValueInStoreIs(*store(), kMergeableDictPref2,
base::Value(base::Value::Dict()
.Set("account_key", "account_value")
.Set("local_key", "local_value")
.Set("common_key", "account_value"))));
store()->RemoveValuesByPrefixSilently(kMergeableDictPref2);
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kMergeableDictPref2));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldUnmergeMergeableDictPrefButNotAddUnchangedValueToAccountStore) {
base::Value local_dict(base::Value::Dict().Set("local_key", "local_value"));
store()->GetLocalPrefStore()->SetValueSilently(kMergeableDictPref1,
local_dict.Clone(), 0);
// `kMergeableDictPref1` only exists in the local store.
ASSERT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(),
kMergeableDictPref1));
// Effective value same as local value since pref is not in account store.
ASSERT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, local_dict));
// Set the effective/merged value again.
// Note: Expecting no notification.
store()->SetValue(kMergeableDictPref1, local_dict.Clone(), 0);
// Value in the local store remains unchanged.
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kMergeableDictPref1,
local_dict));
// An empty dict pref is added to the account store.
// Note: This is an implementation detail. Ideally, not adding the pref to the
// account store might be a better approach.
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(),
kMergeableDictPref1,
base::Value(base::Value::Type::DICT)));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldUnmergeMergeableDictPrefButNotAddUnchangedValueToLocalStore) {
base::Value account_dict(
base::Value::Dict().Set("account_key", "account_value"));
store()->GetAccountPrefStore()->SetValueSilently(kMergeableDictPref1,
account_dict.Clone(), 0);
// `kMergeableDictPref1` only exists in the account store.
ASSERT_TRUE(
ValueInStoreIsAbsent(*store()->GetLocalPrefStore(), kMergeableDictPref1));
ASSERT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, account_dict));
// Set the effective/merged value again.
// Note: Expecting no notification.
store()->SetValue(kMergeableDictPref1, account_dict.Clone(), 0);
// An empty dict pref is added to the local store.
// Note: This is an implementation detail. Ideally, not adding the pref to the
// local store might be a better approach.
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kMergeableDictPref1,
base::Value(base::Value::Type::DICT)));
// Value in the account store remains unchanged.
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(),
kMergeableDictPref1, account_dict));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldUnmergeAndApplyUpdatesForMergeableDictPrefOnSetValue) {
base::Value local_dict(base::Value::Dict()
.Set("local_key1", "local_value1")
.Set("local_key2", "local_value2")
.Set("local_key3", "local_value3")
.Set("common_key1", "local_value4")
.Set("common_key2", "common_value"));
store()->GetLocalPrefStore()->SetValueSilently(kMergeableDictPref1,
local_dict.Clone(), 0);
base::Value account_dict(base::Value::Dict()
.Set("account_key1", "account_value1")
.Set("account_key2", "account_value2")
.Set("account_key3", "account_value3")
.Set("common_key1", "account_value4")
.Set("common_key2", "common_value"));
store()->GetAccountPrefStore()->SetValueSilently(kMergeableDictPref1,
account_dict.Clone(), 0);
base::Value merged_dict(base::Value::Dict()
.Set("account_key1", "account_value1")
.Set("account_key2", "account_value2")
.Set("account_key3", "account_value3")
.Set("common_key1", "account_value4")
.Set("common_key2", "common_value")
.Set("local_key1", "local_value1")
.Set("local_key2", "local_value2")
.Set("local_key3", "local_value3"));
ASSERT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, merged_dict));
base::Value updated_dict(base::Value::Dict()
// New key, should get added to both
// stores.
.Set("new_key", "new_value")
// Updated value, should get added to both
// stores.
.Set("account_key1", "new_value1")
// No change, should only be in account
// store.
.Set("account_key2", "account_value2")
// No change, should only be in local
// store.
.Set("local_key1", "local_value1")
// Updated value, should get added to both
// stores.
.Set("local_key2", "new_value2")
// Updated value, should get added to both
// stores.
.Set("common_key1", "local_value4")
// Updated value, should get added to both
// stores.
.Set("common_key2", "new_common_value"));
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableDictPref1));
store()->SetValue(kMergeableDictPref1, updated_dict.Clone(), 0);
// Note: "local_key3" has been deleted.
base::Value updated_local_dict(base::Value::Dict()
.Set("new_key", "new_value")
.Set("account_key1", "new_value1")
.Set("local_key1", "local_value1")
.Set("local_key2", "new_value2")
.Set("common_key1", "local_value4")
.Set("common_key2", "new_common_value"));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kMergeableDictPref1,
updated_local_dict));
// Note: "account_key3" has been deleted.
base::Value updated_account_dict(base::Value::Dict()
.Set("new_key", "new_value")
.Set("account_key1", "new_value1")
.Set("account_key2", "account_value2")
.Set("local_key2", "new_value2")
.Set("common_key1", "local_value4")
.Set("common_key2", "new_common_value"));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(),
kMergeableDictPref1, updated_account_dict));
ASSERT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, updated_dict));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldUnmergeAndApplyUpdatesForMergeableDictPrefOnSetValueSilently) {
base::Value local_dict(base::Value::Dict()
.Set("local_key1", "local_value1")
.Set("local_key2", "local_value2")
.Set("local_key3", "local_value3")
.Set("common_key1", "local_value4")
.Set("common_key2", "common_value"));
store()->GetLocalPrefStore()->SetValueSilently(kMergeableDictPref1,
local_dict.Clone(), 0);
base::Value account_dict(base::Value::Dict()
.Set("account_key1", "account_value1")
.Set("account_key2", "account_value2")
.Set("account_key3", "account_value3")
.Set("common_key1", "account_value4")
.Set("common_key2", "common_value"));
store()->GetAccountPrefStore()->SetValueSilently(kMergeableDictPref1,
account_dict.Clone(), 0);
base::Value merged_dict(base::Value::Dict()
.Set("account_key1", "account_value1")
.Set("account_key2", "account_value2")
.Set("account_key3", "account_value3")
.Set("common_key1", "account_value4")
.Set("common_key2", "common_value")
.Set("local_key1", "local_value1")
.Set("local_key2", "local_value2")
.Set("local_key3", "local_value3"));
ASSERT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, merged_dict));
base::Value updated_dict(base::Value::Dict()
// New key, should get added to both
// stores.
.Set("new_key", "new_value")
// Updated value, should get added to both
// stores.
.Set("account_key1", "new_value1")
// No change, should only be in account
// store.
.Set("account_key2", "account_value2")
// No change, should only be in local
// store.
.Set("local_key1", "local_value1")
// Updated value, should get added to both
// stores.
.Set("local_key2", "new_value2")
// Updated value, should get added to both
// stores.
.Set("common_key1", "local_value4")
// Updated value, should get added to both
// stores.
.Set("common_key2", "new_common_value"));
store()->SetValueSilently(kMergeableDictPref1, updated_dict.Clone(), 0);
// Note: "local_key3" has been deleted.
base::Value updated_local_dict(base::Value::Dict()
.Set("new_key", "new_value")
.Set("account_key1", "new_value1")
.Set("local_key1", "local_value1")
.Set("local_key2", "new_value2")
.Set("common_key1", "local_value4")
.Set("common_key2", "new_common_value"));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kMergeableDictPref1,
updated_local_dict));
// Note: "account_key3" has been deleted.
base::Value updated_account_dict(base::Value::Dict()
.Set("new_key", "new_value")
.Set("account_key1", "new_value1")
.Set("account_key2", "account_value2")
.Set("local_key2", "new_value2")
.Set("common_key1", "local_value4")
.Set("common_key2", "new_common_value"));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(),
kMergeableDictPref1, updated_account_dict));
ASSERT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, updated_dict));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldUnmergeAndApplyUpdatesForMergeableDictPrefOnReportPrefChanged) {
base::Value local_dict(base::Value::Dict()
.Set("local_key1", "local_value1")
.Set("local_key2", "local_value2")
.Set("local_key3", "local_value3")
.Set("common_key1", "local_value4")
.Set("common_key2", "common_value"));
store()->GetLocalPrefStore()->SetValueSilently(kMergeableDictPref1,
local_dict.Clone(), 0);
base::Value account_dict(base::Value::Dict()
.Set("account_key1", "account_value1")
.Set("account_key2", "account_value2")
.Set("account_key3", "account_value3")
.Set("common_key1", "account_value4")
.Set("common_key2", "common_value"));
store()->GetAccountPrefStore()->SetValueSilently(kMergeableDictPref1,
account_dict.Clone(), 0);
base::Value merged_dict(base::Value::Dict()
.Set("account_key1", "account_value1")
.Set("account_key2", "account_value2")
.Set("account_key3", "account_value3")
.Set("common_key1", "account_value4")
.Set("common_key2", "common_value")
.Set("local_key1", "local_value1")
.Set("local_key2", "local_value2")
.Set("local_key3", "local_value3"));
base::Value* merged_value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kMergeableDictPref1, &merged_value));
ASSERT_EQ(*merged_value, merged_dict);
base::Value updated_dict(base::Value::Dict()
// New key, should get added to both
// stores.
.Set("new_key", "new_value")
// Updated value, should get added to both
// stores.
.Set("account_key1", "new_value1")
// No change, should only be in account
// store.
.Set("account_key2", "account_value2")
// No change, should only be in local
// store.
.Set("local_key1", "local_value1")
// Updated value, should get added to both
// stores.
.Set("local_key2", "new_value2")
// Updated value, should get added to both
// stores.
.Set("common_key1", "local_value4")
// Updated value, should get added to both
// stores.
.Set("common_key2", "new_common_value"));
*merged_value = updated_dict.Clone();
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableDictPref1));
store()->ReportValueChanged(kMergeableDictPref1, 0);
// Note: "local_key3" has been deleted.
base::Value updated_local_dict(base::Value::Dict()
.Set("new_key", "new_value")
.Set("account_key1", "new_value1")
.Set("local_key1", "local_value1")
.Set("local_key2", "new_value2")
.Set("common_key1", "local_value4")
.Set("common_key2", "new_common_value"));
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kMergeableDictPref1,
updated_local_dict));
// Note: "account_key3" has been deleted.
base::Value updated_account_dict(base::Value::Dict()
.Set("new_key", "new_value")
.Set("account_key1", "new_value1")
.Set("account_key2", "account_value2")
.Set("local_key2", "new_value2")
.Set("common_key1", "local_value4")
.Set("common_key2", "new_common_value"));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(),
kMergeableDictPref1, updated_account_dict));
ASSERT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, updated_dict));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldApplyUpdateOnMergeableListPrefAsNonMergeablePref) {
base::Value local_list(
base::Value::List().Append("local_value").Append("common_value"));
store()->GetLocalPrefStore()->SetValueSilently(kMergeableListPref,
local_list.Clone(), 0);
base::Value account_list(
base::Value::List().Append("account_value").Append("common_value"));
store()->GetAccountPrefStore()->SetValueSilently(kMergeableListPref,
account_list.Clone(), 0);
base::Value merged_list(base::Value::List()
.Append("account_value")
.Append("common_value")
.Append("local_value"));
ASSERT_TRUE(ValueInStoreIs(*store(), kMergeableListPref, merged_list));
base::Value updated_list(base::Value::List()
.Append("local_value")
.Append("account_value")
.Append("common_value"));
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableListPref));
// Writes to both stores.
store()->SetValue(kMergeableListPref, updated_list.Clone(), 0);
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kMergeableListPref,
updated_list));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(),
kMergeableListPref, updated_list));
ASSERT_TRUE(ValueInStoreIs(*store(), kMergeableListPref, updated_list));
}
TEST_F(DualLayerUserPrefStoreMergeTest,
ShouldNotUnmergeIfIncorrectlyMarkedAsMergeableDict) {
base::Value local_dict_value(
base::Value::Dict().Set("local_key", "local_value"));
store()->GetLocalPrefStore()->SetValueSilently(kMergeableDictPref1,
local_dict_value.Clone(), 0);
base::Value account_dict_value(
base::Value::Dict().Set("account_key", "account_value"));
store()->GetAccountPrefStore()->SetValueSilently(
kMergeableDictPref1, account_dict_value.Clone(), 0);
base::Value new_value("new_value");
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableDictPref1));
store()->SetValue(kMergeableDictPref1, new_value.Clone(), 0);
ASSERT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, new_value));
// `kMergeableDictPref1` is considered as incorrectly marked as mergeable and
// is treated as a scalar value and overwritten to both stores.
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kMergeableDictPref1,
new_value));
EXPECT_TRUE(ValueInStoreIs(*store()->GetAccountPrefStore(),
kMergeableDictPref1, new_value));
}
TEST_F(
DualLayerUserPrefStoreMergeTest,
ShouldClearAccountPrefsOnDisableAndNotifyObserversIfEffectiveValueChanges) {
base::Value account_dict(
base::Value::Dict().Set("common_key", "account_value"));
store()->GetAccountPrefStore()->SetValueSilently(kMergeableDictPref1,
account_dict.Clone(), 0);
base::Value local_dict(base::Value::Dict().Set("common_key", "local_value"));
store()->GetLocalPrefStore()->SetValueSilently(kMergeableDictPref1,
local_dict.Clone(), 0);
base::Value merged_dict(
base::Value::Dict().Set("common_key", "account_value"));
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableDictPref1));
EXPECT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, merged_dict));
store()->DisableTypeAndClearAccountStore(syncer::PREFERENCES);
// Removed from account store.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(),
kMergeableDictPref1));
// Local store is not affected.
EXPECT_TRUE(ValueInStoreIs(*store()->GetLocalPrefStore(), kMergeableDictPref1,
local_dict));
// Effective value has changed.
EXPECT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, local_dict));
}
TEST_F(
DualLayerUserPrefStoreMergeTest,
ShouldClearAccountPrefsOnDisableButNotNotifyObserversIfEffectiveValueDoesNotChange) {
base::Value dict(base::Value::Dict().Set("common_key", "common_value"));
store()->GetAccountPrefStore()->SetValueSilently(kMergeableDictPref1,
dict.Clone(), 0);
store()->GetLocalPrefStore()->SetValueSilently(kMergeableDictPref1,
dict.Clone(), 0);
EXPECT_CALL(observer_, OnPrefValueChanged(kMergeableDictPref1)).Times(0);
EXPECT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, dict));
store()->DisableTypeAndClearAccountStore(syncer::PREFERENCES);
// Removed from account store.
EXPECT_TRUE(ValueInStoreIsAbsent(*store()->GetAccountPrefStore(),
kMergeableDictPref1));
// Local store is not affected.
EXPECT_TRUE(
ValueInStoreIs(*store()->GetLocalPrefStore(), kMergeableDictPref1, dict));
// Effective value has changed as the local value is same as the previous
// account value.
EXPECT_TRUE(ValueInStoreIs(*store(), kMergeableDictPref1, dict));
// `observer_` was not notified of any pref change.
}
using DualLayerUserPrefStoreHistoryOptInTest = DualLayerUserPrefStoreTest;
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldReturnHistorySensitivePrefFromLocalStoreIfHistorySyncOff) {
store()->SetIsHistorySyncEnabledForTest(false);
local_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("local value"), 0);
account_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("account value"), 0);
// Check GetValue().
EXPECT_TRUE(
ValueInStoreIs(*store(), kHistorySensitivePrefName, "local value"));
// Check GetMutableValue().
{
base::Value* value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kHistorySensitivePrefName, &value));
EXPECT_EQ(*value, base::Value("local value"));
}
// Check GetValues().
{
base::Value::Dict values = store()->GetValues();
base::Value* value = values.FindByDottedPath(kHistorySensitivePrefName);
ASSERT_TRUE(value);
EXPECT_EQ(*value, base::Value("local value"));
}
// Verify that a change in history sync opt-in is reflected.
store()->SetIsHistorySyncEnabledForTest(true);
EXPECT_TRUE(
ValueInStoreIs(*store(), kHistorySensitivePrefName, "account value"));
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldNotGetHistorySensitivePrefFromAccountStoreIfHistorySyncOff) {
store()->SetIsHistorySyncEnabledForTest(false);
account_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("account value"), 0);
// Check GetValue().
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kHistorySensitivePrefName));
// Check GetMutableValue().
EXPECT_FALSE(store()->GetMutableValue(kHistorySensitivePrefName, nullptr));
// Check GetValues().
base::Value::Dict values = store()->GetValues();
EXPECT_FALSE(values.FindByDottedPath(kHistorySensitivePrefName));
// Verify that a change in history sync opt-in is reflected.
store()->SetIsHistorySyncEnabledForTest(true);
EXPECT_TRUE(
ValueInStoreIs(*store(), kHistorySensitivePrefName, "account value"));
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldGetHistorySensitivePrefFromAccountStoreIfHistorySyncOn) {
store()->SetIsHistorySyncEnabledForTest(true);
local_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("local value"), 0);
account_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("account value"), 0);
// Check GetValue().
EXPECT_TRUE(
ValueInStoreIs(*store(), kHistorySensitivePrefName, "account value"));
// Check GetMutableValue().
{
base::Value* value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kHistorySensitivePrefName, &value));
EXPECT_EQ(*value, base::Value("account value"));
}
// Check GetValues().
{
base::Value::Dict values = store()->GetValues();
base::Value* value = values.FindByDottedPath(kHistorySensitivePrefName);
ASSERT_TRUE(value);
EXPECT_EQ(*value, base::Value("account value"));
}
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldNotSetHistorySensitivePrefInAccountStoreIfHistorySyncIsOff) {
store()->SetIsHistorySyncEnabledForTest(false);
testing::StrictMock<MockPrefStoreObserver> account_store_observer;
account_store()->AddObserver(&account_store_observer);
// No call should be made for `kHistorySensitivePrefName` since history sync
// is off.
EXPECT_CALL(account_store_observer,
OnPrefValueChanged(kHistorySensitivePrefName))
.Times(0);
// Check SetValue().
store()->SetValue(kHistorySensitivePrefName, base::Value("sensitive value1"),
0);
EXPECT_TRUE(
ValueInStoreIsAbsent(*account_store(), kHistorySensitivePrefName));
ASSERT_TRUE(
ValueInStoreIs(*store(), kHistorySensitivePrefName, "sensitive value1"));
// Check SetValueSilently().
store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("sensitive value2"), 0);
EXPECT_TRUE(
ValueInStoreIsAbsent(*account_store(), kHistorySensitivePrefName));
ASSERT_TRUE(
ValueInStoreIs(*store(), kHistorySensitivePrefName, "sensitive value2"));
// Check ReportValueChanged(). Observer is not notified.
base::Value* value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kHistorySensitivePrefName, &value));
*value = base::Value("sensitive value3");
store()->ReportValueChanged(kHistorySensitivePrefName, 0);
EXPECT_TRUE(
ValueInStoreIsAbsent(*account_store(), kHistorySensitivePrefName));
ASSERT_TRUE(
ValueInStoreIs(*store(), kHistorySensitivePrefName, "sensitive value3"));
account_store()->RemoveObserver(&account_store_observer);
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldSetHistorySensitivePrefInAccountStoreIfHistorySyncOn) {
store()->SetIsHistorySyncEnabledForTest(true);
testing::StrictMock<MockPrefStoreObserver> account_store_observer;
account_store()->AddObserver(&account_store_observer);
// Check SetValueSilently().
store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("sensitive value1"), 0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kHistorySensitivePrefName,
"sensitive value1"));
// Check SetValue().
EXPECT_CALL(account_store_observer,
OnPrefValueChanged(kHistorySensitivePrefName));
store()->SetValue(kHistorySensitivePrefName, base::Value("sensitive value2"),
0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kHistorySensitivePrefName,
"sensitive value2"));
// Check ReportValueChanged().
base::Value* value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kHistorySensitivePrefName, &value));
*value = base::Value("sensitive value3");
EXPECT_CALL(account_store_observer,
OnPrefValueChanged(kHistorySensitivePrefName));
store()->ReportValueChanged(kHistorySensitivePrefName, 0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kHistorySensitivePrefName,
"sensitive value3"));
account_store()->RemoveObserver(&account_store_observer);
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldNotRemoveFromAccountStoreUponSetIfHistorySyncOff) {
store()->SetIsHistorySyncEnabledForTest(false);
base::Value account_value("account value");
account_store()->SetValueSilently(kHistorySensitivePrefName,
account_value.Clone(), 0);
testing::StrictMock<MockPrefStoreObserver> account_store_observer;
account_store()->AddObserver(&account_store_observer);
// No call should be made for `kHistorySensitivePrefName` since history sync
// is off.
EXPECT_CALL(account_store_observer,
OnPrefValueChanged(kHistorySensitivePrefName))
.Times(0);
// Check SetValue().
store()->SetValue(kHistorySensitivePrefName, base::Value("sensitive value1"),
0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kHistorySensitivePrefName,
account_value));
ASSERT_TRUE(
ValueInStoreIs(*store(), kHistorySensitivePrefName, "sensitive value1"));
// Check SetValueSilently().
store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("sensitive value2"), 0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kHistorySensitivePrefName,
account_value));
ASSERT_TRUE(
ValueInStoreIs(*store(), kHistorySensitivePrefName, "sensitive value2"));
// Check ReportValueChanged(). Observer is not notified.
base::Value* value = nullptr;
ASSERT_TRUE(store()->GetMutableValue(kHistorySensitivePrefName, &value));
*value = base::Value("sensitive value3");
store()->ReportValueChanged(kHistorySensitivePrefName, 0);
EXPECT_TRUE(ValueInStoreIs(*account_store(), kHistorySensitivePrefName,
account_value));
ASSERT_TRUE(
ValueInStoreIs(*store(), kHistorySensitivePrefName, "sensitive value3"));
account_store()->RemoveObserver(&account_store_observer);
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldNotRemoveFromAccountStoreUponRemoveIfHistorySyncOff) {
store()->SetIsHistorySyncEnabledForTest(false);
base::Value local_value("local value");
local_store()->SetValueSilently(kHistorySensitivePrefName,
local_value.Clone(), 0);
base::Value account_value("account value");
account_store()->SetValueSilently(kHistorySensitivePrefName,
account_value.Clone(), 0);
testing::StrictMock<MockPrefStoreObserver> account_store_observer;
account_store()->AddObserver(&account_store_observer);
// No call should be made for `kHistorySensitivePrefName` since history sync
// is off.
EXPECT_CALL(account_store_observer,
OnPrefValueChanged(kHistorySensitivePrefName))
.Times(0);
// Check RemoveValue().
store()->RemoveValue(kHistorySensitivePrefName, 0);
// Not removed from the account store.
EXPECT_TRUE(ValueInStoreIs(*account_store(), kHistorySensitivePrefName,
account_value));
// But removed from the local store.
EXPECT_TRUE(ValueInStoreIsAbsent(*local_store(), kHistorySensitivePrefName));
// Repopulate the local store.
local_store()->SetValueSilently(kHistorySensitivePrefName,
local_value.Clone(), 0);
// Check RemoveValuesByPrefixSilently().
store()->RemoveValuesByPrefixSilently(kHistorySensitivePrefName);
// Not removed from the account store.
EXPECT_TRUE(ValueInStoreIs(*account_store(), kHistorySensitivePrefName,
account_value));
// But removed from the local store.
EXPECT_TRUE(ValueInStoreIsAbsent(*local_store(), kHistorySensitivePrefName));
account_store()->RemoveObserver(&account_store_observer);
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldCheckHistoryOptInUponSubscribe) {
local_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("local value"), 0);
account_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("account value"), 0);
syncer::TestSyncService sync_service;
ASSERT_FALSE(store()->IsHistorySyncEnabledForTest());
// OnSyncServiceInitialized() should check the history sync opt-in state.
store()->OnSyncServiceInitialized(&sync_service);
EXPECT_TRUE(store()->IsHistorySyncEnabledForTest());
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldListenToHistorySyncDisable) {
local_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("local value"), 0);
account_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("account value"), 0);
syncer::TestSyncService sync_service;
store()->OnSyncServiceInitialized(&sync_service);
ASSERT_TRUE(store()->IsHistorySyncEnabledForTest());
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Turning history sync off should raise notification since effective value of
// `kHistorySensitivePrefName` pref changed.
EXPECT_CALL(observer, OnPrefValueChanged(kHistorySensitivePrefName));
sync_service.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false, syncer::UserSelectableTypeSet());
sync_service.FireStateChanged();
EXPECT_FALSE(store()->IsHistorySyncEnabledForTest());
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldListenToHistorySyncEnable) {
local_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("local value"), 0);
account_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("account value"), 0);
syncer::TestSyncService sync_service;
sync_service.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false, syncer::UserSelectableTypeSet());
ASSERT_FALSE(sync_service.GetUserSettings()->GetSelectedTypes().Has(
syncer::UserSelectableType::kHistory));
store()->OnSyncServiceInitialized(&sync_service);
ASSERT_FALSE(store()->IsHistorySyncEnabledForTest());
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Turning history sync on should raise notification since effective value of
// `kHistorySensitivePrefName` pref changed.
EXPECT_CALL(observer, OnPrefValueChanged(kHistorySensitivePrefName));
sync_service.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false,
syncer::UserSelectableTypeSet({syncer::UserSelectableType::kHistory}));
sync_service.FireStateChanged();
EXPECT_TRUE(store()->IsHistorySyncEnabledForTest());
store()->RemoveObserver(&observer);
}
TEST_F(
DualLayerUserPrefStoreHistoryOptInTest,
ShouldNotNotifyObserversOnHistoryOptInChangeIfEffectiveValueDoesNotChange) {
local_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("common value"), 0);
account_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("common value"), 0);
syncer::TestSyncService sync_service;
sync_service.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false, syncer::UserSelectableTypeSet());
ASSERT_FALSE(sync_service.GetUserSettings()->GetSelectedTypes().Has(
syncer::UserSelectableType::kHistory));
store()->OnSyncServiceInitialized(&sync_service);
ASSERT_FALSE(store()->IsHistorySyncEnabledForTest());
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Turning history sync on should not raise notification since effective value
// of `kHistorySensitivePrefName` pref is unchanged.
EXPECT_CALL(observer, OnPrefValueChanged(kHistorySensitivePrefName)).Times(0);
sync_service.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false,
syncer::UserSelectableTypeSet({syncer::UserSelectableType::kHistory}));
sync_service.FireStateChanged();
EXPECT_TRUE(store()->IsHistorySyncEnabledForTest());
// Turning history sync off should not raise notification since effective
// value of `kHistorySensitivePrefName` pref is unchanged.
EXPECT_CALL(observer, OnPrefValueChanged(kHistorySensitivePrefName)).Times(0);
sync_service.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false, syncer::UserSelectableTypeSet());
sync_service.FireStateChanged();
EXPECT_FALSE(store()->IsHistorySyncEnabledForTest());
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldNotifyObserversOnHistoryOptInChangeIfEffectiveValueChanges) {
account_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("account value"), 0);
syncer::TestSyncService sync_service;
sync_service.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false, syncer::UserSelectableTypeSet());
ASSERT_FALSE(sync_service.GetUserSettings()->GetSelectedTypes().Has(
syncer::UserSelectableType::kHistory));
store()->OnSyncServiceInitialized(&sync_service);
ASSERT_FALSE(store()->IsHistorySyncEnabledForTest());
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Turning history sync on should raise notification since effective value
// of `kHistorySensitivePrefName` pref changes.
EXPECT_CALL(observer, OnPrefValueChanged(kHistorySensitivePrefName)).Times(1);
sync_service.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false,
syncer::UserSelectableTypeSet({syncer::UserSelectableType::kHistory}));
sync_service.FireStateChanged();
EXPECT_TRUE(store()->IsHistorySyncEnabledForTest());
// Turning history sync off should raise notification since effective value
// of `kHistorySensitivePrefName` pref changes.
EXPECT_CALL(observer, OnPrefValueChanged(kHistorySensitivePrefName)).Times(1);
sync_service.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false, syncer::UserSelectableTypeSet());
sync_service.FireStateChanged();
EXPECT_FALSE(store()->IsHistorySyncEnabledForTest());
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldNotReactIfHistoryOptInIsUnchanged) {
local_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("local value"), 0);
account_store()->SetValueSilently(kHistorySensitivePrefName,
base::Value("account value"), 0);
syncer::TestSyncService sync_service;
ASSERT_FALSE(store()->IsHistorySyncEnabledForTest());
store()->OnSyncServiceInitialized(&sync_service);
ASSERT_TRUE(store()->IsHistorySyncEnabledForTest());
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// Should not lead to notification.
sync_service.FireStateChanged();
ASSERT_TRUE(store()->IsHistorySyncEnabledForTest());
store()->RemoveObserver(&observer);
}
TEST_F(DualLayerUserPrefStoreHistoryOptInTest,
ShouldRemoveSensitivePrefsFromAccountStoreUponDisableIfHistorySyncOff) {
store()->SetIsHistorySyncEnabledForTest(false);
base::Value account_value("account value");
account_store()->SetValueSilently(kHistorySensitivePrefName,
account_value.Clone(), 0);
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
// No call should be made for `kHistorySensitivePrefName` since history sync
// is off and the effective is thus unchanged.
EXPECT_CALL(observer, OnPrefValueChanged(kHistorySensitivePrefName)).Times(0);
store()->DisableTypeAndClearAccountStore(syncer::PREFERENCES);
EXPECT_TRUE(
ValueInStoreIsAbsent(*account_store(), kHistorySensitivePrefName));
store()->RemoveObserver(&observer);
}
class DualLayerUserPrefStorePriorityPrefDecoupleTest
: public DualLayerUserPrefStoreTest {
base::test::ScopedFeatureList scoped_feature_list_{
syncer::kSyncSupportAlwaysSyncingPriorityPreferences};
};
TEST_F(DualLayerUserPrefStorePriorityPrefDecoupleTest,
ShouldGetAllowlistedPrefFromAccountStoreIfUserToggleIsOff) {
store()->SetUserSelectedTypesForTest(syncer::UserSelectableTypeSet());
account_store()->SetValueSilently(kPriorityPrefName,
base::Value("account value"), 0);
// Allowlisted pref.
account_store()->SetValueSilently(kAlwaysSyncingPriorityPrefName,
base::Value("account value"), 0);
// Check GetValue().
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kPriorityPrefName));
EXPECT_TRUE(ValueInStoreIs(*store(), kAlwaysSyncingPriorityPrefName,
"account value"));
// Check GetMutableValue().
EXPECT_FALSE(store()->GetMutableValue(kPriorityPrefName, nullptr));
base::Value* value = nullptr;
EXPECT_TRUE(store()->GetMutableValue(kAlwaysSyncingPriorityPrefName, &value));
EXPECT_THAT(value, testing::Pointee(testing::Eq("account value")));
// Check GetValues().
base::Value::Dict values = store()->GetValues();
EXPECT_FALSE(values.FindByDottedPath(kPriorityPrefName));
EXPECT_THAT(values.FindByDottedPath(kAlwaysSyncingPriorityPrefName),
testing::Pointee(testing::Eq("account value")));
}
TEST_F(DualLayerUserPrefStorePriorityPrefDecoupleTest,
ShouldGetRegularPrefFromAccountStoreIfUserToggleIsOn) {
store()->SetUserSelectedTypesForTest(syncer::UserSelectableTypeSet(
{syncer::UserSelectableType::kPreferences}));
account_store()->SetValueSilently(kPriorityPrefName,
base::Value("account value"), 0);
// Allowlisted pref.
account_store()->SetValueSilently(kAlwaysSyncingPriorityPrefName,
base::Value("account value"), 0);
// Check GetValue().
EXPECT_TRUE(ValueInStoreIs(*store(), kPriorityPrefName, "account value"));
EXPECT_TRUE(ValueInStoreIs(*store(), kAlwaysSyncingPriorityPrefName,
"account value"));
// Check GetMutableValue().
base::Value* value = nullptr;
EXPECT_TRUE(store()->GetMutableValue(kPriorityPrefName, &value));
EXPECT_THAT(value, testing::Pointee(testing::Eq("account value")));
EXPECT_TRUE(store()->GetMutableValue(kAlwaysSyncingPriorityPrefName, &value));
EXPECT_THAT(value, testing::Pointee(testing::Eq("account value")));
// Check GetValues().
base::Value::Dict values = store()->GetValues();
EXPECT_THAT(values.FindByDottedPath(kPriorityPrefName),
testing::Pointee(testing::Eq("account value")));
EXPECT_THAT(values.FindByDottedPath(kAlwaysSyncingPriorityPrefName),
testing::Pointee(testing::Eq("account value")));
}
TEST_F(DualLayerUserPrefStorePriorityPrefDecoupleTest,
ShouldObserverUserToggleChange) {
syncer::TestSyncService sync_service;
sync_service.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false, syncer::UserSelectableTypeSet());
store()->OnSyncServiceInitialized(&sync_service);
account_store()->SetValueSilently(kPriorityPrefName,
base::Value("account value"), 0);
// Allowlisted pref.
account_store()->SetValueSilently(kAlwaysSyncingPriorityPrefName,
base::Value("account value"), 0);
EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kPriorityPrefName));
EXPECT_TRUE(ValueInStoreIs(*store(), kAlwaysSyncingPriorityPrefName,
"account value"));
testing::StrictMock<MockPrefStoreObserver> observer;
store()->AddObserver(&observer);
EXPECT_CALL(observer, OnPrefValueChanged(kPriorityPrefName));
EXPECT_CALL(observer, OnPrefValueChanged(kAlwaysSyncingPriorityPrefName))
.Times(0);
sync_service.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false,
syncer::UserSelectableTypeSet(
{syncer::UserSelectableType::kPreferences}));
sync_service.FireStateChanged();
EXPECT_TRUE(ValueInStoreIs(*store(), kPriorityPrefName, "account value"));
EXPECT_TRUE(ValueInStoreIs(*store(), kAlwaysSyncingPriorityPrefName,
"account value"));
store()->RemoveObserver(&observer);
}
// Test to verify that the user selected types are loaded from a pref in the
// local store.
TEST_F(DualLayerUserPrefStorePriorityPrefDecoupleTest,
ShouldGetUserSelectedTypesFromLocalStore) {
// Multiple types.
local_store()->SetValueSilently(kUserSelectedTypesPrefName,
base::Value(base::Value::List()
.Append("passwords")
.Append("preferences")
.Append("typedUrls")),
0);
// Only the interesting types (preferences and history) are returned.
EXPECT_EQ(
store()->GetUserSelectedTypesForTest(),
syncer::UserSelectableTypeSet({syncer::UserSelectableType::kPreferences,
syncer::UserSelectableType::kHistory}));
// Empty list.
local_store()->SetValueSilently(kUserSelectedTypesPrefName,
base::Value(base::Value::List()), 0);
EXPECT_TRUE(store()->GetUserSelectedTypesForTest().empty());
}
// Test to verify that the user selected types are stored in a pref in the local
// store.
TEST_F(DualLayerUserPrefStorePriorityPrefDecoupleTest,
ShouldSetUserSelectedTypesToLocalStore) {
// Multiple types.
store()->SetUserSelectedTypesForTest(
syncer::UserSelectableTypeSet({syncer::UserSelectableType::kPreferences,
syncer::UserSelectableType::kHistory}));
EXPECT_TRUE(ValueInStoreIs(
*local_store(), kUserSelectedTypesPrefName,
base::Value(
base::Value::List().Append("preferences").Append("typedUrls"))));
EXPECT_TRUE(
ValueInStoreIsAbsent(*account_store(), kUserSelectedTypesPrefName));
// Empty list.
store()->SetUserSelectedTypesForTest(syncer::UserSelectableTypeSet());
EXPECT_TRUE(ValueInStoreIs(*local_store(), kUserSelectedTypesPrefName,
base::Value(base::Value::List())));
}
// Test to verify that the user selected types pref is cleared from the local
// store upon sync stop.
TEST_F(DualLayerUserPrefStorePriorityPrefDecoupleTest,
ShouldClearUserSelectedTypesIfLocalStoreUponSyncStop) {
store()->SetUserSelectedTypesForTest(
syncer::UserSelectableTypeSet({syncer::UserSelectableType::kPreferences,
syncer::UserSelectableType::kHistory}));
const base::Value user_selected_types_value(
base::Value::List().Append("preferences").Append("typedUrls"));
// The pref is only cleared after all the data types are disabled.
store()->DisableTypeAndClearAccountStore(syncer::PREFERENCES);
EXPECT_TRUE(ValueInStoreIs(*local_store(), kUserSelectedTypesPrefName,
user_selected_types_value));
store()->DisableTypeAndClearAccountStore(syncer::PRIORITY_PREFERENCES);
#if BUILDFLAG(IS_CHROMEOS)
EXPECT_TRUE(ValueInStoreIs(*local_store(), kUserSelectedTypesPrefName,
user_selected_types_value));
store()->DisableTypeAndClearAccountStore(syncer::OS_PREFERENCES);
store()->DisableTypeAndClearAccountStore(syncer::OS_PRIORITY_PREFERENCES);
#endif
EXPECT_TRUE(ValueInStoreIs(*local_store(), kUserSelectedTypesPrefName,
base::Value(base::Value::List())));
}
} // namespace
} // namespace sync_preferences