blob: 7a326276c0a76ff2b8fe5ad258de217afdf93bc7 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/preferences/persistent_pref_store_impl.h"
#include <utility>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/values.h"
#include "components/prefs/in_memory_pref_store.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/preferences/public/cpp/persistent_pref_store_client.h"
#include "services/preferences/public/interfaces/preferences.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::Invoke;
using testing::WithoutArgs;
namespace prefs {
namespace {
class PrefStoreObserverMock : public PrefStore::Observer {
public:
MOCK_METHOD1(OnPrefValueChanged, void(const std::string&));
MOCK_METHOD1(OnInitializationCompleted, void(bool));
};
class PersistentPrefStoreMock : public InMemoryPrefStore {
public:
MOCK_METHOD0(CommitPendingWrite, void());
MOCK_METHOD0(SchedulePendingLossyWrites, void());
MOCK_METHOD0(ClearMutableValues, void());
private:
~PersistentPrefStoreMock() override = default;
};
void ExpectPrefChange(PrefStore* pref_store, base::StringPiece key) {
PrefStoreObserverMock observer;
pref_store->AddObserver(&observer);
base::RunLoop run_loop;
EXPECT_CALL(observer, OnPrefValueChanged(key.as_string()))
.WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); })));
run_loop.Run();
pref_store->RemoveObserver(&observer);
}
class InitializationMockPersistentPrefStore : public InMemoryPrefStore {
public:
InitializationMockPersistentPrefStore(
bool success,
PersistentPrefStore::PrefReadError error,
bool read_only)
: success_(success), read_error_(error), read_only_(read_only) {}
bool IsInitializationComplete() const override {
return initialized_ && success_;
}
void AddObserver(PrefStore::Observer* observer) override {
observers_.AddObserver(observer);
}
void RemoveObserver(PrefStore::Observer* observer) override {
observers_.RemoveObserver(observer);
}
void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override {
DCHECK(!error_delegate);
DCHECK(!initialized_);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&InitializationMockPersistentPrefStore::CompleteRead, this));
}
void CompleteRead() {
initialized_ = true;
for (auto& observer : observers_) {
observer.OnInitializationCompleted(success_);
}
}
PersistentPrefStore::PrefReadError GetReadError() const override {
return read_error_;
}
bool ReadOnly() const override { return read_only_; }
private:
~InitializationMockPersistentPrefStore() override = default;
bool initialized_ = false;
bool success_;
PersistentPrefStore::PrefReadError read_error_;
bool read_only_;
base::ObserverList<PrefStore::Observer, true> observers_;
};
constexpr char kKey[] = "path.to.key";
constexpr char kOtherKey[] = "path.to.other_key";
class PersistentPrefStoreImplTest : public testing::Test {
public:
PersistentPrefStoreImplTest() = default;
// testing::Test:
void TearDown() override {
pref_store_ = nullptr;
base::RunLoop().RunUntilIdle();
impl_.reset();
base::RunLoop().RunUntilIdle();
}
void CreateImpl(scoped_refptr<PersistentPrefStore> backing_pref_store) {
base::RunLoop run_loop;
bool initialized = backing_pref_store->IsInitializationComplete();
impl_ = base::MakeUnique<PersistentPrefStoreImpl>(
std::move(backing_pref_store), run_loop.QuitClosure());
if (!initialized)
run_loop.Run();
pref_store_ = CreateConnection();
}
scoped_refptr<PersistentPrefStore> CreateConnection(
PersistentPrefStoreImpl::ObservedPrefs observed_prefs =
PersistentPrefStoreImpl::ObservedPrefs()) {
if (observed_prefs.empty())
observed_prefs.insert({kKey, kOtherKey});
return make_scoped_refptr(new PersistentPrefStoreClient(
impl_->CreateConnection(std::move(observed_prefs))));
}
PersistentPrefStore* pref_store() { return pref_store_.get(); }
private:
base::MessageLoop message_loop_;
std::unique_ptr<PersistentPrefStoreImpl> impl_;
scoped_refptr<PersistentPrefStore> pref_store_;
DISALLOW_COPY_AND_ASSIGN(PersistentPrefStoreImplTest);
};
TEST_F(PersistentPrefStoreImplTest, InitializationSuccess) {
auto backing_pref_store =
make_scoped_refptr(new InitializationMockPersistentPrefStore(
true, PersistentPrefStore::PREF_READ_ERROR_NONE, false));
CreateImpl(backing_pref_store);
EXPECT_TRUE(pref_store()->IsInitializationComplete());
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
pref_store()->GetReadError());
EXPECT_FALSE(pref_store()->ReadOnly());
}
TEST_F(PersistentPrefStoreImplTest, InitializationFailure) {
auto backing_pref_store =
make_scoped_refptr(new InitializationMockPersistentPrefStore(
false, PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE, true));
CreateImpl(backing_pref_store);
EXPECT_FALSE(pref_store()->IsInitializationComplete());
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
pref_store()->GetReadError());
EXPECT_TRUE(pref_store()->ReadOnly());
}
TEST_F(PersistentPrefStoreImplTest, InitialValue) {
auto backing_pref_store = make_scoped_refptr(new InMemoryPrefStore());
const base::Value value("value");
backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0);
CreateImpl(backing_pref_store);
EXPECT_TRUE(pref_store()->IsInitializationComplete());
const base::Value* output = nullptr;
ASSERT_TRUE(pref_store()->GetValue(kKey, &output));
EXPECT_TRUE(value.Equals(output));
}
TEST_F(PersistentPrefStoreImplTest, InitialValueWithoutPathExpansion) {
auto backing_pref_store = make_scoped_refptr(new InMemoryPrefStore());
base::DictionaryValue dict;
dict.SetStringWithoutPathExpansion(kKey, "value");
backing_pref_store->SetValue(kKey, dict.CreateDeepCopy(), 0);
CreateImpl(backing_pref_store);
EXPECT_TRUE(pref_store()->IsInitializationComplete());
const base::Value* output = nullptr;
ASSERT_TRUE(pref_store()->GetValue(kKey, &output));
EXPECT_TRUE(dict.Equals(output));
}
TEST_F(PersistentPrefStoreImplTest, WriteObservedByOtherClient) {
auto backing_pref_store = make_scoped_refptr(new InMemoryPrefStore());
CreateImpl(backing_pref_store);
EXPECT_TRUE(pref_store()->IsInitializationComplete());
auto other_pref_store = CreateConnection();
EXPECT_TRUE(other_pref_store->IsInitializationComplete());
const base::Value value("value");
pref_store()->SetValueSilently(kKey, value.CreateDeepCopy(), 0);
ExpectPrefChange(other_pref_store.get(), kKey);
const base::Value* output = nullptr;
ASSERT_TRUE(other_pref_store->GetValue(kKey, &output));
EXPECT_TRUE(value.Equals(output));
}
TEST_F(PersistentPrefStoreImplTest, UnregisteredPrefNotObservedByOtherClient) {
auto backing_pref_store = make_scoped_refptr(new InMemoryPrefStore());
CreateImpl(backing_pref_store);
EXPECT_TRUE(pref_store()->IsInitializationComplete());
PersistentPrefStoreImpl::ObservedPrefs observed_prefs;
observed_prefs.insert(kKey);
auto other_pref_store = CreateConnection(std::move(observed_prefs));
EXPECT_TRUE(other_pref_store->IsInitializationComplete());
pref_store()->SetValue(kOtherKey, base::MakeUnique<base::Value>(123), 0);
pref_store()->SetValue(kKey, base::MakeUnique<base::Value>("value"), 0);
ExpectPrefChange(other_pref_store.get(), kKey);
EXPECT_FALSE(other_pref_store->GetValue(kOtherKey, nullptr));
}
TEST_F(PersistentPrefStoreImplTest,
WriteWithoutPathExpansionObservedByOtherClient) {
auto backing_pref_store = make_scoped_refptr(new InMemoryPrefStore());
CreateImpl(backing_pref_store);
EXPECT_TRUE(pref_store()->IsInitializationComplete());
auto other_pref_store = CreateConnection();
EXPECT_TRUE(other_pref_store->IsInitializationComplete());
base::DictionaryValue dict;
dict.SetStringWithoutPathExpansion(kKey, "value");
pref_store()->SetValue(kKey, dict.CreateDeepCopy(), 0);
ExpectPrefChange(other_pref_store.get(), kKey);
const base::Value* output = nullptr;
ASSERT_TRUE(other_pref_store->GetValue(kKey, &output));
EXPECT_TRUE(dict.Equals(output));
}
TEST_F(PersistentPrefStoreImplTest, RemoveObservedByOtherClient) {
auto backing_pref_store = make_scoped_refptr(new InMemoryPrefStore());
const base::Value value("value");
backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0);
CreateImpl(backing_pref_store);
EXPECT_TRUE(pref_store()->IsInitializationComplete());
auto other_pref_store = CreateConnection();
EXPECT_TRUE(other_pref_store->IsInitializationComplete());
const base::Value* output = nullptr;
ASSERT_TRUE(other_pref_store->GetValue(kKey, &output));
EXPECT_TRUE(value.Equals(output));
pref_store()->RemoveValue(kKey, 0);
// This should be a no-op and shouldn't trigger a notification for the other
// client.
pref_store()->RemoveValue(kKey, 0);
ExpectPrefChange(other_pref_store.get(), kKey);
EXPECT_FALSE(other_pref_store->GetValue(kKey, &output));
}
TEST_F(PersistentPrefStoreImplTest,
RemoveWithoutPathExpansionObservedByOtherClient) {
auto backing_pref_store = make_scoped_refptr(new InMemoryPrefStore());
base::DictionaryValue dict;
dict.SetStringWithoutPathExpansion(kKey, "value");
backing_pref_store->SetValue(kKey, dict.CreateDeepCopy(), 0);
CreateImpl(backing_pref_store);
EXPECT_TRUE(pref_store()->IsInitializationComplete());
auto other_pref_store = CreateConnection();
EXPECT_TRUE(other_pref_store->IsInitializationComplete());
const base::Value* output = nullptr;
ASSERT_TRUE(other_pref_store->GetValue(kKey, &output));
EXPECT_TRUE(dict.Equals(output));
base::Value* mutable_value = nullptr;
dict.SetStringWithoutPathExpansion(kKey, "value");
ASSERT_TRUE(pref_store()->GetMutableValue(kKey, &mutable_value));
base::DictionaryValue* mutable_dict = nullptr;
ASSERT_TRUE(mutable_value->GetAsDictionary(&mutable_dict));
mutable_dict->RemoveWithoutPathExpansion(kKey, nullptr);
pref_store()->ReportValueChanged(kKey, 0);
ExpectPrefChange(other_pref_store.get(), kKey);
ASSERT_TRUE(other_pref_store->GetValue(kKey, &output));
const base::DictionaryValue* dict_value = nullptr;
ASSERT_TRUE(output->GetAsDictionary(&dict_value));
EXPECT_TRUE(dict_value->empty());
}
TEST_F(PersistentPrefStoreImplTest, CommitPendingWrite) {
auto backing_store = make_scoped_refptr(new PersistentPrefStoreMock);
CreateImpl(backing_store);
base::RunLoop run_loop;
EXPECT_CALL(*backing_store, CommitPendingWrite())
.Times(2)
.WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); })));
pref_store()->CommitPendingWrite();
run_loop.Run();
}
TEST_F(PersistentPrefStoreImplTest, SchedulePendingLossyWrites) {
auto backing_store = make_scoped_refptr(new PersistentPrefStoreMock);
CreateImpl(backing_store);
base::RunLoop run_loop;
EXPECT_CALL(*backing_store, SchedulePendingLossyWrites())
.WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); })));
EXPECT_CALL(*backing_store, CommitPendingWrite()).Times(1);
pref_store()->SchedulePendingLossyWrites();
run_loop.Run();
}
TEST_F(PersistentPrefStoreImplTest, ClearMutableValues) {
auto backing_store = make_scoped_refptr(new PersistentPrefStoreMock);
CreateImpl(backing_store);
base::RunLoop run_loop;
EXPECT_CALL(*backing_store, ClearMutableValues())
.WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); })));
EXPECT_CALL(*backing_store, CommitPendingWrite()).Times(1);
pref_store()->ClearMutableValues();
run_loop.Run();
}
} // namespace
} // namespace prefs