0

[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:
Ankush Singh
2024-05-15 13:27:45 +00:00
committed by Chromium LUCI CQ
parent 8188600e6d
commit 35121a04e8
4 changed files with 686 additions and 0 deletions

@ -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 = [

@ -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();
}

@ -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_

@ -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