
This method can be used as proxy to detect if ReadPrefsAsync() has been called on a pref store. Bug: 336776819 Change-Id: I21ba0a5f4d5a00d1710303359989277e4a940410 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5535432 Reviewed-by: Gabriel Charette <gab@chromium.org> Commit-Queue: Ankush Singh <ankushkush@google.com> Cr-Commit-Position: refs/heads/main@{#1301154}
1027 lines
39 KiB
C++
1027 lines
39 KiB
C++
// Copyright 2012 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/json_pref_store.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "base/compiler_specific.h"
|
|
#include "base/files/file_util.h"
|
|
#include "base/files/scoped_temp_dir.h"
|
|
#include "base/functional/bind.h"
|
|
#include "base/functional/callback.h"
|
|
#include "base/location.h"
|
|
#include "base/memory/ref_counted.h"
|
|
#include "base/metrics/histogram_samples.h"
|
|
#include "base/path_service.h"
|
|
#include "base/run_loop.h"
|
|
#include "base/strings/string_number_conversions.h"
|
|
#include "base/strings/string_util.h"
|
|
#include "base/strings/utf_string_conversions.h"
|
|
#include "base/synchronization/waitable_event.h"
|
|
#include "base/task/sequenced_task_runner.h"
|
|
#include "base/task/single_thread_task_runner.h"
|
|
#include "base/test/metrics/histogram_tester.h"
|
|
#include "base/test/scoped_feature_list.h"
|
|
#include "base/test/task_environment.h"
|
|
#include "base/threading/thread.h"
|
|
#include "base/values.h"
|
|
#include "components/prefs/persistent_pref_store_unittest.h"
|
|
#include "components/prefs/pref_filter.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace base {
|
|
namespace {
|
|
|
|
const char kHomePage[] = "homepage";
|
|
|
|
const char kReadJson[] =
|
|
"{\n"
|
|
" \"homepage\": \"http://www.cnn.com\",\n"
|
|
" \"some_directory\": \"/usr/local/\",\n"
|
|
" \"tabs\": {\n"
|
|
" \"new_windows_in_tabs\": true,\n"
|
|
" \"max_tabs\": 20\n"
|
|
" }\n"
|
|
"}";
|
|
|
|
const char kInvalidJson[] = "!@#$%^&";
|
|
|
|
// Expected output for tests using RunBasicJsonPrefStoreTest().
|
|
const char kWriteGolden[] =
|
|
"{\"homepage\":\"http://www.cnn.com\","
|
|
"\"long_int\":{\"pref\":\"214748364842\"},"
|
|
"\"some_directory\":\"/usr/sbin/\","
|
|
"\"tabs\":{\"max_tabs\":10,\"new_windows_in_tabs\":false}}";
|
|
|
|
// A PrefFilter that will intercept all calls to FilterOnLoad() and hold on
|
|
// to the |prefs| until explicitly asked to release them.
|
|
class InterceptingPrefFilter : public PrefFilter {
|
|
public:
|
|
InterceptingPrefFilter();
|
|
InterceptingPrefFilter(OnWriteCallbackPair callback_pair);
|
|
|
|
InterceptingPrefFilter(const InterceptingPrefFilter&) = delete;
|
|
InterceptingPrefFilter& operator=(const InterceptingPrefFilter&) = delete;
|
|
|
|
~InterceptingPrefFilter() override;
|
|
|
|
// PrefFilter implementation:
|
|
void FilterOnLoad(PostFilterOnLoadCallback post_filter_on_load_callback,
|
|
base::Value::Dict pref_store_contents) override;
|
|
void FilterUpdate(const std::string& path) override {}
|
|
OnWriteCallbackPair FilterSerializeData(
|
|
base::Value::Dict& pref_store_contents) override {
|
|
return std::move(on_write_callback_pair_);
|
|
}
|
|
void OnStoreDeletionFromDisk() override {}
|
|
|
|
bool has_intercepted_prefs() const { return intercepted_prefs_ != nullptr; }
|
|
|
|
// Finalize an intercepted read, handing |intercepted_prefs_| back to its
|
|
// JsonPrefStore.
|
|
void ReleasePrefs();
|
|
|
|
private:
|
|
PostFilterOnLoadCallback post_filter_on_load_callback_;
|
|
std::unique_ptr<base::Value::Dict> intercepted_prefs_;
|
|
OnWriteCallbackPair on_write_callback_pair_;
|
|
};
|
|
|
|
InterceptingPrefFilter::InterceptingPrefFilter() {}
|
|
|
|
InterceptingPrefFilter::InterceptingPrefFilter(
|
|
OnWriteCallbackPair callback_pair) {
|
|
on_write_callback_pair_ = std::move(callback_pair);
|
|
}
|
|
|
|
InterceptingPrefFilter::~InterceptingPrefFilter() {}
|
|
|
|
void InterceptingPrefFilter::FilterOnLoad(
|
|
PostFilterOnLoadCallback post_filter_on_load_callback,
|
|
base::Value::Dict pref_store_contents) {
|
|
post_filter_on_load_callback_ = std::move(post_filter_on_load_callback);
|
|
intercepted_prefs_ =
|
|
std::make_unique<base::Value::Dict>(std::move(pref_store_contents));
|
|
}
|
|
|
|
void InterceptingPrefFilter::ReleasePrefs() {
|
|
EXPECT_FALSE(post_filter_on_load_callback_.is_null());
|
|
std::unique_ptr<base::Value::Dict> prefs = std::move(intercepted_prefs_);
|
|
std::move(post_filter_on_load_callback_).Run(std::move(*prefs), false);
|
|
}
|
|
|
|
class MockPrefStoreObserver : public PrefStore::Observer {
|
|
public:
|
|
MOCK_METHOD1(OnPrefValueChanged, void (const std::string&));
|
|
MOCK_METHOD1(OnInitializationCompleted, void (bool));
|
|
};
|
|
|
|
class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
|
|
public:
|
|
MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError));
|
|
};
|
|
|
|
enum class CommitPendingWriteMode {
|
|
// Basic mode.
|
|
WITHOUT_CALLBACK,
|
|
// With reply callback.
|
|
WITH_CALLBACK,
|
|
// With synchronous notify callback (synchronous after the write -- shouldn't
|
|
// require pumping messages to observe).
|
|
WITH_SYNCHRONOUS_CALLBACK,
|
|
};
|
|
|
|
base::test::TaskEnvironment::ThreadPoolExecutionMode GetExecutionMode(
|
|
CommitPendingWriteMode commit_mode) {
|
|
switch (commit_mode) {
|
|
case CommitPendingWriteMode::WITHOUT_CALLBACK:
|
|
[[fallthrough]];
|
|
case CommitPendingWriteMode::WITH_CALLBACK:
|
|
return base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED;
|
|
case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK:
|
|
// Synchronous callbacks require async tasks to run on their own.
|
|
return base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC;
|
|
}
|
|
}
|
|
|
|
void CommitPendingWrite(JsonPrefStore* pref_store,
|
|
CommitPendingWriteMode commit_pending_write_mode,
|
|
base::test::TaskEnvironment* task_environment) {
|
|
switch (commit_pending_write_mode) {
|
|
case CommitPendingWriteMode::WITHOUT_CALLBACK: {
|
|
pref_store->CommitPendingWrite();
|
|
task_environment->RunUntilIdle();
|
|
break;
|
|
}
|
|
case CommitPendingWriteMode::WITH_CALLBACK: {
|
|
TestCommitPendingWriteWithCallback(pref_store, task_environment);
|
|
break;
|
|
}
|
|
case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK: {
|
|
base::WaitableEvent written;
|
|
pref_store->CommitPendingWrite(
|
|
base::OnceClosure(),
|
|
base::BindOnce(&base::WaitableEvent::Signal, Unretained(&written)));
|
|
written.Wait();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
class JsonPrefStoreTest
|
|
: public testing::TestWithParam<CommitPendingWriteMode> {
|
|
public:
|
|
JsonPrefStoreTest()
|
|
: task_environment_(base::test::TaskEnvironment::MainThreadType::DEFAULT,
|
|
GetExecutionMode(GetParam())) {}
|
|
|
|
JsonPrefStoreTest(const JsonPrefStoreTest&) = delete;
|
|
JsonPrefStoreTest& operator=(const JsonPrefStoreTest&) = delete;
|
|
|
|
protected:
|
|
void SetUp() override {
|
|
commit_pending_write_mode_ = GetParam();
|
|
|
|
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
|
|
}
|
|
|
|
// The path to temporary directory used to contain the test operations.
|
|
base::ScopedTempDir temp_dir_;
|
|
|
|
base::test::TaskEnvironment task_environment_;
|
|
CommitPendingWriteMode commit_pending_write_mode_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// Test fallback behavior for a nonexistent file.
|
|
TEST_P(JsonPrefStoreTest, NonExistentFile) {
|
|
base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
|
|
ASSERT_FALSE(PathExists(bogus_input_file));
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
|
|
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
|
|
pref_store->ReadPrefs());
|
|
EXPECT_FALSE(pref_store->ReadOnly());
|
|
EXPECT_EQ(0u, pref_store->get_writer().previous_data_size());
|
|
}
|
|
|
|
// Test fallback behavior for an invalid file.
|
|
TEST_P(JsonPrefStoreTest, InvalidFile) {
|
|
base::FilePath invalid_file = temp_dir_.GetPath().AppendASCII("invalid.json");
|
|
ASSERT_TRUE(base::WriteFile(invalid_file, kInvalidJson));
|
|
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(invalid_file);
|
|
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
|
|
pref_store->ReadPrefs());
|
|
EXPECT_FALSE(pref_store->ReadOnly());
|
|
|
|
// The file should have been moved aside.
|
|
EXPECT_FALSE(PathExists(invalid_file));
|
|
base::FilePath moved_aside = temp_dir_.GetPath().AppendASCII("invalid.bad");
|
|
EXPECT_TRUE(PathExists(moved_aside));
|
|
|
|
std::string moved_aside_contents;
|
|
ASSERT_TRUE(base::ReadFileToString(moved_aside, &moved_aside_contents));
|
|
EXPECT_EQ(kInvalidJson, moved_aside_contents);
|
|
}
|
|
|
|
// This function is used to avoid code duplication while testing synchronous
|
|
// and asynchronous version of the JsonPrefStore loading. It validates that the
|
|
// given output file's contents matches kWriteGolden.
|
|
void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
|
|
const base::FilePath& output_file,
|
|
CommitPendingWriteMode commit_pending_write_mode,
|
|
base::test::TaskEnvironment* task_environment) {
|
|
const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
|
|
const char kMaxTabs[] = "tabs.max_tabs";
|
|
const char kLongIntPref[] = "long_int.pref";
|
|
|
|
std::string cnn("http://www.cnn.com");
|
|
|
|
const Value* actual;
|
|
EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual));
|
|
EXPECT_TRUE(actual->is_string());
|
|
EXPECT_EQ(cnn, actual->GetString());
|
|
|
|
const char kSomeDirectory[] = "some_directory";
|
|
|
|
EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
|
|
EXPECT_TRUE(actual->is_string());
|
|
EXPECT_EQ("/usr/local/", actual->GetString());
|
|
base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/"));
|
|
|
|
pref_store->SetValue(kSomeDirectory, Value(some_path.AsUTF8Unsafe()),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
|
|
EXPECT_TRUE(actual->is_string());
|
|
EXPECT_EQ(some_path.AsUTF8Unsafe(), actual->GetString());
|
|
|
|
// Test reading some other data types from sub-dictionaries.
|
|
EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
|
|
EXPECT_TRUE(actual->is_bool());
|
|
EXPECT_TRUE(actual->GetBool());
|
|
|
|
pref_store->SetValue(kNewWindowsInTabs, Value(false),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
|
|
EXPECT_TRUE(actual->is_bool());
|
|
EXPECT_FALSE(actual->GetBool());
|
|
|
|
EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
|
|
ASSERT_TRUE(actual->is_int());
|
|
EXPECT_EQ(20, actual->GetInt());
|
|
pref_store->SetValue(kMaxTabs, Value(10),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
|
|
ASSERT_TRUE(actual->is_int());
|
|
EXPECT_EQ(10, actual->GetInt());
|
|
|
|
pref_store->SetValue(kLongIntPref,
|
|
Value(base::NumberToString(214748364842LL)),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual));
|
|
EXPECT_TRUE(actual->is_string());
|
|
int64_t value;
|
|
base::StringToInt64(actual->GetString(), &value);
|
|
EXPECT_EQ(214748364842LL, value);
|
|
|
|
// Serialize and compare to expected output.
|
|
CommitPendingWrite(pref_store, commit_pending_write_mode, task_environment);
|
|
|
|
std::string output_contents;
|
|
ASSERT_TRUE(base::ReadFileToString(output_file, &output_contents));
|
|
EXPECT_EQ(kWriteGolden, output_contents);
|
|
ASSERT_TRUE(base::DeleteFile(output_file));
|
|
}
|
|
|
|
TEST_P(JsonPrefStoreTest, Basic) {
|
|
base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
|
|
ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
|
|
|
|
// Test that the persistent value can be loaded.
|
|
ASSERT_TRUE(PathExists(input_file));
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file);
|
|
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
|
|
EXPECT_FALSE(pref_store->ReadOnly());
|
|
EXPECT_TRUE(pref_store->IsInitializationComplete());
|
|
EXPECT_GT(pref_store->get_writer().previous_data_size(), 0u);
|
|
|
|
// The JSON file looks like this:
|
|
// {
|
|
// "homepage": "http://www.cnn.com",
|
|
// "some_directory": "/usr/local/",
|
|
// "tabs": {
|
|
// "new_windows_in_tabs": true,
|
|
// "max_tabs": 20
|
|
// }
|
|
// }
|
|
|
|
RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
|
|
commit_pending_write_mode_, &task_environment_);
|
|
}
|
|
|
|
TEST_P(JsonPrefStoreTest, BasicAsync) {
|
|
base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
|
|
ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
|
|
|
|
// Test that the persistent value can be loaded.
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file);
|
|
|
|
{
|
|
MockPrefStoreObserver mock_observer;
|
|
pref_store->AddObserver(&mock_observer);
|
|
|
|
MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
|
|
pref_store->ReadPrefsAsync(mock_error_delegate);
|
|
|
|
EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
|
|
EXPECT_CALL(*mock_error_delegate,
|
|
OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
|
|
task_environment_.RunUntilIdle();
|
|
pref_store->RemoveObserver(&mock_observer);
|
|
|
|
EXPECT_FALSE(pref_store->ReadOnly());
|
|
EXPECT_TRUE(pref_store->IsInitializationComplete());
|
|
EXPECT_GT(pref_store->get_writer().previous_data_size(), 0u);
|
|
}
|
|
|
|
// The JSON file looks like this:
|
|
// {
|
|
// "homepage": "http://www.cnn.com",
|
|
// "some_directory": "/usr/local/",
|
|
// "tabs": {
|
|
// "new_windows_in_tabs": true,
|
|
// "max_tabs": 20
|
|
// }
|
|
// }
|
|
|
|
RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
|
|
commit_pending_write_mode_, &task_environment_);
|
|
}
|
|
|
|
TEST_P(JsonPrefStoreTest, PreserveEmptyValues) {
|
|
FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json");
|
|
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
|
|
|
|
// Set some keys with empty values.
|
|
pref_store->SetValue("list", base::Value(base::Value::List()),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
pref_store->SetValue("dict", base::Value(base::Value::Dict()),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
|
|
// Write to file.
|
|
CommitPendingWrite(pref_store.get(), commit_pending_write_mode_,
|
|
&task_environment_);
|
|
|
|
// Reload.
|
|
pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
|
|
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
|
|
ASSERT_FALSE(pref_store->ReadOnly());
|
|
|
|
// Check values.
|
|
const Value* result = nullptr;
|
|
EXPECT_TRUE(pref_store->GetValue("list", &result));
|
|
EXPECT_EQ(Value::List(), *result);
|
|
EXPECT_TRUE(pref_store->GetValue("dict", &result));
|
|
EXPECT_EQ(Value::Dict(), *result);
|
|
}
|
|
|
|
// This test is just documenting some potentially non-obvious behavior. It
|
|
// shouldn't be taken as normative.
|
|
TEST_P(JsonPrefStoreTest, RemoveClearsEmptyParent) {
|
|
FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json");
|
|
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
|
|
|
|
base::Value::Dict dict;
|
|
dict.Set("key", "value");
|
|
pref_store->SetValue("dict", base::Value(std::move(dict)),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
|
|
pref_store->RemoveValue("dict.key",
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
|
|
const base::Value* retrieved_dict = nullptr;
|
|
bool has_dict = pref_store->GetValue("dict", &retrieved_dict);
|
|
EXPECT_FALSE(has_dict);
|
|
}
|
|
|
|
// Tests asynchronous reading of the file when there is no file.
|
|
TEST_P(JsonPrefStoreTest, AsyncNonExistingFile) {
|
|
base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
|
|
ASSERT_FALSE(PathExists(bogus_input_file));
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
|
|
MockPrefStoreObserver mock_observer;
|
|
pref_store->AddObserver(&mock_observer);
|
|
|
|
MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate;
|
|
pref_store->ReadPrefsAsync(mock_error_delegate);
|
|
|
|
EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
|
|
EXPECT_CALL(*mock_error_delegate,
|
|
OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1);
|
|
task_environment_.RunUntilIdle();
|
|
pref_store->RemoveObserver(&mock_observer);
|
|
|
|
EXPECT_FALSE(pref_store->ReadOnly());
|
|
}
|
|
|
|
TEST_P(JsonPrefStoreTest, ReadWithInterceptor) {
|
|
base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
|
|
ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
|
|
|
|
std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
|
|
new InterceptingPrefFilter());
|
|
InterceptingPrefFilter* raw_intercepting_pref_filter_ =
|
|
intercepting_pref_filter.get();
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(
|
|
input_file, std::move(intercepting_pref_filter));
|
|
|
|
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE,
|
|
pref_store->ReadPrefs());
|
|
EXPECT_FALSE(pref_store->ReadOnly());
|
|
|
|
// The store shouldn't be considered initialized until the interceptor
|
|
// returns.
|
|
EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
|
|
EXPECT_FALSE(pref_store->IsInitializationComplete());
|
|
EXPECT_FALSE(pref_store->GetValue(kHomePage, nullptr));
|
|
|
|
raw_intercepting_pref_filter_->ReleasePrefs();
|
|
|
|
EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
|
|
EXPECT_TRUE(pref_store->IsInitializationComplete());
|
|
EXPECT_TRUE(pref_store->GetValue(kHomePage, nullptr));
|
|
|
|
// The JSON file looks like this:
|
|
// {
|
|
// "homepage": "http://www.cnn.com",
|
|
// "some_directory": "/usr/local/",
|
|
// "tabs": {
|
|
// "new_windows_in_tabs": true,
|
|
// "max_tabs": 20
|
|
// }
|
|
// }
|
|
|
|
RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
|
|
commit_pending_write_mode_, &task_environment_);
|
|
}
|
|
|
|
TEST_P(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
|
|
base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
|
|
ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
|
|
|
|
std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
|
|
new InterceptingPrefFilter());
|
|
InterceptingPrefFilter* raw_intercepting_pref_filter_ =
|
|
intercepting_pref_filter.get();
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(
|
|
input_file, std::move(intercepting_pref_filter));
|
|
|
|
MockPrefStoreObserver mock_observer;
|
|
pref_store->AddObserver(&mock_observer);
|
|
|
|
// Ownership of the |mock_error_delegate| is handed to the |pref_store| below.
|
|
MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
|
|
|
|
{
|
|
pref_store->ReadPrefsAsync(mock_error_delegate);
|
|
|
|
EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0);
|
|
// EXPECT_CALL(*mock_error_delegate,
|
|
// OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
|
|
task_environment_.RunUntilIdle();
|
|
|
|
EXPECT_FALSE(pref_store->ReadOnly());
|
|
EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
|
|
EXPECT_FALSE(pref_store->IsInitializationComplete());
|
|
EXPECT_FALSE(pref_store->GetValue(kHomePage, nullptr));
|
|
}
|
|
|
|
{
|
|
EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
|
|
// EXPECT_CALL(*mock_error_delegate,
|
|
// OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
|
|
|
|
raw_intercepting_pref_filter_->ReleasePrefs();
|
|
|
|
EXPECT_FALSE(pref_store->ReadOnly());
|
|
EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
|
|
EXPECT_TRUE(pref_store->IsInitializationComplete());
|
|
EXPECT_TRUE(pref_store->GetValue(kHomePage, nullptr));
|
|
}
|
|
|
|
pref_store->RemoveObserver(&mock_observer);
|
|
|
|
// The JSON file looks like this:
|
|
// {
|
|
// "homepage": "http://www.cnn.com",
|
|
// "some_directory": "/usr/local/",
|
|
// "tabs": {
|
|
// "new_windows_in_tabs": true,
|
|
// "max_tabs": 20
|
|
// }
|
|
// }
|
|
|
|
RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
|
|
commit_pending_write_mode_, &task_environment_);
|
|
}
|
|
|
|
TEST_P(JsonPrefStoreTest, RemoveValuesByPrefix) {
|
|
FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty.json");
|
|
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
|
|
|
|
const Value* value;
|
|
const std::string prefix = "pref";
|
|
const std::string subpref_name1 = "pref.a";
|
|
const std::string subpref_name2 = "pref.b";
|
|
const std::string other_name = "other";
|
|
|
|
pref_store->SetValue(subpref_name1, base::Value(42),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
pref_store->SetValue(subpref_name2, base::Value(42),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
pref_store->SetValue(other_name, base::Value(42),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
|
|
pref_store->RemoveValuesByPrefixSilently(prefix);
|
|
EXPECT_FALSE(pref_store->GetValue(subpref_name1, &value));
|
|
EXPECT_FALSE(pref_store->GetValue(subpref_name2, &value));
|
|
EXPECT_TRUE(pref_store->GetValue(other_name, &value));
|
|
}
|
|
|
|
TEST_P(JsonPrefStoreTest, HasReadErrorDelegate) {
|
|
base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
|
|
ASSERT_FALSE(PathExists(bogus_input_file));
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
|
|
|
|
EXPECT_FALSE(pref_store->HasReadErrorDelegate());
|
|
|
|
pref_store->ReadPrefsAsync(new MockReadErrorDelegate);
|
|
EXPECT_TRUE(pref_store->HasReadErrorDelegate());
|
|
}
|
|
|
|
TEST_P(JsonPrefStoreTest, HasReadErrorDelegateWithNullDelegate) {
|
|
base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
|
|
ASSERT_FALSE(PathExists(bogus_input_file));
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
|
|
|
|
EXPECT_FALSE(pref_store->HasReadErrorDelegate());
|
|
|
|
pref_store->ReadPrefsAsync(nullptr);
|
|
// Returns true even though no instance was passed.
|
|
EXPECT_TRUE(pref_store->HasReadErrorDelegate());
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
JsonPrefStoreTestVariations,
|
|
JsonPrefStoreTest,
|
|
::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK,
|
|
CommitPendingWriteMode::WITH_CALLBACK,
|
|
CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
|
|
|
|
class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
|
|
public:
|
|
JsonPrefStoreLossyWriteTest() = default;
|
|
|
|
JsonPrefStoreLossyWriteTest(const JsonPrefStoreLossyWriteTest&) = delete;
|
|
JsonPrefStoreLossyWriteTest& operator=(const JsonPrefStoreLossyWriteTest&) =
|
|
delete;
|
|
|
|
protected:
|
|
void SetUp() override {
|
|
JsonPrefStoreTest::SetUp();
|
|
test_file_ = temp_dir_.GetPath().AppendASCII("test.json");
|
|
}
|
|
|
|
scoped_refptr<JsonPrefStore> CreatePrefStore() {
|
|
return base::MakeRefCounted<JsonPrefStore>(test_file_);
|
|
}
|
|
|
|
// Return the ImportantFileWriter for a given JsonPrefStore.
|
|
ImportantFileWriter* GetImportantFileWriter(JsonPrefStore* pref_store) {
|
|
return &(pref_store->writer_);
|
|
}
|
|
|
|
// Get the contents of kTestFile. Pumps the message loop before returning the
|
|
// result.
|
|
std::string GetTestFileContents() {
|
|
task_environment_.RunUntilIdle();
|
|
std::string file_contents;
|
|
ReadFileToString(test_file_, &file_contents);
|
|
return file_contents;
|
|
}
|
|
|
|
private:
|
|
base::FilePath test_file_;
|
|
};
|
|
|
|
TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteBasic) {
|
|
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
|
|
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
|
|
|
|
// Set a normal pref and check that it gets scheduled to be written.
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
pref_store->SetValue("normal", base::Value("normal"),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
ASSERT_TRUE(file_writer->HasPendingWrite());
|
|
file_writer->DoScheduledWrite();
|
|
ASSERT_EQ("{\"normal\":\"normal\"}", GetTestFileContents());
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
|
|
// Set a lossy pref and check that it is not scheduled to be written.
|
|
// SetValue/RemoveValue.
|
|
pref_store->SetValue("lossy", base::Value("lossy"),
|
|
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
pref_store->RemoveValue("lossy", WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
|
|
// SetValueSilently/RemoveValueSilently.
|
|
pref_store->SetValueSilently("lossy", base::Value("lossy"),
|
|
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
pref_store->RemoveValueSilently("lossy",
|
|
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
|
|
// ReportValueChanged.
|
|
pref_store->SetValue("lossy", base::Value("lossy"),
|
|
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
pref_store->ReportValueChanged("lossy",
|
|
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
|
|
// Call CommitPendingWrite and check that the lossy pref and the normal pref
|
|
// are there with the last values set above.
|
|
pref_store->CommitPendingWrite(base::OnceClosure());
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
|
|
GetTestFileContents());
|
|
}
|
|
|
|
TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) {
|
|
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
|
|
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
|
|
|
|
// Set a lossy pref and check that it is not scheduled to be written.
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
pref_store->SetValue("lossy", base::Value("lossy"),
|
|
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
|
|
// Set a normal pref and check that it is scheduled to be written.
|
|
pref_store->SetValue("normal", base::Value("normal"),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
ASSERT_TRUE(file_writer->HasPendingWrite());
|
|
|
|
// Call DoScheduledWrite and check both prefs get written.
|
|
file_writer->DoScheduledWrite();
|
|
ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
|
|
GetTestFileContents());
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
}
|
|
|
|
TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) {
|
|
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
|
|
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
|
|
|
|
// Set a normal pref and check that it is scheduled to be written.
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
pref_store->SetValue("normal", base::Value("normal"),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
ASSERT_TRUE(file_writer->HasPendingWrite());
|
|
|
|
// Set a lossy pref and check that the write is still scheduled.
|
|
pref_store->SetValue("lossy", base::Value("lossy"),
|
|
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
|
|
ASSERT_TRUE(file_writer->HasPendingWrite());
|
|
|
|
// Call DoScheduledWrite and check both prefs get written.
|
|
file_writer->DoScheduledWrite();
|
|
ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
|
|
GetTestFileContents());
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
}
|
|
|
|
TEST_P(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) {
|
|
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
|
|
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
|
|
|
|
// Set a lossy pref and check that it is not scheduled to be written.
|
|
pref_store->SetValue("lossy", base::Value("lossy"),
|
|
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
|
|
// Schedule pending lossy writes and check that it is scheduled.
|
|
pref_store->SchedulePendingLossyWrites();
|
|
ASSERT_TRUE(file_writer->HasPendingWrite());
|
|
|
|
// Call CommitPendingWrite and check that the lossy pref is there with the
|
|
// last value set above.
|
|
pref_store->CommitPendingWrite(base::OnceClosure());
|
|
ASSERT_FALSE(file_writer->HasPendingWrite());
|
|
ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents());
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
JsonPrefStoreLossyWriteTestVariations,
|
|
JsonPrefStoreLossyWriteTest,
|
|
::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK,
|
|
CommitPendingWriteMode::WITH_CALLBACK,
|
|
CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
|
|
|
|
class SuccessfulWriteReplyObserver {
|
|
public:
|
|
SuccessfulWriteReplyObserver() = default;
|
|
|
|
SuccessfulWriteReplyObserver(const SuccessfulWriteReplyObserver&) = delete;
|
|
SuccessfulWriteReplyObserver& operator=(const SuccessfulWriteReplyObserver&) =
|
|
delete;
|
|
|
|
// Returns true if a successful write was observed via on_successful_write()
|
|
// and resets the observation state to false regardless.
|
|
bool GetAndResetObservationState() {
|
|
bool was_successful_write_observed = successful_write_reply_observed_;
|
|
successful_write_reply_observed_ = false;
|
|
return was_successful_write_observed;
|
|
}
|
|
|
|
// Register OnWrite() to be called on the next write of |json_pref_store|.
|
|
void ObserveNextWriteCallback(JsonPrefStore* json_pref_store);
|
|
|
|
void OnSuccessfulWrite() {
|
|
EXPECT_FALSE(successful_write_reply_observed_);
|
|
successful_write_reply_observed_ = true;
|
|
}
|
|
|
|
private:
|
|
bool successful_write_reply_observed_ = false;
|
|
};
|
|
|
|
void SuccessfulWriteReplyObserver::ObserveNextWriteCallback(
|
|
JsonPrefStore* json_pref_store) {
|
|
json_pref_store->RegisterOnNextSuccessfulWriteReply(
|
|
base::BindOnce(&SuccessfulWriteReplyObserver::OnSuccessfulWrite,
|
|
base::Unretained(this)));
|
|
}
|
|
|
|
enum WriteCallbackObservationState {
|
|
NOT_CALLED,
|
|
CALLED_WITH_ERROR,
|
|
CALLED_WITH_SUCCESS,
|
|
};
|
|
|
|
class WriteCallbacksObserver {
|
|
public:
|
|
WriteCallbacksObserver() = default;
|
|
|
|
WriteCallbacksObserver(const WriteCallbacksObserver&) = delete;
|
|
WriteCallbacksObserver& operator=(const WriteCallbacksObserver&) = delete;
|
|
|
|
// Register OnWrite() to be called on the next write of |json_pref_store|.
|
|
void ObserveNextWriteCallback(JsonPrefStore* json_pref_store);
|
|
|
|
// Returns whether OnPreWrite() was called, and resets the observation state
|
|
// to false.
|
|
bool GetAndResetPreWriteObservationState();
|
|
|
|
// Returns the |WriteCallbackObservationState| which was observed, then resets
|
|
// it to |NOT_CALLED|.
|
|
WriteCallbackObservationState GetAndResetPostWriteObservationState();
|
|
|
|
JsonPrefStore::OnWriteCallbackPair GetCallbackPair() {
|
|
return std::make_pair(base::BindOnce(&WriteCallbacksObserver::OnPreWrite,
|
|
base::Unretained(this)),
|
|
base::BindOnce(&WriteCallbacksObserver::OnPostWrite,
|
|
base::Unretained(this)));
|
|
}
|
|
|
|
void OnPreWrite() {
|
|
EXPECT_FALSE(pre_write_called_);
|
|
pre_write_called_ = true;
|
|
}
|
|
|
|
void OnPostWrite(bool success) {
|
|
EXPECT_EQ(NOT_CALLED, post_write_observation_state_);
|
|
post_write_observation_state_ =
|
|
success ? CALLED_WITH_SUCCESS : CALLED_WITH_ERROR;
|
|
}
|
|
|
|
private:
|
|
bool pre_write_called_ = false;
|
|
WriteCallbackObservationState post_write_observation_state_ = NOT_CALLED;
|
|
};
|
|
|
|
void WriteCallbacksObserver::ObserveNextWriteCallback(JsonPrefStore* writer) {
|
|
writer->RegisterOnNextWriteSynchronousCallbacks(GetCallbackPair());
|
|
}
|
|
|
|
bool WriteCallbacksObserver::GetAndResetPreWriteObservationState() {
|
|
bool observation_state = pre_write_called_;
|
|
pre_write_called_ = false;
|
|
return observation_state;
|
|
}
|
|
|
|
WriteCallbackObservationState
|
|
WriteCallbacksObserver::GetAndResetPostWriteObservationState() {
|
|
WriteCallbackObservationState state = post_write_observation_state_;
|
|
pre_write_called_ = false;
|
|
post_write_observation_state_ = NOT_CALLED;
|
|
return state;
|
|
}
|
|
|
|
class JsonPrefStoreCallbackTest : public testing::Test {
|
|
public:
|
|
JsonPrefStoreCallbackTest() = default;
|
|
|
|
JsonPrefStoreCallbackTest(const JsonPrefStoreCallbackTest&) = delete;
|
|
JsonPrefStoreCallbackTest& operator=(const JsonPrefStoreCallbackTest&) =
|
|
delete;
|
|
|
|
protected:
|
|
void SetUp() override {
|
|
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
|
|
test_file_ = temp_dir_.GetPath().AppendASCII("test.json");
|
|
}
|
|
|
|
scoped_refptr<JsonPrefStore> CreatePrefStore() {
|
|
return base::MakeRefCounted<JsonPrefStore>(test_file_);
|
|
}
|
|
|
|
// Return the ImportantFileWriter for a given JsonPrefStore.
|
|
ImportantFileWriter* GetImportantFileWriter(JsonPrefStore* pref_store) {
|
|
return &(pref_store->writer_);
|
|
}
|
|
|
|
void TriggerFakeWriteForCallback(JsonPrefStore* pref_store, bool success) {
|
|
JsonPrefStore::PostWriteCallback(
|
|
base::BindOnce(&JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback,
|
|
pref_store->AsWeakPtr()),
|
|
base::BindOnce(&WriteCallbacksObserver::OnPostWrite,
|
|
base::Unretained(&write_callback_observer_)),
|
|
base::SequencedTaskRunner::GetCurrentDefault(), success);
|
|
}
|
|
|
|
SuccessfulWriteReplyObserver successful_write_reply_observer_;
|
|
WriteCallbacksObserver write_callback_observer_;
|
|
|
|
protected:
|
|
base::test::TaskEnvironment task_environment_{
|
|
base::test::TaskEnvironment::MainThreadType::DEFAULT,
|
|
base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED};
|
|
|
|
base::ScopedTempDir temp_dir_;
|
|
|
|
private:
|
|
base::FilePath test_file_;
|
|
};
|
|
|
|
TEST_F(JsonPrefStoreCallbackTest, TestSerializeDataCallbacks) {
|
|
base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
|
|
ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
|
|
|
|
std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
|
|
new InterceptingPrefFilter(write_callback_observer_.GetCallbackPair()));
|
|
auto pref_store = base::MakeRefCounted<JsonPrefStore>(
|
|
input_file, std::move(intercepting_pref_filter));
|
|
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
|
|
|
|
EXPECT_EQ(NOT_CALLED,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
pref_store->SetValue("normal", base::Value("normal"),
|
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
|
file_writer->DoScheduledWrite();
|
|
|
|
// The observer should not be invoked right away.
|
|
EXPECT_FALSE(write_callback_observer_.GetAndResetPreWriteObservationState());
|
|
EXPECT_EQ(NOT_CALLED,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
|
|
task_environment_.RunUntilIdle();
|
|
|
|
EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
|
|
EXPECT_EQ(CALLED_WITH_SUCCESS,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
}
|
|
|
|
TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacks) {
|
|
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
|
|
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
|
|
|
|
// Test RegisterOnNextWriteSynchronousCallbacks after
|
|
// RegisterOnNextSuccessfulWriteReply.
|
|
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
|
|
write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
|
|
file_writer->WriteNow("foo");
|
|
task_environment_.RunUntilIdle();
|
|
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
|
|
EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
|
|
EXPECT_EQ(CALLED_WITH_SUCCESS,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
|
|
// Test RegisterOnNextSuccessfulWriteReply after
|
|
// RegisterOnNextWriteSynchronousCallbacks.
|
|
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
|
|
write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
|
|
file_writer->WriteNow("foo");
|
|
task_environment_.RunUntilIdle();
|
|
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
|
|
EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
|
|
EXPECT_EQ(CALLED_WITH_SUCCESS,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
|
|
// Test RegisterOnNextSuccessfulWriteReply only.
|
|
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
|
|
file_writer->WriteNow("foo");
|
|
task_environment_.RunUntilIdle();
|
|
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
|
|
EXPECT_FALSE(write_callback_observer_.GetAndResetPreWriteObservationState());
|
|
EXPECT_EQ(NOT_CALLED,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
|
|
// Test RegisterOnNextWriteSynchronousCallbacks only.
|
|
write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
|
|
file_writer->WriteNow("foo");
|
|
task_environment_.RunUntilIdle();
|
|
EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
|
|
EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
|
|
EXPECT_EQ(CALLED_WITH_SUCCESS,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
}
|
|
|
|
TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksWithFakeFailure) {
|
|
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
|
|
|
|
// Confirm that the observers are invoked.
|
|
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
|
|
TriggerFakeWriteForCallback(pref_store.get(), true);
|
|
task_environment_.RunUntilIdle();
|
|
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
|
|
EXPECT_EQ(CALLED_WITH_SUCCESS,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
|
|
// Confirm that the observation states were reset.
|
|
EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
|
|
EXPECT_EQ(NOT_CALLED,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
|
|
// Confirm that re-installing the observers works for another write.
|
|
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
|
|
TriggerFakeWriteForCallback(pref_store.get(), true);
|
|
task_environment_.RunUntilIdle();
|
|
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
|
|
EXPECT_EQ(CALLED_WITH_SUCCESS,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
|
|
// Confirm that the successful observer is not invoked by an unsuccessful
|
|
// write, and that the synchronous observer is invoked.
|
|
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
|
|
TriggerFakeWriteForCallback(pref_store.get(), false);
|
|
task_environment_.RunUntilIdle();
|
|
EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
|
|
EXPECT_EQ(CALLED_WITH_ERROR,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
|
|
// Do a real write, and confirm that the successful observer was invoked after
|
|
// being set by |PostWriteCallback| by the last TriggerFakeWriteCallback.
|
|
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
|
|
file_writer->WriteNow("foo");
|
|
task_environment_.RunUntilIdle();
|
|
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
|
|
EXPECT_EQ(NOT_CALLED,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
}
|
|
|
|
TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksDuringProfileDeath) {
|
|
// Create a JsonPrefStore and attach observers to it, then delete it by making
|
|
// it go out of scope to simulate profile switch or Chrome shutdown.
|
|
{
|
|
scoped_refptr<JsonPrefStore> soon_out_of_scope_pref_store =
|
|
CreatePrefStore();
|
|
ImportantFileWriter* file_writer =
|
|
GetImportantFileWriter(soon_out_of_scope_pref_store.get());
|
|
successful_write_reply_observer_.ObserveNextWriteCallback(
|
|
soon_out_of_scope_pref_store.get());
|
|
write_callback_observer_.ObserveNextWriteCallback(
|
|
soon_out_of_scope_pref_store.get());
|
|
file_writer->WriteNow("foo");
|
|
}
|
|
task_environment_.RunUntilIdle();
|
|
EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
|
|
EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
|
|
EXPECT_EQ(CALLED_WITH_SUCCESS,
|
|
write_callback_observer_.GetAndResetPostWriteObservationState());
|
|
}
|
|
|
|
} // namespace base
|