Index: services/preferences/public/cpp/tests/persistent_pref_store_client_unittest.cc |
diff --git a/services/preferences/public/cpp/tests/persistent_pref_store_client_unittest.cc b/services/preferences/public/cpp/tests/persistent_pref_store_client_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..47c408112cc9fe49afc0b7d80b105c34b4a3b2d6 |
--- /dev/null |
+++ b/services/preferences/public/cpp/tests/persistent_pref_store_client_unittest.cc |
@@ -0,0 +1,500 @@ |
+// 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/persistent_pref_store_client.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/pref_notifier_impl.h" |
+#include "components/prefs/pref_registry_simple.h" |
+#include "components/prefs/pref_service.h" |
+#include "mojo/public/cpp/bindings/binding_set.h" |
+#include "services/preferences/public/cpp/scoped_pref_update.h" |
+#include "services/preferences/public/interfaces/preferences.mojom.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace prefs { |
+namespace { |
+ |
+constexpr char kDictionaryKey[] = "path.to.key"; |
+constexpr char kUninitializedDictionaryKey[] = "path.to.an.uninitialized.dict"; |
+ |
+void DoNothingWithReadError(::PersistentPrefStore::PrefReadError read_error) {} |
+ |
+class PersistentPrefStoreClientTest : public testing::Test, |
+ public mojom::PersistentPrefStore { |
+ public: |
+ PersistentPrefStoreClientTest() : binding_(this) {} |
+ |
+ // testing::Test: |
+ void SetUp() override { |
+ auto persistent_pref_store_client = make_scoped_refptr( |
+ new PersistentPrefStoreClient(mojom::PersistentPrefStoreConnection::New( |
+ mojom::PrefStoreConnection::New( |
+ mojom::PrefStoreObserverRequest(), |
+ base::MakeUnique<base::DictionaryValue>(), true), |
+ binding_.CreateInterfacePtrAndBind(), |
+ ::PersistentPrefStore::PREF_READ_ERROR_NONE, false))); |
+ auto pref_registry = make_scoped_refptr(new PrefRegistrySimple()); |
+ pref_registry->RegisterDictionaryPref(kDictionaryKey); |
+ pref_registry->RegisterDictionaryPref(kUninitializedDictionaryKey); |
+ PrefNotifierImpl* pref_notifier = new PrefNotifierImpl; |
+ pref_service_ = base::MakeUnique<PrefService>( |
+ pref_notifier, |
+ new PrefValueStore(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, |
+ nullptr, pref_notifier), |
+ persistent_pref_store_client.get(), pref_registry.get(), |
+ base::Bind(&DoNothingWithReadError), false); |
+ { ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_atomic_update()); |
+ EXPECT_EQ(base::DictionaryValue(), *update->get_atomic_update()); |
+ } |
+ |
+ void TearDown() override { |
+ pref_service_ = nullptr; |
+ base::RunLoop().RunUntilIdle(); |
+ binding_.Close(); |
+ base::RunLoop().RunUntilIdle(); |
+ } |
+ |
+ PrefService* pref_service() { return pref_service_.get(); } |
+ |
+ mojom::PrefUpdateValuePtr WaitForUpdate() { |
+ base::RunLoop run_loop; |
+ on_update_ = run_loop.QuitClosure(); |
+ run_loop.Run(); |
+ EXPECT_EQ(1u, last_updates_.size()); |
+ auto result = std::move(last_updates_[0]->value); |
+ last_updates_.clear(); |
+ return result; |
+ } |
+ |
+ void ExpectNoUpdate() { |
+ binding_.FlushForTesting(); |
+ EXPECT_TRUE(last_updates_.empty()); |
+ } |
+ |
+ private: |
+ void SetValues(std::vector<mojom::PrefUpdatePtr> updates) override { |
+ last_updates_ = std::move(updates); |
+ if (on_update_) |
+ std::move(on_update_).Run(); |
+ } |
+ |
+ void CommitPendingWrite() override {} |
+ void SchedulePendingLossyWrites() override {} |
+ void ClearMutableValues() override {} |
+ |
+ base::MessageLoop message_loop_; |
+ |
+ std::unique_ptr<PrefService> pref_service_; |
+ |
+ mojo::Binding<mojom::PersistentPrefStore> binding_; |
+ |
+ std::vector<mojom::PrefUpdatePtr> last_updates_; |
+ base::OnceClosure on_update_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PersistentPrefStoreClientTest); |
+}; |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_Basic) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_EQ(base::Value(1), *split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path", "to", "integer"}), |
+ split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, |
+ SubPrefUpdates_BasicWithoutPathExpansion) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetIntegerWithoutPathExpansion("key.for.integer", 1); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ EXPECT_EQ(1u, split_updates.size()); |
+ EXPECT_EQ(base::Value(1), *split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"key.for.integer"}), |
+ split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_Remove) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.another_integer", 1); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ WaitForUpdate(); |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->Remove("path.to.integer", nullptr); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_FALSE(split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path", "to", "integer"}), |
+ split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, |
+ SubPrefUpdates_RemoveWithoutPathExpansion) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetIntegerWithoutPathExpansion("path.to.another_integer", 1); |
+ update->SetIntegerWithoutPathExpansion("path.to.integer", 1); |
+ } |
+ WaitForUpdate(); |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->RemoveWithoutPathExpansion("path.to.integer", nullptr); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_FALSE(split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path.to.integer"}), |
+ split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_MultipleUpdates) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetDoubleWithoutPathExpansion("a.double", 1); |
+ } |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 2); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(2u, split_updates.size()); |
+ EXPECT_EQ(base::Value(1.0), *split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"a.double"}), split_updates[0]->path); |
+ EXPECT_EQ(base::Value(2), *split_updates[1]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path", "to", "integer"}), |
+ split_updates[1]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_NestedUpdateAfterSet) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ DictionaryValueUpdate* dict; |
+ ASSERT_TRUE(update->GetDictionary("path.to", &dict)); |
+ dict->Clear(); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_EQ(base::DictionaryValue(), *split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path", "to"}), split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_NestedUpdateBeforeSet) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->Set("path.to", base::MakeUnique<base::DictionaryValue>()); |
+ } |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ base::DictionaryValue expected_value; |
+ expected_value.SetInteger("integer", 1); |
+ EXPECT_EQ(expected_value, *split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path", "to"}), split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_DoubleNestedUpdate) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ DictionaryValueUpdate* dict; |
+ ASSERT_TRUE(update->GetDictionary("path", &dict)); |
+ dict->Clear(); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_EQ(base::DictionaryValue(), *split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path"}), split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_ManualNesting) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ WaitForUpdate(); |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ DictionaryValueUpdate* dict = nullptr; |
+ ASSERT_TRUE(update->GetDictionary("path.to", &dict)); |
+ dict->SetString("string", "string value"); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_EQ(base::Value("string value"), *split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path", "to", "string"}), |
+ split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, |
+ SubPrefUpdates_ManualDictCreationAndNesting) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ auto* dict = update->SetDictionary( |
+ "path.to", base::MakeUnique<base::DictionaryValue>()); |
+ dict->SetString("string", "string value"); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ base::DictionaryValue expected_value; |
+ expected_value.SetString("string", "string value"); |
+ EXPECT_EQ(expected_value, *split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path", "to"}), split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, |
+ SubPrefUpdates_ManualDictCreationWithoutPathExpansionAndNesting) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ auto* dict = update->SetDictionaryWithoutPathExpansion( |
+ "a.dictionary", base::MakeUnique<base::DictionaryValue>()); |
+ dict->SetStringWithoutPathExpansion("a.string", "string value"); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ base::DictionaryValue expected_value; |
+ expected_value.SetStringWithoutPathExpansion("a.string", "string value"); |
+ EXPECT_EQ(expected_value, *split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"a.dictionary"}), split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, |
+ SubPrefUpdates_AsDictionaryTriggersFullWrite) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ update->AsDictionary(); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_atomic_update()); |
+ base::DictionaryValue expected_value; |
+ expected_value.SetInteger("path.to.integer", 1); |
+ EXPECT_EQ(expected_value, *update->get_atomic_update()); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_AsConstDictionaryIsNoOp) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ update->AsConstDictionary(); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_EQ(base::Value(1), *split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path", "to", "integer"}), |
+ split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_RemovePath_Basic) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ update->SetInteger("path.to.something.else", 1); |
+ } |
+ WaitForUpdate(); |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->RemovePath("path.to.integer", nullptr); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_FALSE(split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path", "to", "integer"}), |
+ split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, |
+ SubPrefUpdates_RemovePath_RemoveContainingDict) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ update->SetInteger("path.for.something.else", 1); |
+ } |
+ WaitForUpdate(); |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->RemovePath("path.to.integer", nullptr); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_FALSE(split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path", "to"}), split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, |
+ SubPrefUpdates_RemovePath_SinglePathComponent) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("integer", 1); |
+ update->SetInteger("something_else", 1); |
+ } |
+ WaitForUpdate(); |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->RemovePath("integer", nullptr); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_FALSE(split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"integer"}), split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_RemovePath_FullPref) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ WaitForUpdate(); |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->RemovePath("path.to.integer", nullptr); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_FALSE(split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path"}), split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, |
+ SubPrefUpdates_RemovePathSinglePathComponent_FullPref) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("integer", 1); |
+ } |
+ WaitForUpdate(); |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->RemovePath("integer", nullptr); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_FALSE(split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"integer"}), split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_NoChange) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ WaitForUpdate(); |
+ { ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); } |
+ ExpectNoUpdate(); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_SetToExistingValue) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ WaitForUpdate(); |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ ExpectNoUpdate(); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_ClearEmptyDictionary) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->Clear(); |
+ } |
+ ExpectNoUpdate(); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_ReplaceDictionary) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ WaitForUpdate(); |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), kDictionaryKey); |
+ update->SetInteger("path", 2); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_split_updates()); |
+ auto& split_updates = update->get_split_updates(); |
+ ASSERT_EQ(1u, split_updates.size()); |
+ EXPECT_EQ(base::Value(2), *split_updates[0]->value); |
+ EXPECT_EQ((std::vector<std::string>{"path"}), split_updates[0]->path); |
+} |
+ |
+TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_Uninitialized) { |
+ { |
+ ScopedDictionaryPrefUpdate update(pref_service(), |
+ kUninitializedDictionaryKey); |
+ update->SetInteger("path.to.integer", 1); |
+ } |
+ auto update = WaitForUpdate(); |
+ ASSERT_TRUE(update->is_atomic_update()); |
+ base::DictionaryValue expected_value; |
+ expected_value.SetInteger("path.to.integer", 1); |
+ EXPECT_EQ(expected_value, *update->get_atomic_update()); |
+} |
+ |
+} // namespace |
+} // namespace prefs |