[prefs] Implement WrapWithPrefixPrefStore
This operates on a dictionary managed by another PersistentPrefStore. It works by prefixing all the queries with the provided prefix. This should allow having multiple pref stores work on the same underlying file. Bug: 336776819 Change-Id: I3445b562eb0eba28b7cbf8fbdb362ea5d33b67dd Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5512607 Reviewed-by: Gabriel Charette <gab@chromium.org> Commit-Queue: Ankush Singh <ankushkush@google.com> Cr-Commit-Position: refs/heads/main@{#1301248}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
8188600e6d
commit
35121a04e8
@@ -49,6 +49,8 @@ component("prefs") {
|
|||||||
"segregated_pref_store.h",
|
"segregated_pref_store.h",
|
||||||
"value_map_pref_store.cc",
|
"value_map_pref_store.cc",
|
||||||
"value_map_pref_store.h",
|
"value_map_pref_store.h",
|
||||||
|
"wrap_with_prefix_pref_store.cc",
|
||||||
|
"wrap_with_prefix_pref_store.h",
|
||||||
"writeable_pref_store.cc",
|
"writeable_pref_store.cc",
|
||||||
"writeable_pref_store.h",
|
"writeable_pref_store.h",
|
||||||
]
|
]
|
||||||
@@ -109,6 +111,7 @@ source_set("unit_tests") {
|
|||||||
"pref_value_store_unittest.cc",
|
"pref_value_store_unittest.cc",
|
||||||
"scoped_user_pref_update_unittest.cc",
|
"scoped_user_pref_update_unittest.cc",
|
||||||
"segregated_pref_store_unittest.cc",
|
"segregated_pref_store_unittest.cc",
|
||||||
|
"wrap_with_prefix_pref_store_unittest.cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
|
175
components/prefs/wrap_with_prefix_pref_store.cc
Normal file
175
components/prefs/wrap_with_prefix_pref_store.cc
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
// Copyright 2024 The Chromium Authors
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "components/prefs/wrap_with_prefix_pref_store.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "base/strings/strcat.h"
|
||||||
|
#include "base/strings/string_util.h"
|
||||||
|
|
||||||
|
WrapWithPrefixPrefStore::WrapWithPrefixPrefStore(
|
||||||
|
scoped_refptr<PersistentPrefStore> target_pref_store,
|
||||||
|
std::string_view path_prefix)
|
||||||
|
: target_pref_store_(std::move(target_pref_store)),
|
||||||
|
dotted_prefix_(base::StrCat({path_prefix, "."})) {
|
||||||
|
target_pref_store_->AddObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapWithPrefixPrefStore::~WrapWithPrefixPrefStore() {
|
||||||
|
target_pref_store_->RemoveObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WrapWithPrefixPrefStore::GetValue(base::StringPiece key,
|
||||||
|
const base::Value** value) const {
|
||||||
|
return target_pref_store_->GetValue(AddDottedPrefix(key), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
base::Value::Dict WrapWithPrefixPrefStore::GetValues() const {
|
||||||
|
base::Value::Dict values = target_pref_store_->GetValues();
|
||||||
|
std::string_view prefix(dotted_prefix_.c_str(), dotted_prefix_.size() - 1);
|
||||||
|
if (base::Value::Dict* values_with_prefix =
|
||||||
|
values.FindDictByDottedPath(prefix)) {
|
||||||
|
return std::move(*values_with_prefix);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WrapWithPrefixPrefStore::GetMutableValue(const std::string& key,
|
||||||
|
base::Value** value) {
|
||||||
|
return target_pref_store_->GetMutableValue(AddDottedPrefix(key), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::AddObserver(PrefStore::Observer* observer) {
|
||||||
|
observers_.AddObserver(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::RemoveObserver(PrefStore::Observer* observer) {
|
||||||
|
observers_.RemoveObserver(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WrapWithPrefixPrefStore::HasObservers() const {
|
||||||
|
return !observers_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WrapWithPrefixPrefStore::IsInitializationComplete() const {
|
||||||
|
return target_pref_store_->IsInitializationComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::SetValue(const std::string& key,
|
||||||
|
base::Value value,
|
||||||
|
uint32_t flags) {
|
||||||
|
target_pref_store_->SetValue(AddDottedPrefix(key), std::move(value), flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::SetValueSilently(const std::string& key,
|
||||||
|
base::Value value,
|
||||||
|
uint32_t flags) {
|
||||||
|
target_pref_store_->SetValueSilently(AddDottedPrefix(key), std::move(value),
|
||||||
|
flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::RemoveValue(const std::string& key,
|
||||||
|
uint32_t flags) {
|
||||||
|
target_pref_store_->RemoveValue(AddDottedPrefix(key), flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::RemoveValuesByPrefixSilently(
|
||||||
|
const std::string& prefix) {
|
||||||
|
target_pref_store_->RemoveValuesByPrefixSilently(AddDottedPrefix(prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WrapWithPrefixPrefStore::ReadOnly() const {
|
||||||
|
return target_pref_store_->ReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
PersistentPrefStore::PrefReadError WrapWithPrefixPrefStore::GetReadError()
|
||||||
|
const {
|
||||||
|
return target_pref_store_->GetReadError();
|
||||||
|
}
|
||||||
|
|
||||||
|
PersistentPrefStore::PrefReadError WrapWithPrefixPrefStore::ReadPrefs() {
|
||||||
|
// The target pref store should have been initialized prior to calling
|
||||||
|
// ReadPrefs() on this store.
|
||||||
|
CHECK(target_pref_store_->IsInitializationComplete() ||
|
||||||
|
// To catch case where target pref store initialization failed.
|
||||||
|
target_pref_store_->GetReadError() !=
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_NONE);
|
||||||
|
return target_pref_store_->GetReadError();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::ReadPrefsAsync(
|
||||||
|
ReadErrorDelegate* error_delegate) {
|
||||||
|
// The target pref store should either have been initialized or should have an
|
||||||
|
// ongoing read.
|
||||||
|
CHECK(IsInitializationComplete() ||
|
||||||
|
// To catch case where target pref store initialization failed.
|
||||||
|
GetReadError() != PersistentPrefStore::PREF_READ_ERROR_NONE ||
|
||||||
|
// ReadPrefsAsync() was called but it's still ongoing.
|
||||||
|
target_pref_store_->HasReadErrorDelegate());
|
||||||
|
read_error_delegate_.emplace(error_delegate);
|
||||||
|
if (PersistentPrefStore::PrefReadError read_error = GetReadError();
|
||||||
|
read_error != PersistentPrefStore::PREF_READ_ERROR_NONE &&
|
||||||
|
error_delegate) {
|
||||||
|
error_delegate->OnError(read_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::SchedulePendingLossyWrites() {
|
||||||
|
// This store is only a wrapper and relies on the target pref store being
|
||||||
|
// independently notified of this.
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::OnStoreDeletionFromDisk() {
|
||||||
|
// This store is only a wrapper and relies on the target pref store being
|
||||||
|
// independently notified of this.
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::ReportValueChanged(const std::string& key,
|
||||||
|
uint32_t flags) {
|
||||||
|
return target_pref_store_->ReportValueChanged(AddDottedPrefix(key), flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::OnPrefValueChanged(const std::string& key) {
|
||||||
|
if (!HasDottedPrefix(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string original_key(RemoveDottedPrefix(key));
|
||||||
|
for (Observer& observer : observers_) {
|
||||||
|
observer.OnPrefValueChanged(original_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapWithPrefixPrefStore::OnInitializationCompleted(bool succeeded) {
|
||||||
|
if (PersistentPrefStore::PrefReadError read_error = GetReadError();
|
||||||
|
read_error != PersistentPrefStore::PREF_READ_ERROR_NONE &&
|
||||||
|
read_error_delegate_.has_value() && read_error_delegate_.value()) {
|
||||||
|
read_error_delegate_.value()->OnError(read_error);
|
||||||
|
}
|
||||||
|
for (Observer& observer : observers_) {
|
||||||
|
observer.OnInitializationCompleted(succeeded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string WrapWithPrefixPrefStore::AddDottedPrefix(
|
||||||
|
std::string_view path) const {
|
||||||
|
CHECK(!HasDottedPrefix(path));
|
||||||
|
return base::StrCat({dotted_prefix_, path});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view WrapWithPrefixPrefStore::RemoveDottedPrefix(
|
||||||
|
std::string_view path) const {
|
||||||
|
CHECK(HasDottedPrefix(path));
|
||||||
|
path.remove_prefix(dotted_prefix_.size());
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WrapWithPrefixPrefStore::HasDottedPrefix(std::string_view path) const {
|
||||||
|
return base::StartsWith(path, dotted_prefix_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WrapWithPrefixPrefStore::HasReadErrorDelegate() const {
|
||||||
|
return read_error_delegate_.has_value();
|
||||||
|
}
|
93
components/prefs/wrap_with_prefix_pref_store.h
Normal file
93
components/prefs/wrap_with_prefix_pref_store.h
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2024 The Chromium Authors
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef COMPONENTS_PREFS_WRAP_WITH_PREFIX_PREF_STORE_H_
|
||||||
|
#define COMPONENTS_PREFS_WRAP_WITH_PREFIX_PREF_STORE_H_
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/observer_list.h"
|
||||||
|
#include "components/prefs/persistent_pref_store.h"
|
||||||
|
|
||||||
|
// This is a wrapper over another PersistentPrefStore.
|
||||||
|
// This can be used to implement a pref store over a dictionary in the
|
||||||
|
// PersistentPrefStore.
|
||||||
|
// For example, consider the following JSON being handled by a JsonPrefStore:
|
||||||
|
// {
|
||||||
|
// "foo": "Hello World",
|
||||||
|
// "bar": {
|
||||||
|
// "foobar": "Goodbye World"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// A WrapWithPrefixPrefStore can help operate on the dict for "bar", directly.
|
||||||
|
// That is, any query for "foobar" on this store will correspond to a query for
|
||||||
|
// "bar.foobar" in the JsonPrefStore.
|
||||||
|
//
|
||||||
|
// This is achieved by prefixing all the queries with the provided prefix.
|
||||||
|
//
|
||||||
|
// This can be used to merge separate pref stores into one single storage under
|
||||||
|
// separate dictionary items.
|
||||||
|
class COMPONENTS_PREFS_EXPORT WrapWithPrefixPrefStore
|
||||||
|
: public PersistentPrefStore,
|
||||||
|
public PrefStore::Observer {
|
||||||
|
public:
|
||||||
|
WrapWithPrefixPrefStore(scoped_refptr<PersistentPrefStore> target_pref_store,
|
||||||
|
std::string_view path_prefix);
|
||||||
|
|
||||||
|
WrapWithPrefixPrefStore(const WrapWithPrefixPrefStore&) = delete;
|
||||||
|
WrapWithPrefixPrefStore& operator=(const WrapWithPrefixPrefStore&) = delete;
|
||||||
|
|
||||||
|
// PrefStore implementation.
|
||||||
|
bool GetValue(base::StringPiece key,
|
||||||
|
const base::Value** result) const override;
|
||||||
|
base::Value::Dict GetValues() const override;
|
||||||
|
void AddObserver(PrefStore::Observer* observer) override;
|
||||||
|
void RemoveObserver(PrefStore::Observer* observer) override;
|
||||||
|
bool HasObservers() const override;
|
||||||
|
bool IsInitializationComplete() const override;
|
||||||
|
|
||||||
|
// PersistentPrefStore implementation.
|
||||||
|
bool GetMutableValue(const std::string& key, base::Value** result) override;
|
||||||
|
void ReportValueChanged(const std::string& key, uint32_t flags) override;
|
||||||
|
void SetValue(const std::string& key,
|
||||||
|
base::Value value,
|
||||||
|
uint32_t flags) override;
|
||||||
|
void SetValueSilently(const std::string& key,
|
||||||
|
base::Value value,
|
||||||
|
uint32_t flags) override;
|
||||||
|
void RemoveValue(const std::string& key, uint32_t flags) override;
|
||||||
|
bool ReadOnly() const override;
|
||||||
|
PrefReadError GetReadError() const override;
|
||||||
|
PersistentPrefStore::PrefReadError ReadPrefs() override;
|
||||||
|
void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
|
||||||
|
void SchedulePendingLossyWrites() override;
|
||||||
|
void OnStoreDeletionFromDisk() override;
|
||||||
|
void RemoveValuesByPrefixSilently(const std::string& prefix) override;
|
||||||
|
bool HasReadErrorDelegate() const override;
|
||||||
|
|
||||||
|
// PrefStore::Observer implementation.
|
||||||
|
void OnPrefValueChanged(const std::string& key) override;
|
||||||
|
void OnInitializationCompleted(bool succeeded) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~WrapWithPrefixPrefStore() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string AddDottedPrefix(std::string_view path) const;
|
||||||
|
std::string_view RemoveDottedPrefix(std::string_view path) const;
|
||||||
|
bool HasDottedPrefix(std::string_view path) const;
|
||||||
|
|
||||||
|
scoped_refptr<PersistentPrefStore> target_pref_store_;
|
||||||
|
const std::string dotted_prefix_;
|
||||||
|
|
||||||
|
base::ObserverList<PrefStore::Observer, true>::Unchecked observers_;
|
||||||
|
|
||||||
|
// Optional so we can differentiate `nullopt` from `nullptr`.
|
||||||
|
std::optional<std::unique_ptr<PersistentPrefStore::ReadErrorDelegate>>
|
||||||
|
read_error_delegate_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COMPONENTS_PREFS_WRAP_WITH_PREFIX_PREF_STORE_H_
|
415
components/prefs/wrap_with_prefix_pref_store_unittest.cc
Normal file
415
components/prefs/wrap_with_prefix_pref_store_unittest.cc
Normal file
@@ -0,0 +1,415 @@
|
|||||||
|
// Copyright 2024 The Chromium Authors
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "components/prefs/wrap_with_prefix_pref_store.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "base/test/gtest_util.h"
|
||||||
|
#include "base/test/task_environment.h"
|
||||||
|
#include "base/values.h"
|
||||||
|
#include "components/prefs/pref_store.h"
|
||||||
|
#include "components/prefs/testing_pref_store.h"
|
||||||
|
#include "testing/gmock/include/gmock/gmock.h"
|
||||||
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ::testing::Eq;
|
||||||
|
using ::testing::IsEmpty;
|
||||||
|
using ::testing::NiceMock;
|
||||||
|
using ::testing::Pointee;
|
||||||
|
|
||||||
|
const char kPrefix[] = "prefixed";
|
||||||
|
const char kTestPref[] = "test.pref";
|
||||||
|
const char kPrefixedTestPref[] = "prefixed.test.pref";
|
||||||
|
|
||||||
|
testing::AssertionResult ValueInStoreIs(const PrefStore& store,
|
||||||
|
const std::string& path,
|
||||||
|
const auto& expected) {
|
||||||
|
const base::Value expected_value(expected);
|
||||||
|
const base::Value* actual_value = nullptr;
|
||||||
|
if (!store.GetValue(path, &actual_value)) {
|
||||||
|
return testing::AssertionFailure() << "Pref " << path << " isn't present";
|
||||||
|
}
|
||||||
|
DCHECK(actual_value);
|
||||||
|
if (expected_value != *actual_value) {
|
||||||
|
return testing::AssertionFailure()
|
||||||
|
<< "Pref " << path << " has value " << *actual_value
|
||||||
|
<< " but was expected to be " << expected_value;
|
||||||
|
}
|
||||||
|
return testing::AssertionSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
testing::AssertionResult ValueInStoreIsAbsent(const PrefStore& store,
|
||||||
|
const std::string& path) {
|
||||||
|
const base::Value* actual_value = nullptr;
|
||||||
|
if (store.GetValue(path, &actual_value)) {
|
||||||
|
DCHECK(actual_value);
|
||||||
|
return testing::AssertionFailure()
|
||||||
|
<< "Pref " << path << " should be absent, but exists with value "
|
||||||
|
<< *actual_value;
|
||||||
|
}
|
||||||
|
return testing::AssertionSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockPrefStoreObserver : public PrefStore::Observer {
|
||||||
|
public:
|
||||||
|
~MockPrefStoreObserver() override = default;
|
||||||
|
|
||||||
|
MOCK_METHOD(void, OnPrefValueChanged, (const std::string& key), (override));
|
||||||
|
MOCK_METHOD(void, OnInitializationCompleted, (bool succeeded), (override));
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD(void, OnError, (PersistentPrefStore::PrefReadError), (override));
|
||||||
|
};
|
||||||
|
|
||||||
|
class WrapWithPrefixPrefStoreTest : public testing::Test {
|
||||||
|
public:
|
||||||
|
WrapWithPrefixPrefStoreTest()
|
||||||
|
: target_store_(base::MakeRefCounted<TestingPrefStore>()),
|
||||||
|
store_(base::MakeRefCounted<WrapWithPrefixPrefStore>(target_store_,
|
||||||
|
kPrefix)) {}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
store_->AddObserver(&observer_);
|
||||||
|
target_store_->AddObserver(&target_store_observer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
store_->RemoveObserver(&observer_);
|
||||||
|
target_store_->RemoveObserver(&target_store_observer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestingPrefStore& target_store() { return *target_store_; }
|
||||||
|
WrapWithPrefixPrefStore& store() { return *store_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
base::test::TaskEnvironment task_environment_;
|
||||||
|
scoped_refptr<TestingPrefStore> target_store_;
|
||||||
|
scoped_refptr<WrapWithPrefixPrefStore> store_;
|
||||||
|
NiceMock<MockPrefStoreObserver> observer_;
|
||||||
|
NiceMock<MockPrefStoreObserver> target_store_observer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, AddRemoveObserver) {
|
||||||
|
MockPrefStoreObserver observer;
|
||||||
|
store().AddObserver(&observer);
|
||||||
|
|
||||||
|
EXPECT_CALL(observer, OnPrefValueChanged).Times(0);
|
||||||
|
// No observer should be notified since the pref is not prefixed.
|
||||||
|
target_store().ReportValueChanged(kTestPref, /*flags=*/0);
|
||||||
|
|
||||||
|
EXPECT_CALL(observer, OnPrefValueChanged(kTestPref));
|
||||||
|
target_store().ReportValueChanged(kPrefixedTestPref, /*flags=*/0);
|
||||||
|
|
||||||
|
store().RemoveObserver(&observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, HasObservers) {
|
||||||
|
EXPECT_TRUE(store().HasObservers());
|
||||||
|
store().RemoveObserver(&observer_);
|
||||||
|
EXPECT_FALSE(store().HasObservers());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, IsInitializationComplete) {
|
||||||
|
ASSERT_FALSE(target_store().IsInitializationComplete());
|
||||||
|
EXPECT_FALSE(store().IsInitializationComplete());
|
||||||
|
|
||||||
|
EXPECT_CALL(target_store_observer_, OnInitializationCompleted);
|
||||||
|
EXPECT_CALL(observer_, OnInitializationCompleted);
|
||||||
|
target_store().NotifyInitializationCompleted();
|
||||||
|
|
||||||
|
ASSERT_TRUE(target_store().IsInitializationComplete());
|
||||||
|
EXPECT_TRUE(store().IsInitializationComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, GetValue) {
|
||||||
|
ASSERT_TRUE(ValueInStoreIsAbsent(target_store(), kTestPref));
|
||||||
|
ASSERT_TRUE(ValueInStoreIsAbsent(target_store(), kPrefixedTestPref));
|
||||||
|
ASSERT_TRUE(ValueInStoreIsAbsent(store(), kTestPref));
|
||||||
|
|
||||||
|
target_store().SetString(kTestPref, "value1");
|
||||||
|
ASSERT_TRUE(ValueInStoreIs(target_store(), kTestPref, "value1"));
|
||||||
|
// kTestPref is not prefixed and should not be returned by `store_`.
|
||||||
|
EXPECT_TRUE(ValueInStoreIsAbsent(store(), kTestPref));
|
||||||
|
|
||||||
|
target_store().SetString(kPrefixedTestPref, "value2");
|
||||||
|
EXPECT_TRUE(ValueInStoreIs(store(), kTestPref, "value2"));
|
||||||
|
EXPECT_TRUE(ValueInStoreIs(target_store(), kTestPref, "value1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, GetValues) {
|
||||||
|
ASSERT_THAT(target_store().GetValues(), IsEmpty());
|
||||||
|
ASSERT_THAT(store().GetValues(), IsEmpty());
|
||||||
|
|
||||||
|
target_store().SetString(kTestPref, "value1");
|
||||||
|
ASSERT_EQ(target_store().GetValues(),
|
||||||
|
base::Value::Dict().SetByDottedPath(kTestPref, "value1"));
|
||||||
|
EXPECT_THAT(store().GetValues(), IsEmpty());
|
||||||
|
|
||||||
|
target_store().SetString(kPrefixedTestPref, "value2");
|
||||||
|
// Expect the new pref store to return the "un-prefixed" value.
|
||||||
|
EXPECT_EQ(store().GetValues(),
|
||||||
|
base::Value::Dict().SetByDottedPath(kTestPref, "value2"));
|
||||||
|
EXPECT_TRUE(ValueInStoreIs(store(), kTestPref, "value2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, SetValue) {
|
||||||
|
EXPECT_CALL(observer_, OnPrefValueChanged(kTestPref));
|
||||||
|
EXPECT_CALL(target_store_observer_, OnPrefValueChanged(kPrefixedTestPref));
|
||||||
|
store().SetValue(kTestPref, base::Value("value"), /*flags=*/0);
|
||||||
|
|
||||||
|
EXPECT_TRUE(ValueInStoreIs(store(), kTestPref, "value"));
|
||||||
|
EXPECT_TRUE(ValueInStoreIsAbsent(target_store(), kTestPref));
|
||||||
|
// The new pref should be under the prefix dict.
|
||||||
|
EXPECT_TRUE(ValueInStoreIs(target_store(), kPrefixedTestPref, "value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, SetValueShouldNotNotifyIfUnchanged) {
|
||||||
|
target_store().SetValue(kPrefixedTestPref, base::Value("value"), /*flags=*/0);
|
||||||
|
ASSERT_TRUE(ValueInStoreIs(store(), kTestPref, "value"));
|
||||||
|
|
||||||
|
EXPECT_CALL(observer_, OnPrefValueChanged).Times(0);
|
||||||
|
store().SetValue(kTestPref, base::Value("value"), /*flags=*/0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, SetValueSilently) {
|
||||||
|
EXPECT_CALL(observer_, OnPrefValueChanged).Times(0);
|
||||||
|
EXPECT_CALL(target_store_observer_, OnPrefValueChanged).Times(0);
|
||||||
|
store().SetValueSilently(kTestPref, base::Value("value"), /*flags=*/0);
|
||||||
|
|
||||||
|
EXPECT_TRUE(ValueInStoreIs(store(), kTestPref, "value"));
|
||||||
|
EXPECT_TRUE(ValueInStoreIsAbsent(target_store(), kTestPref));
|
||||||
|
// The new pref should be under the prefix dict.
|
||||||
|
EXPECT_TRUE(ValueInStoreIs(target_store(), kPrefixedTestPref, "value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, GetMutableValue) {
|
||||||
|
target_store().SetString(kTestPref, "value1");
|
||||||
|
base::Value* value = nullptr;
|
||||||
|
// kTestPref is not prefixed and should not be returned by `store_`.
|
||||||
|
EXPECT_FALSE(store().GetMutableValue(kTestPref, &value));
|
||||||
|
EXPECT_FALSE(value);
|
||||||
|
|
||||||
|
target_store().SetString(kPrefixedTestPref, "value2");
|
||||||
|
EXPECT_TRUE(store().GetMutableValue(kTestPref, &value));
|
||||||
|
ASSERT_TRUE(value);
|
||||||
|
EXPECT_EQ(*value, base::Value("value2"));
|
||||||
|
|
||||||
|
*value = base::Value("value3");
|
||||||
|
EXPECT_TRUE(ValueInStoreIs(store(), kTestPref, "value3"));
|
||||||
|
EXPECT_TRUE(store().GetMutableValue(kTestPref, &value));
|
||||||
|
EXPECT_THAT(value, Pointee(Eq("value3")));
|
||||||
|
|
||||||
|
EXPECT_FALSE(ValueInStoreIsAbsent(target_store(), kTestPref));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, ReportValueChanged) {
|
||||||
|
EXPECT_CALL(observer_, OnPrefValueChanged(kTestPref));
|
||||||
|
EXPECT_CALL(target_store_observer_, OnPrefValueChanged(kPrefixedTestPref));
|
||||||
|
store().ReportValueChanged(kTestPref, /*flags=*/0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, RemoveValue) {
|
||||||
|
target_store().SetString(kTestPref, "value1");
|
||||||
|
target_store().SetString(kPrefixedTestPref, "value2");
|
||||||
|
|
||||||
|
EXPECT_CALL(observer_, OnPrefValueChanged(kTestPref));
|
||||||
|
EXPECT_CALL(target_store_observer_, OnPrefValueChanged(kPrefixedTestPref));
|
||||||
|
store().RemoveValue(kTestPref, /*flags=*/0);
|
||||||
|
|
||||||
|
EXPECT_TRUE(ValueInStoreIsAbsent(store(), kTestPref));
|
||||||
|
EXPECT_TRUE(ValueInStoreIsAbsent(target_store(), kPrefixedTestPref));
|
||||||
|
EXPECT_TRUE(ValueInStoreIs(target_store(), kTestPref, "value1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, RemoveValueShouldNotNotifyIfAbsent) {
|
||||||
|
EXPECT_TRUE(ValueInStoreIsAbsent(store(), kTestPref));
|
||||||
|
EXPECT_TRUE(ValueInStoreIsAbsent(target_store(), kPrefixedTestPref));
|
||||||
|
|
||||||
|
EXPECT_CALL(observer_, OnPrefValueChanged).Times(0);
|
||||||
|
store().RemoveValue(kTestPref, /*flags=*/0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, RemoveValuesByPrefixSilently) {
|
||||||
|
target_store().SetString("test.pref", "value");
|
||||||
|
target_store().SetString("prefixed.test.pref", "value1");
|
||||||
|
target_store().SetString("prefixed.test.pref2", "value2");
|
||||||
|
|
||||||
|
ASSERT_EQ(store().GetValues(), base::Value::Dict()
|
||||||
|
.SetByDottedPath("test.pref", "value1")
|
||||||
|
.SetByDottedPath("test.pref2", "value2"));
|
||||||
|
|
||||||
|
store().RemoveValuesByPrefixSilently("test");
|
||||||
|
|
||||||
|
EXPECT_THAT(store().GetValues(), IsEmpty());
|
||||||
|
EXPECT_EQ(target_store().GetValues(),
|
||||||
|
base::Value::Dict().SetByDottedPath("test.pref", "value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, ReadOnly) {
|
||||||
|
target_store().set_read_only(false);
|
||||||
|
ASSERT_FALSE(target_store().ReadOnly());
|
||||||
|
EXPECT_FALSE(store().ReadOnly());
|
||||||
|
|
||||||
|
target_store().set_read_only(true);
|
||||||
|
ASSERT_TRUE(target_store().ReadOnly());
|
||||||
|
EXPECT_TRUE(store().ReadOnly());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, GetReadError) {
|
||||||
|
ASSERT_EQ(target_store().GetReadError(),
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_NONE);
|
||||||
|
EXPECT_EQ(store().GetReadError(), PersistentPrefStore::PREF_READ_ERROR_NONE);
|
||||||
|
|
||||||
|
target_store().set_read_error(
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE);
|
||||||
|
ASSERT_EQ(target_store().GetReadError(),
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE);
|
||||||
|
EXPECT_EQ(store().GetReadError(),
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, ReadPrefsForwardsReadError) {
|
||||||
|
// Read error.
|
||||||
|
target_store().set_read_error(
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE);
|
||||||
|
|
||||||
|
ASSERT_EQ(target_store().ReadPrefs(),
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE);
|
||||||
|
// Should now forward the read error.
|
||||||
|
EXPECT_EQ(store().ReadPrefs(),
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, ReadPrefsForwardsReadSuccess) {
|
||||||
|
// Read success.
|
||||||
|
target_store().set_read_error(PersistentPrefStore::PREF_READ_ERROR_NONE);
|
||||||
|
ASSERT_EQ(target_store().ReadPrefs(),
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_NONE);
|
||||||
|
// Should now forward the read success.
|
||||||
|
EXPECT_EQ(store().ReadPrefs(), PersistentPrefStore::PREF_READ_ERROR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest,
|
||||||
|
ReadPrefsAsyncUponPrexistingReadPrefsSuccess) {
|
||||||
|
ASSERT_EQ(target_store().ReadPrefs(),
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_NONE);
|
||||||
|
ASSERT_TRUE(target_store().IsInitializationComplete());
|
||||||
|
|
||||||
|
// The callee is expected to take the ownership, hence the assignment to a raw
|
||||||
|
// ptr.
|
||||||
|
auto* read_error_delegate =
|
||||||
|
new ::testing::StrictMock<MockReadErrorDelegate>();
|
||||||
|
EXPECT_CALL(*read_error_delegate, OnError).Times(0);
|
||||||
|
store().ReadPrefsAsync(read_error_delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest,
|
||||||
|
ReadPrefsAsyncUponPrexistingReadPrefsAsyncSuccess) {
|
||||||
|
target_store().ReadPrefsAsync(nullptr);
|
||||||
|
ASSERT_TRUE(target_store().IsInitializationComplete());
|
||||||
|
|
||||||
|
// The callee is expected to take the ownership, hence the assignment to a raw
|
||||||
|
// ptr.
|
||||||
|
auto* read_error_delegate =
|
||||||
|
new ::testing::StrictMock<MockReadErrorDelegate>();
|
||||||
|
EXPECT_CALL(*read_error_delegate, OnError).Times(0);
|
||||||
|
store().ReadPrefsAsync(read_error_delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest,
|
||||||
|
ReadPrefsAsyncForwardsPreexistingReadError) {
|
||||||
|
target_store().SetBlockAsyncRead(true);
|
||||||
|
target_store().ReadPrefsAsync(nullptr);
|
||||||
|
target_store().set_read_error(
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED);
|
||||||
|
target_store().SetBlockAsyncRead(false);
|
||||||
|
|
||||||
|
// The callee is expected to take the ownership, hence the assignment to a raw
|
||||||
|
// ptr.
|
||||||
|
auto* read_error_delegate =
|
||||||
|
new ::testing::StrictMock<MockReadErrorDelegate>();
|
||||||
|
EXPECT_CALL(*read_error_delegate,
|
||||||
|
OnError(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED));
|
||||||
|
store().ReadPrefsAsync(read_error_delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest,
|
||||||
|
ReadPrefsAsyncUponUnderlyingReadPrefsAsyncSuccess) {
|
||||||
|
target_store().SetBlockAsyncRead(true);
|
||||||
|
target_store().ReadPrefsAsync(nullptr);
|
||||||
|
|
||||||
|
// The callee is expected to take the ownership, hence the assignment to a raw
|
||||||
|
// ptr.
|
||||||
|
auto* read_error_delegate =
|
||||||
|
new ::testing::StrictMock<MockReadErrorDelegate>();
|
||||||
|
EXPECT_CALL(*read_error_delegate, OnError).Times(0);
|
||||||
|
store().ReadPrefsAsync(read_error_delegate);
|
||||||
|
ASSERT_FALSE(store().IsInitializationComplete());
|
||||||
|
|
||||||
|
target_store().SetBlockAsyncRead(false);
|
||||||
|
ASSERT_TRUE(store().IsInitializationComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, ReadPrefsAsyncForwardsReadError) {
|
||||||
|
target_store().SetBlockAsyncRead(true);
|
||||||
|
target_store().ReadPrefsAsync(nullptr);
|
||||||
|
|
||||||
|
// The callee is expected to take the ownership, hence the assignment to a raw
|
||||||
|
// ptr.
|
||||||
|
auto* read_error_delegate =
|
||||||
|
new ::testing::StrictMock<MockReadErrorDelegate>();
|
||||||
|
EXPECT_CALL(*read_error_delegate, OnError).Times(0);
|
||||||
|
store().ReadPrefsAsync(read_error_delegate);
|
||||||
|
|
||||||
|
target_store().set_read_error(
|
||||||
|
PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED);
|
||||||
|
|
||||||
|
EXPECT_CALL(*read_error_delegate,
|
||||||
|
OnError(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED));
|
||||||
|
target_store().SetBlockAsyncRead(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, HasReadErrorDelegate) {
|
||||||
|
EXPECT_FALSE(store().HasReadErrorDelegate());
|
||||||
|
|
||||||
|
// Target store's ReadPrefsAsync() is a prerequisite.
|
||||||
|
target_store().ReadPrefsAsync(nullptr);
|
||||||
|
store().ReadPrefsAsync(new MockReadErrorDelegate);
|
||||||
|
EXPECT_TRUE(store().HasReadErrorDelegate());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreTest, HasReadErrorDelegateWithNullDelegate) {
|
||||||
|
EXPECT_FALSE(store().HasReadErrorDelegate());
|
||||||
|
|
||||||
|
// Target store's ReadPrefsAsync() is a prerequisite.
|
||||||
|
target_store().ReadPrefsAsync(nullptr);
|
||||||
|
store().ReadPrefsAsync(nullptr);
|
||||||
|
// Returns true even though no instance was passed.
|
||||||
|
EXPECT_TRUE(store().HasReadErrorDelegate());
|
||||||
|
}
|
||||||
|
|
||||||
|
using WrapWithPrefixPrefStoreDeathTest = WrapWithPrefixPrefStoreTest;
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreDeathTest,
|
||||||
|
ReadPrefsShouldCrashIfUnderlyingStoreUninitialized) {
|
||||||
|
ASSERT_FALSE(target_store().IsInitializationComplete());
|
||||||
|
// Disallowed to call ReadPrefs() without the underlying store having been
|
||||||
|
// initialized.
|
||||||
|
EXPECT_CHECK_DEATH(store().ReadPrefs());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WrapWithPrefixPrefStoreDeathTest,
|
||||||
|
ReadPrefsAsyncShouldCrashIfUnderlyingStoreUninitialized) {
|
||||||
|
ASSERT_FALSE(target_store().IsInitializationComplete());
|
||||||
|
// Disallowed to call ReadPrefs() without the underlying store having been
|
||||||
|
// initialized.
|
||||||
|
EXPECT_CHECK_DEATH(store().ReadPrefsAsync(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
Reference in New Issue
Block a user