| Index: services/preferences/public/cpp/tests/pref_store_impl_unittest.cc
|
| diff --git a/services/preferences/public/cpp/tests/pref_store_impl_unittest.cc b/services/preferences/public/cpp/tests/pref_store_impl_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e35a9d869eef32f9421b0c7ed7df6e9b8aa8ba89
|
| --- /dev/null
|
| +++ b/services/preferences/public/cpp/tests/pref_store_impl_unittest.cc
|
| @@ -0,0 +1,325 @@
|
| +// 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/public/cpp/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/value_map_pref_store.h"
|
| +#include "mojo/public/cpp/bindings/binding_set.h"
|
| +#include "services/preferences/public/cpp/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 MockPrefStore : public ValueMapPrefStore {
|
| + public:
|
| + 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 CompleteInitialization(bool success) {
|
| + initialized_ = true;
|
| + success_ = success;
|
| + for (auto& observer : observers_) {
|
| + // Some pref stores report completing initialization more than once. Test
|
| + // that additional calls are ignored.
|
| + observer.OnInitializationCompleted(success);
|
| + observer.OnInitializationCompleted(success);
|
| + }
|
| + }
|
| +
|
| + void SetValue(const std::string& key,
|
| + std::unique_ptr<base::Value> value,
|
| + uint32_t flags) override {
|
| + ValueMapPrefStore::SetValue(key, std::move(value), flags);
|
| + for (auto& observer : observers_) {
|
| + observer.OnPrefValueChanged(key);
|
| + }
|
| + }
|
| +
|
| + private:
|
| + ~MockPrefStore() override = default;
|
| +
|
| + bool initialized_ = false;
|
| + bool success_ = false;
|
| + base::ObserverList<PrefStore::Observer, true> observers_;
|
| +};
|
| +
|
| +constexpr char kKey[] = "path.to.key";
|
| +constexpr char kOtherKey[] = "path.to.other_key";
|
| +
|
| +void ExpectInitializationComplete(PrefStore* pref_store, bool success) {
|
| + PrefStoreObserverMock observer;
|
| + pref_store->AddObserver(&observer);
|
| + base::RunLoop run_loop;
|
| + EXPECT_CALL(observer, OnPrefValueChanged("")).Times(0);
|
| + EXPECT_CALL(observer, OnInitializationCompleted(success))
|
| + .WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); })));
|
| + run_loop.Run();
|
| + pref_store->RemoveObserver(&observer);
|
| +}
|
| +
|
| +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 PrefStoreImplTest : public testing::Test {
|
| + public:
|
| + PrefStoreImplTest() = default;
|
| +
|
| + // testing::Test:
|
| + void TearDown() override {
|
| + pref_store_ = nullptr;
|
| + base::RunLoop().RunUntilIdle();
|
| + pref_store_ptr_.reset();
|
| + base::RunLoop().RunUntilIdle();
|
| + impl_.reset();
|
| + base::RunLoop().RunUntilIdle();
|
| + }
|
| +
|
| + void CreateImpl(
|
| + scoped_refptr<PrefStore> backing_pref_store,
|
| + std::vector<std::string> observed_prefs = std::vector<std::string>()) {
|
| + impl_ = base::MakeUnique<PrefStoreImpl>(
|
| + std::move(backing_pref_store), mojo::MakeRequest(&pref_store_ptr_));
|
| +
|
| + if (observed_prefs.empty())
|
| + observed_prefs.insert(observed_prefs.end(), {kKey, kOtherKey});
|
| + pref_store_ = CreateConnection(std::move(observed_prefs));
|
| + }
|
| +
|
| + scoped_refptr<PrefStore> CreateConnection(
|
| + std::vector<std::string> observed_prefs) {
|
| + base::RunLoop run_loop;
|
| + mojom::PrefStoreConnectionPtr connection;
|
| + pref_store_ptr_->AddObserver(
|
| + observed_prefs,
|
| + base::Bind(
|
| + [](mojom::PrefStoreConnectionPtr* output, base::OnceClosure quit,
|
| + mojom::PrefStoreConnectionPtr connection) {
|
| + std::move(quit).Run();
|
| + *output = std::move(connection);
|
| + },
|
| + &connection, run_loop.QuitClosure()));
|
| + run_loop.Run();
|
| + return make_scoped_refptr(new PrefStoreClient(std::move(connection)));
|
| + }
|
| +
|
| + PrefStore* pref_store() { return pref_store_.get(); }
|
| +
|
| + private:
|
| + base::MessageLoop message_loop_;
|
| +
|
| + std::unique_ptr<PrefStoreImpl> impl_;
|
| + mojom::PrefStorePtr pref_store_ptr_;
|
| +
|
| + scoped_refptr<PrefStore> pref_store_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(PrefStoreImplTest);
|
| +};
|
| +
|
| +TEST_F(PrefStoreImplTest, InitializationSuccess) {
|
| + auto backing_pref_store = make_scoped_refptr(new MockPrefStore());
|
| + backing_pref_store->SetValue(kKey, base::MakeUnique<base::Value>("value"), 0);
|
| + CreateImpl(backing_pref_store);
|
| + EXPECT_FALSE(pref_store()->IsInitializationComplete());
|
| +
|
| + backing_pref_store->CompleteInitialization(true);
|
| + ExpectInitializationComplete(pref_store(), true);
|
| + EXPECT_TRUE(pref_store()->IsInitializationComplete());
|
| +}
|
| +
|
| +TEST_F(PrefStoreImplTest, InitializationFailure) {
|
| + auto backing_pref_store = make_scoped_refptr(new MockPrefStore());
|
| + backing_pref_store->SetValue(kKey, base::MakeUnique<base::Value>("value"), 0);
|
| + CreateImpl(backing_pref_store);
|
| + EXPECT_FALSE(pref_store()->IsInitializationComplete());
|
| +
|
| + backing_pref_store->CompleteInitialization(false);
|
| + ExpectInitializationComplete(pref_store(), false);
|
| +
|
| + // TODO(sammc): Should IsInitializationComplete() return false?
|
| + EXPECT_TRUE(pref_store()->IsInitializationComplete());
|
| +}
|
| +
|
| +TEST_F(PrefStoreImplTest, ValueChangesBeforeInitializationCompletes) {
|
| + auto backing_pref_store = make_scoped_refptr(new MockPrefStore());
|
| + CreateImpl(backing_pref_store);
|
| + EXPECT_FALSE(pref_store()->IsInitializationComplete());
|
| +
|
| + const base::Value value("value");
|
| + backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0);
|
| + backing_pref_store->CompleteInitialization(true);
|
| +
|
| + // The update occurs before initialization has completed, so should not
|
| + // trigger notifications to client observers, but the value should be
|
| + // observable once initialization completes.
|
| + ExpectInitializationComplete(pref_store(), true);
|
| + EXPECT_TRUE(pref_store()->IsInitializationComplete());
|
| +
|
| + const base::Value* output = nullptr;
|
| + ASSERT_TRUE(pref_store()->GetValue(kKey, &output));
|
| + EXPECT_TRUE(value.Equals(output));
|
| +}
|
| +
|
| +TEST_F(PrefStoreImplTest, InitialValue) {
|
| + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore());
|
| + const base::Value value("value");
|
| + backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0);
|
| + CreateImpl(backing_pref_store);
|
| + ASSERT_TRUE(pref_store()->IsInitializationComplete());
|
| + const base::Value* output = nullptr;
|
| + ASSERT_TRUE(pref_store()->GetValue(kKey, &output));
|
| + EXPECT_TRUE(value.Equals(output));
|
| +}
|
| +
|
| +TEST_F(PrefStoreImplTest, InitialValueWithoutPathExpansion) {
|
| + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore());
|
| + base::DictionaryValue dict;
|
| + dict.SetStringWithoutPathExpansion(kKey, "value");
|
| + backing_pref_store->SetValue(kKey, dict.CreateDeepCopy(), 0);
|
| + CreateImpl(backing_pref_store);
|
| + ASSERT_TRUE(pref_store()->IsInitializationComplete());
|
| + const base::Value* output = nullptr;
|
| + ASSERT_TRUE(pref_store()->GetValue(kKey, &output));
|
| + EXPECT_TRUE(dict.Equals(output));
|
| +}
|
| +
|
| +TEST_F(PrefStoreImplTest, WriteObservedByClient) {
|
| + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore());
|
| + CreateImpl(backing_pref_store);
|
| + ASSERT_TRUE(pref_store()->IsInitializationComplete());
|
| +
|
| + const base::Value value("value");
|
| + backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0);
|
| +
|
| + ExpectPrefChange(pref_store(), kKey);
|
| + const base::Value* output = nullptr;
|
| + ASSERT_TRUE(pref_store()->GetValue(kKey, &output));
|
| + EXPECT_TRUE(value.Equals(output));
|
| +}
|
| +
|
| +TEST_F(PrefStoreImplTest, WriteToUnregisteredPrefNotObservedByClient) {
|
| + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore());
|
| + CreateImpl(backing_pref_store, {kKey});
|
| + ASSERT_TRUE(pref_store()->IsInitializationComplete());
|
| +
|
| + backing_pref_store->SetValue(kOtherKey, base::MakeUnique<base::Value>(123),
|
| + 0);
|
| + backing_pref_store->SetValue(kKey, base::MakeUnique<base::Value>("value"), 0);
|
| +
|
| + ExpectPrefChange(pref_store(), kKey);
|
| + EXPECT_FALSE(pref_store()->GetValue(kOtherKey, nullptr));
|
| +}
|
| +
|
| +TEST_F(PrefStoreImplTest, WriteWithoutPathExpansionObservedByClient) {
|
| + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore());
|
| + CreateImpl(backing_pref_store);
|
| + ASSERT_TRUE(pref_store()->IsInitializationComplete());
|
| +
|
| + base::DictionaryValue dict;
|
| + dict.SetStringWithoutPathExpansion(kKey, "value");
|
| + backing_pref_store->SetValue(kKey, dict.CreateDeepCopy(), 0);
|
| +
|
| + ExpectPrefChange(pref_store(), kKey);
|
| + const base::Value* output = nullptr;
|
| + ASSERT_TRUE(pref_store()->GetValue(kKey, &output));
|
| + EXPECT_TRUE(dict.Equals(output));
|
| +}
|
| +
|
| +TEST_F(PrefStoreImplTest, RemoveObservedByClient) {
|
| + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore());
|
| + const base::Value value("value");
|
| + backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0);
|
| + CreateImpl(backing_pref_store);
|
| + ASSERT_TRUE(pref_store()->IsInitializationComplete());
|
| +
|
| + const base::Value* output = nullptr;
|
| + ASSERT_TRUE(pref_store()->GetValue(kKey, &output));
|
| + EXPECT_TRUE(value.Equals(output));
|
| + backing_pref_store->RemoveValue(kKey, 0);
|
| +
|
| + // This should be a no-op and shouldn't trigger a notification for the other
|
| + // client.
|
| + backing_pref_store->RemoveValue(kKey, 0);
|
| +
|
| + ExpectPrefChange(pref_store(), kKey);
|
| + EXPECT_FALSE(pref_store()->GetValue(kKey, &output));
|
| +}
|
| +
|
| +TEST_F(PrefStoreImplTest, RemoveOfUnregisteredPrefNotObservedByClient) {
|
| + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore());
|
| + const base::Value value("value");
|
| + backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0);
|
| + backing_pref_store->SetValue(kOtherKey, value.CreateDeepCopy(), 0);
|
| + CreateImpl(backing_pref_store, {kKey});
|
| + ASSERT_TRUE(pref_store()->IsInitializationComplete());
|
| +
|
| + backing_pref_store->RemoveValue(kOtherKey, 0);
|
| + backing_pref_store->RemoveValue(kKey, 0);
|
| +
|
| + ExpectPrefChange(pref_store(), kKey);
|
| +}
|
| +
|
| +TEST_F(PrefStoreImplTest, RemoveWithoutPathExpansionObservedByOtherClient) {
|
| + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore());
|
| + base::DictionaryValue dict;
|
| + dict.SetStringWithoutPathExpansion(kKey, "value");
|
| + backing_pref_store->SetValue(kKey, dict.CreateDeepCopy(), 0);
|
| + CreateImpl(backing_pref_store);
|
| + ASSERT_TRUE(pref_store()->IsInitializationComplete());
|
| +
|
| + const base::Value* output = nullptr;
|
| + ASSERT_TRUE(pref_store()->GetValue(kKey, &output));
|
| + EXPECT_TRUE(dict.Equals(output));
|
| +
|
| + base::Value* mutable_value = nullptr;
|
| + dict.SetStringWithoutPathExpansion(kKey, "value");
|
| + ASSERT_TRUE(backing_pref_store->GetMutableValue(kKey, &mutable_value));
|
| + base::DictionaryValue* mutable_dict = nullptr;
|
| + ASSERT_TRUE(mutable_value->GetAsDictionary(&mutable_dict));
|
| + mutable_dict->RemoveWithoutPathExpansion(kKey, nullptr);
|
| + backing_pref_store->ReportValueChanged(kKey, 0);
|
| +
|
| + ExpectPrefChange(pref_store(), kKey);
|
| + ASSERT_TRUE(pref_store()->GetValue(kKey, &output));
|
| + const base::DictionaryValue* dict_value = nullptr;
|
| + ASSERT_TRUE(output->GetAsDictionary(&dict_value));
|
| + EXPECT_TRUE(dict_value->empty());
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace prefs
|
|
|