| Index: services/preferences/persistent_pref_store_impl_unittest.cc
|
| diff --git a/services/preferences/persistent_pref_store_impl_unittest.cc b/services/preferences/persistent_pref_store_impl_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..806a3428e9b0d8b14c487b5b54fd6db8b71aee5b
|
| --- /dev/null
|
| +++ b/services/preferences/persistent_pref_store_impl_unittest.cc
|
| @@ -0,0 +1,459 @@
|
| +// 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 succeeded));
|
| +};
|
| +
|
| +class PersistentPrefStoreMock : public InMemoryPrefStore {
|
| + public:
|
| + MOCK_METHOD0(CommitPendingWrite, void());
|
| + MOCK_METHOD0(SchedulePendingLossyWrites, void());
|
| + MOCK_METHOD0(ClearMutableValues, void());
|
| +
|
| + private:
|
| + ~PersistentPrefStoreMock() override = default;
|
| +};
|
| +
|
| +class PrefStoreConnectorMock : public mojom::PrefStoreConnector {
|
| + public:
|
| + MOCK_METHOD1(Connect, void(const ConnectCallback&));
|
| +};
|
| +
|
| +class InitializationMockPersistentPrefStore : public InMemoryPrefStore {
|
| + public:
|
| + bool IsInitializationComplete() const override { return initialized_; }
|
| +
|
| + 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);
|
| + }
|
| +
|
| + PersistentPrefStore::PrefReadError GetReadError() const override {
|
| + return read_error_;
|
| + }
|
| + bool ReadOnly() const override { return read_only_; }
|
| +
|
| + void Initialize(bool success,
|
| + PersistentPrefStore::PrefReadError error,
|
| + bool read_only) {
|
| + initialized_ = success;
|
| + read_error_ = error;
|
| + read_only_ = read_only;
|
| + for (auto& observer : observers_) {
|
| + observer.OnInitializationCompleted(initialized_);
|
| + }
|
| + }
|
| +
|
| + private:
|
| + ~InitializationMockPersistentPrefStore() override = default;
|
| +
|
| + PersistentPrefStore::PrefReadError read_error_;
|
| + bool read_only_ = false;
|
| + bool initialized_ = false;
|
| + base::ObserverList<PrefStore::Observer, true> observers_;
|
| +};
|
| +
|
| +class PersistentPrefStoreImplTest : public testing::Test {
|
| + public:
|
| + PersistentPrefStoreImplTest() = default;
|
| +
|
| + // testing::Test:
|
| + void TearDown() override {
|
| + pref_store_ = nullptr;
|
| + base::RunLoop().RunUntilIdle();
|
| + bindings_.CloseAllBindings();
|
| + backing_pref_store_.reset();
|
| + base::RunLoop().RunUntilIdle();
|
| + }
|
| +
|
| + void CreateImpl(scoped_refptr<PersistentPrefStore> backing_pref_store) {
|
| + backing_pref_store_ = base::MakeUnique<PersistentPrefStoreImpl>(
|
| + std::move(backing_pref_store), nullptr);
|
| + mojo::Binding<mojom::PersistentPrefStoreConnector> binding(
|
| + backing_pref_store_.get());
|
| + pref_store_ = CreateConnection();
|
| + }
|
| +
|
| + mojom::PersistentPrefStoreConnectorPtr CreateConnector() {
|
| + return bindings_.CreateInterfacePtrAndBind(backing_pref_store_.get());
|
| + }
|
| +
|
| + scoped_refptr<PersistentPrefStore> CreateConnection() {
|
| + return make_scoped_refptr(new PersistentPrefStoreClient(
|
| + bindings_.CreateInterfacePtrAndBind(backing_pref_store_.get())));
|
| + }
|
| +
|
| + PersistentPrefStore* pref_store() { return pref_store_.get(); }
|
| +
|
| + private:
|
| + base::MessageLoop message_loop_;
|
| +
|
| + std::unique_ptr<PersistentPrefStoreImpl> backing_pref_store_;
|
| + mojo::BindingSet<mojom::PersistentPrefStoreConnector> bindings_;
|
| +
|
| + scoped_refptr<PersistentPrefStore> pref_store_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(PersistentPrefStoreImplTest);
|
| +};
|
| +
|
| +TEST_F(PersistentPrefStoreImplTest, InitializationSuccess) {
|
| + auto backing_pref_store =
|
| + make_scoped_refptr(new InitializationMockPersistentPrefStore);
|
| + CreateImpl(backing_pref_store);
|
| + backing_pref_store->Initialize(
|
| + true, PersistentPrefStore::PREF_READ_ERROR_NONE, false);
|
| + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + pref_store()->ReadPrefs());
|
| + 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);
|
| + CreateImpl(backing_pref_store);
|
| + backing_pref_store->Initialize(
|
| + false, PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE, true);
|
| + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
|
| + pref_store()->ReadPrefs());
|
| + EXPECT_FALSE(pref_store()->IsInitializationComplete());
|
| + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
|
| + pref_store()->GetReadError());
|
| + EXPECT_TRUE(pref_store()->ReadOnly());
|
| +}
|
| +
|
| +class TestReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
|
| + public:
|
| + TestReadErrorDelegate(PersistentPrefStore::PrefReadError* storage,
|
| + const base::Closure& quit)
|
| + : storage_(storage), quit_(quit) {
|
| + DCHECK(storage_);
|
| + DCHECK(quit_);
|
| + }
|
| +
|
| + void OnError(PersistentPrefStore::PrefReadError error) override {
|
| + *storage_ = error;
|
| + quit_.Run();
|
| + }
|
| +
|
| + private:
|
| + PersistentPrefStore::PrefReadError* const storage_;
|
| + const base::Closure quit_;
|
| +};
|
| +
|
| +TEST_F(PersistentPrefStoreImplTest, InitializationFailure_AsyncRead) {
|
| + auto backing_pref_store =
|
| + make_scoped_refptr(new InitializationMockPersistentPrefStore);
|
| + CreateImpl(backing_pref_store);
|
| + backing_pref_store->Initialize(
|
| + false, PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE, true);
|
| + PersistentPrefStore::PrefReadError read_error =
|
| + PersistentPrefStore::PREF_READ_ERROR_NONE;
|
| + base::RunLoop run_loop;
|
| + pref_store()->ReadPrefsAsync(
|
| + new TestReadErrorDelegate(&read_error, run_loop.QuitClosure()));
|
| + run_loop.Run();
|
| + EXPECT_FALSE(pref_store()->IsInitializationComplete());
|
| + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE, read_error);
|
| + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE,
|
| + pref_store()->GetReadError());
|
| + EXPECT_TRUE(pref_store()->ReadOnly());
|
| +}
|
| +
|
| +TEST_F(PersistentPrefStoreImplTest, DelayedInitializationSuccess) {
|
| + auto backing_pref_store =
|
| + make_scoped_refptr(new InitializationMockPersistentPrefStore);
|
| +
|
| + CreateImpl(backing_pref_store);
|
| + auto connector = CreateConnector();
|
| + base::RunLoop run_loop;
|
| + connector->Connect(base::Bind(
|
| + [](const base::Closure& quit,
|
| + PersistentPrefStore::PrefReadError read_error, bool read_only,
|
| + std::unique_ptr<base::DictionaryValue> local_prefs,
|
| + mojom::PersistentPrefStorePtr pref_store,
|
| + mojom::PrefStoreObserverRequest observer_request) {
|
| + quit.Run();
|
| + EXPECT_FALSE(read_only);
|
| + EXPECT_TRUE(local_prefs);
|
| + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, read_error);
|
| + },
|
| + run_loop.QuitClosure()));
|
| + connector.FlushForTesting();
|
| + backing_pref_store->Initialize(
|
| + true, PersistentPrefStore::PREF_READ_ERROR_NONE, false);
|
| + run_loop.Run();
|
| +}
|
| +
|
| +TEST_F(PersistentPrefStoreImplTest, DelayedInitializationFailure) {
|
| + auto backing_pref_store =
|
| + make_scoped_refptr(new InitializationMockPersistentPrefStore);
|
| +
|
| + CreateImpl(backing_pref_store);
|
| + auto connector = CreateConnector();
|
| + base::RunLoop run_loop;
|
| + connector->Connect(base::Bind(
|
| + [](const base::Closure& quit,
|
| + PersistentPrefStore::PrefReadError read_error, bool read_only,
|
| + std::unique_ptr<base::DictionaryValue> local_prefs,
|
| + mojom::PersistentPrefStorePtr pref_store,
|
| + mojom::PrefStoreObserverRequest observer_request) {
|
| + quit.Run();
|
| + EXPECT_TRUE(read_only);
|
| + EXPECT_FALSE(local_prefs);
|
| + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED,
|
| + read_error);
|
| + },
|
| + run_loop.QuitClosure()));
|
| + connector.FlushForTesting();
|
| + backing_pref_store->Initialize(
|
| + false, PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED, true);
|
| + run_loop.Run();
|
| +}
|
| +
|
| +constexpr char kKey[] = "path.to.key";
|
| +
|
| +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);
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + pref_store()->ReadPrefs());
|
| + 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);
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + pref_store()->ReadPrefs());
|
| + 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);
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + pref_store()->ReadPrefs());
|
| + EXPECT_TRUE(pref_store()->IsInitializationComplete());
|
| +
|
| + auto other_pref_store = CreateConnection();
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + other_pref_store->ReadPrefs());
|
| + EXPECT_TRUE(other_pref_store->IsInitializationComplete());
|
| +
|
| + const base::Value value("value");
|
| + pref_store()->SetValueSilently(kKey, value.CreateDeepCopy(), 0);
|
| +
|
| + PrefStoreObserverMock observer;
|
| + other_pref_store->AddObserver(&observer);
|
| + base::RunLoop run_loop;
|
| + EXPECT_CALL(observer, OnPrefValueChanged(kKey))
|
| + .Times(1)
|
| + .WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); })));
|
| + run_loop.Run();
|
| + other_pref_store->RemoveObserver(&observer);
|
| +
|
| + const base::Value* output = nullptr;
|
| + ASSERT_TRUE(other_pref_store->GetValue(kKey, &output));
|
| + EXPECT_TRUE(value.Equals(output));
|
| +}
|
| +
|
| +TEST_F(PersistentPrefStoreImplTest,
|
| + WriteWithoutPathExpansionObservedByOtherClient) {
|
| + auto backing_pref_store = make_scoped_refptr(new InMemoryPrefStore());
|
| + CreateImpl(backing_pref_store);
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + pref_store()->ReadPrefs());
|
| + EXPECT_TRUE(pref_store()->IsInitializationComplete());
|
| +
|
| + auto other_pref_store = CreateConnection();
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + other_pref_store->ReadPrefs());
|
| + EXPECT_TRUE(other_pref_store->IsInitializationComplete());
|
| +
|
| + base::DictionaryValue dict;
|
| + dict.SetStringWithoutPathExpansion(kKey, "value");
|
| + pref_store()->SetValue(kKey, dict.CreateDeepCopy(), 0);
|
| +
|
| + PrefStoreObserverMock observer;
|
| + other_pref_store->AddObserver(&observer);
|
| + base::RunLoop run_loop;
|
| + EXPECT_CALL(observer, OnPrefValueChanged(kKey))
|
| + .Times(1)
|
| + .WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); })));
|
| + run_loop.Run();
|
| + other_pref_store->RemoveObserver(&observer);
|
| +
|
| + 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);
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + pref_store()->ReadPrefs());
|
| + EXPECT_TRUE(pref_store()->IsInitializationComplete());
|
| +
|
| + auto other_pref_store = CreateConnection();
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + other_pref_store->ReadPrefs());
|
| + 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);
|
| +
|
| + PrefStoreObserverMock observer;
|
| + other_pref_store->AddObserver(&observer);
|
| + base::RunLoop run_loop;
|
| + EXPECT_CALL(observer, OnPrefValueChanged(kKey))
|
| + .Times(1)
|
| + .WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); })));
|
| + run_loop.Run();
|
| + base::RunLoop().RunUntilIdle();
|
| + other_pref_store->RemoveObserver(&observer);
|
| +
|
| + 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);
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + pref_store()->ReadPrefs());
|
| + EXPECT_TRUE(pref_store()->IsInitializationComplete());
|
| +
|
| + auto other_pref_store = CreateConnection();
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + other_pref_store->ReadPrefs());
|
| + 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);
|
| +
|
| + PrefStoreObserverMock observer;
|
| + other_pref_store->AddObserver(&observer);
|
| + base::RunLoop run_loop;
|
| + EXPECT_CALL(observer, OnPrefValueChanged(kKey))
|
| + .Times(1)
|
| + .WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); })));
|
| + run_loop.Run();
|
| + other_pref_store->RemoveObserver(&observer);
|
| +
|
| + 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);
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + pref_store()->ReadPrefs());
|
| + 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);
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + pref_store()->ReadPrefs());
|
| + base::RunLoop run_loop;
|
| + EXPECT_CALL(*backing_store, SchedulePendingLossyWrites())
|
| + .Times(1)
|
| + .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);
|
| + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
|
| + pref_store()->ReadPrefs());
|
| + base::RunLoop run_loop;
|
| + EXPECT_CALL(*backing_store, ClearMutableValues())
|
| + .Times(1)
|
| + .WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); })));
|
| + EXPECT_CALL(*backing_store, CommitPendingWrite()).Times(1);
|
| + pref_store()->ClearMutableValues();
|
| + run_loop.Run();
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace prefs
|
|
|