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 |