[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",
|
||||
"value_map_pref_store.cc",
|
||||
"value_map_pref_store.h",
|
||||
"wrap_with_prefix_pref_store.cc",
|
||||
"wrap_with_prefix_pref_store.h",
|
||||
"writeable_pref_store.cc",
|
||||
"writeable_pref_store.h",
|
||||
]
|
||||
@ -109,6 +111,7 @@ source_set("unit_tests") {
|
||||
"pref_value_store_unittest.cc",
|
||||
"scoped_user_pref_update_unittest.cc",
|
||||
"segregated_pref_store_unittest.cc",
|
||||
"wrap_with_prefix_pref_store_unittest.cc",
|
||||
]
|
||||
|
||||
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