blob: 7a041ae1c310d498a12a0d89d591e700353d6787 [file] [log] [blame]
// 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, (std::string_view 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());
}
TEST_F(WrapWithPrefixPrefStoreTest, GetValueForPrefixedKeyIfNonExisting) {
target_store().SetString(kPrefixedTestPref, "value");
EXPECT_FALSE(store().GetValue(kPrefixedTestPref, nullptr));
}
TEST_F(WrapWithPrefixPrefStoreTest, GetValueForExistingIfExisting) {
target_store().SetString("prefixed.prefixed.test.pref", "value");
EXPECT_TRUE(ValueInStoreIs(store(), kPrefixedTestPref, "value"));
}
TEST_F(WrapWithPrefixPrefStoreTest, SetValueForPrefixedKey) {
EXPECT_CALL(observer_, OnPrefValueChanged(kPrefixedTestPref));
store().SetValue(kPrefixedTestPref, base::Value("value"), /*flags=*/0);
EXPECT_TRUE(ValueInStoreIs(store(), kPrefixedTestPref, "value"));
}
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