1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/prefs/json_pref_store.h"
12 #include "base/compiler_specific.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/functional/bind.h"
16 #include "base/functional/callback.h"
17 #include "base/location.h"
18 #include "base/memory/ref_counted.h"
19 #include "base/metrics/histogram_samples.h"
20 #include "base/path_service.h"
21 #include "base/run_loop.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/synchronization/waitable_event.h"
26 #include "base/task/sequenced_task_runner.h"
27 #include "base/task/single_thread_task_runner.h"
28 #include "base/test/metrics/histogram_tester.h"
29 #include "base/test/scoped_feature_list.h"
30 #include "base/test/task_environment.h"
31 #include "base/threading/thread.h"
32 #include "base/values.h"
33 #include "components/prefs/persistent_pref_store_unittest.h"
34 #include "components/prefs/pref_filter.h"
35 #include "testing/gmock/include/gmock/gmock.h"
36 #include "testing/gtest/include/gtest/gtest.h"
41 const char kHomePage[] = "homepage";
43 const char kReadJson[] =
45 " \"homepage\": \"http://www.cnn.com\",\n"
46 " \"some_directory\": \"/usr/local/\",\n"
48 " \"new_windows_in_tabs\": true,\n"
53 const char kInvalidJson[] = "!@#$%^&";
55 // Expected output for tests using RunBasicJsonPrefStoreTest().
56 const char kWriteGolden[] =
57 "{\"homepage\":\"http://www.cnn.com\","
58 "\"long_int\":{\"pref\":\"214748364842\"},"
59 "\"some_directory\":\"/usr/sbin/\","
60 "\"tabs\":{\"max_tabs\":10,\"new_windows_in_tabs\":false}}";
62 // A PrefFilter that will intercept all calls to FilterOnLoad() and hold on
63 // to the |prefs| until explicitly asked to release them.
64 class InterceptingPrefFilter : public PrefFilter {
66 InterceptingPrefFilter();
67 InterceptingPrefFilter(OnWriteCallbackPair callback_pair);
69 InterceptingPrefFilter(const InterceptingPrefFilter&) = delete;
70 InterceptingPrefFilter& operator=(const InterceptingPrefFilter&) = delete;
72 ~InterceptingPrefFilter() override;
74 // PrefFilter implementation:
75 void FilterOnLoad(PostFilterOnLoadCallback post_filter_on_load_callback,
76 base::Value::Dict pref_store_contents) override;
77 void FilterUpdate(const std::string& path) override {}
78 OnWriteCallbackPair FilterSerializeData(
79 base::Value::Dict& pref_store_contents) override {
80 return std::move(on_write_callback_pair_);
82 void OnStoreDeletionFromDisk() override {}
84 bool has_intercepted_prefs() const { return intercepted_prefs_ != nullptr; }
86 // Finalize an intercepted read, handing |intercepted_prefs_| back to its
91 PostFilterOnLoadCallback post_filter_on_load_callback_;
92 std::unique_ptr<base::Value::Dict> intercepted_prefs_;
93 OnWriteCallbackPair on_write_callback_pair_;
96 InterceptingPrefFilter::InterceptingPrefFilter() {}
98 InterceptingPrefFilter::InterceptingPrefFilter(
99 OnWriteCallbackPair callback_pair) {
100 on_write_callback_pair_ = std::move(callback_pair);
103 InterceptingPrefFilter::~InterceptingPrefFilter() {}
105 void InterceptingPrefFilter::FilterOnLoad(
106 PostFilterOnLoadCallback post_filter_on_load_callback,
107 base::Value::Dict pref_store_contents) {
108 post_filter_on_load_callback_ = std::move(post_filter_on_load_callback);
110 std::make_unique<base::Value::Dict>(std::move(pref_store_contents));
113 void InterceptingPrefFilter::ReleasePrefs() {
114 EXPECT_FALSE(post_filter_on_load_callback_.is_null());
115 std::unique_ptr<base::Value::Dict> prefs = std::move(intercepted_prefs_);
116 std::move(post_filter_on_load_callback_).Run(std::move(*prefs), false);
119 class MockPrefStoreObserver : public PrefStore::Observer {
121 MOCK_METHOD1(OnPrefValueChanged, void (const std::string&));
122 MOCK_METHOD1(OnInitializationCompleted, void (bool));
125 class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
127 MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError));
130 enum class CommitPendingWriteMode {
133 // With reply callback.
135 // With synchronous notify callback (synchronous after the write -- shouldn't
136 // require pumping messages to observe).
137 WITH_SYNCHRONOUS_CALLBACK,
140 base::test::TaskEnvironment::ThreadPoolExecutionMode GetExecutionMode(
141 CommitPendingWriteMode commit_mode) {
142 switch (commit_mode) {
143 case CommitPendingWriteMode::WITHOUT_CALLBACK:
145 case CommitPendingWriteMode::WITH_CALLBACK:
146 return base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED;
147 case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK:
148 // Synchronous callbacks require async tasks to run on their own.
149 return base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC;
153 void CommitPendingWrite(JsonPrefStore* pref_store,
154 CommitPendingWriteMode commit_pending_write_mode,
155 base::test::TaskEnvironment* task_environment) {
156 switch (commit_pending_write_mode) {
157 case CommitPendingWriteMode::WITHOUT_CALLBACK: {
158 pref_store->CommitPendingWrite();
159 task_environment->RunUntilIdle();
162 case CommitPendingWriteMode::WITH_CALLBACK: {
163 TestCommitPendingWriteWithCallback(pref_store, task_environment);
166 case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK: {
167 base::WaitableEvent written;
168 pref_store->CommitPendingWrite(
170 base::BindOnce(&base::WaitableEvent::Signal, Unretained(&written)));
177 class JsonPrefStoreTest
178 : public testing::TestWithParam<CommitPendingWriteMode> {
181 : task_environment_(base::test::TaskEnvironment::MainThreadType::DEFAULT,
182 GetExecutionMode(GetParam())) {}
184 JsonPrefStoreTest(const JsonPrefStoreTest&) = delete;
185 JsonPrefStoreTest& operator=(const JsonPrefStoreTest&) = delete;
188 void SetUp() override {
189 commit_pending_write_mode_ = GetParam();
191 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
194 // The path to temporary directory used to contain the test operations.
195 base::ScopedTempDir temp_dir_;
197 base::test::TaskEnvironment task_environment_;
198 CommitPendingWriteMode commit_pending_write_mode_;
203 // Test fallback behavior for a nonexistent file.
204 TEST_P(JsonPrefStoreTest, NonExistentFile) {
205 base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
206 ASSERT_FALSE(PathExists(bogus_input_file));
207 auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
208 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
209 pref_store->ReadPrefs());
210 EXPECT_FALSE(pref_store->ReadOnly());
211 EXPECT_EQ(0u, pref_store->get_writer().previous_data_size());
214 // Test fallback behavior for an invalid file.
215 TEST_P(JsonPrefStoreTest, InvalidFile) {
216 base::FilePath invalid_file = temp_dir_.GetPath().AppendASCII("invalid.json");
217 ASSERT_TRUE(base::WriteFile(invalid_file, kInvalidJson));
219 auto pref_store = base::MakeRefCounted<JsonPrefStore>(invalid_file);
220 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
221 pref_store->ReadPrefs());
222 EXPECT_FALSE(pref_store->ReadOnly());
224 // The file should have been moved aside.
225 EXPECT_FALSE(PathExists(invalid_file));
226 base::FilePath moved_aside = temp_dir_.GetPath().AppendASCII("invalid.bad");
227 EXPECT_TRUE(PathExists(moved_aside));
229 std::string moved_aside_contents;
230 ASSERT_TRUE(base::ReadFileToString(moved_aside, &moved_aside_contents));
231 EXPECT_EQ(kInvalidJson, moved_aside_contents);
234 // This function is used to avoid code duplication while testing synchronous
235 // and asynchronous version of the JsonPrefStore loading. It validates that the
236 // given output file's contents matches kWriteGolden.
237 void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
238 const base::FilePath& output_file,
239 CommitPendingWriteMode commit_pending_write_mode,
240 base::test::TaskEnvironment* task_environment) {
241 const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
242 const char kMaxTabs[] = "tabs.max_tabs";
243 const char kLongIntPref[] = "long_int.pref";
245 std::string cnn("http://www.cnn.com");
248 EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual));
249 EXPECT_TRUE(actual->is_string());
250 EXPECT_EQ(cnn, actual->GetString());
252 const char kSomeDirectory[] = "some_directory";
254 EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
255 EXPECT_TRUE(actual->is_string());
256 EXPECT_EQ("/usr/local/", actual->GetString());
257 base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/"));
259 pref_store->SetValue(kSomeDirectory, Value(some_path.AsUTF8Unsafe()),
260 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
261 EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
262 EXPECT_TRUE(actual->is_string());
263 EXPECT_EQ(some_path.AsUTF8Unsafe(), actual->GetString());
265 // Test reading some other data types from sub-dictionaries.
266 EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
267 EXPECT_TRUE(actual->is_bool());
268 EXPECT_TRUE(actual->GetBool());
270 pref_store->SetValue(kNewWindowsInTabs, Value(false),
271 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
272 EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
273 EXPECT_TRUE(actual->is_bool());
274 EXPECT_FALSE(actual->GetBool());
276 EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
277 ASSERT_TRUE(actual->is_int());
278 EXPECT_EQ(20, actual->GetInt());
279 pref_store->SetValue(kMaxTabs, Value(10),
280 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
281 EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
282 ASSERT_TRUE(actual->is_int());
283 EXPECT_EQ(10, actual->GetInt());
285 pref_store->SetValue(kLongIntPref,
286 Value(base::NumberToString(214748364842LL)),
287 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
288 EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual));
289 EXPECT_TRUE(actual->is_string());
291 base::StringToInt64(actual->GetString(), &value);
292 EXPECT_EQ(214748364842LL, value);
294 // Serialize and compare to expected output.
295 CommitPendingWrite(pref_store, commit_pending_write_mode, task_environment);
297 std::string output_contents;
298 ASSERT_TRUE(base::ReadFileToString(output_file, &output_contents));
299 EXPECT_EQ(kWriteGolden, output_contents);
300 ASSERT_TRUE(base::DeleteFile(output_file));
303 TEST_P(JsonPrefStoreTest, Basic) {
304 base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
305 ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
307 // Test that the persistent value can be loaded.
308 ASSERT_TRUE(PathExists(input_file));
309 auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file);
310 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
311 EXPECT_FALSE(pref_store->ReadOnly());
312 EXPECT_TRUE(pref_store->IsInitializationComplete());
313 EXPECT_GT(pref_store->get_writer().previous_data_size(), 0u);
315 // The JSON file looks like this:
317 // "homepage": "http://www.cnn.com",
318 // "some_directory": "/usr/local/",
320 // "new_windows_in_tabs": true,
325 RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
326 commit_pending_write_mode_, &task_environment_);
329 TEST_P(JsonPrefStoreTest, BasicAsync) {
330 base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
331 ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
333 // Test that the persistent value can be loaded.
334 auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file);
337 MockPrefStoreObserver mock_observer;
338 pref_store->AddObserver(&mock_observer);
340 MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
341 pref_store->ReadPrefsAsync(mock_error_delegate);
343 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
344 EXPECT_CALL(*mock_error_delegate,
345 OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
346 task_environment_.RunUntilIdle();
347 pref_store->RemoveObserver(&mock_observer);
349 EXPECT_FALSE(pref_store->ReadOnly());
350 EXPECT_TRUE(pref_store->IsInitializationComplete());
351 EXPECT_GT(pref_store->get_writer().previous_data_size(), 0u);
354 // The JSON file looks like this:
356 // "homepage": "http://www.cnn.com",
357 // "some_directory": "/usr/local/",
359 // "new_windows_in_tabs": true,
364 RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
365 commit_pending_write_mode_, &task_environment_);
368 TEST_P(JsonPrefStoreTest, PreserveEmptyValues) {
369 FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json");
371 auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
373 // Set some keys with empty values.
374 pref_store->SetValue("list", base::Value(base::Value::List()),
375 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
376 pref_store->SetValue("dict", base::Value(base::Value::Dict()),
377 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
380 CommitPendingWrite(pref_store.get(), commit_pending_write_mode_,
384 pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
385 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
386 ASSERT_FALSE(pref_store->ReadOnly());
389 const Value* result = nullptr;
390 EXPECT_TRUE(pref_store->GetValue("list", &result));
391 EXPECT_EQ(Value::List(), *result);
392 EXPECT_TRUE(pref_store->GetValue("dict", &result));
393 EXPECT_EQ(Value::Dict(), *result);
396 // This test is just documenting some potentially non-obvious behavior. It
397 // shouldn't be taken as normative.
398 TEST_P(JsonPrefStoreTest, RemoveClearsEmptyParent) {
399 FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json");
401 auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
403 base::Value::Dict dict;
404 dict.Set("key", "value");
405 pref_store->SetValue("dict", base::Value(std::move(dict)),
406 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
408 pref_store->RemoveValue("dict.key",
409 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
411 const base::Value* retrieved_dict = nullptr;
412 bool has_dict = pref_store->GetValue("dict", &retrieved_dict);
413 EXPECT_FALSE(has_dict);
416 // Tests asynchronous reading of the file when there is no file.
417 TEST_P(JsonPrefStoreTest, AsyncNonExistingFile) {
418 base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
419 ASSERT_FALSE(PathExists(bogus_input_file));
420 auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
421 MockPrefStoreObserver mock_observer;
422 pref_store->AddObserver(&mock_observer);
424 MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate;
425 pref_store->ReadPrefsAsync(mock_error_delegate);
427 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
428 EXPECT_CALL(*mock_error_delegate,
429 OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1);
430 task_environment_.RunUntilIdle();
431 pref_store->RemoveObserver(&mock_observer);
433 EXPECT_FALSE(pref_store->ReadOnly());
436 TEST_P(JsonPrefStoreTest, ReadWithInterceptor) {
437 base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
438 ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
440 std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
441 new InterceptingPrefFilter());
442 InterceptingPrefFilter* raw_intercepting_pref_filter_ =
443 intercepting_pref_filter.get();
444 auto pref_store = base::MakeRefCounted<JsonPrefStore>(
445 input_file, std::move(intercepting_pref_filter));
447 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE,
448 pref_store->ReadPrefs());
449 EXPECT_FALSE(pref_store->ReadOnly());
451 // The store shouldn't be considered initialized until the interceptor
453 EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
454 EXPECT_FALSE(pref_store->IsInitializationComplete());
455 EXPECT_FALSE(pref_store->GetValue(kHomePage, nullptr));
457 raw_intercepting_pref_filter_->ReleasePrefs();
459 EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
460 EXPECT_TRUE(pref_store->IsInitializationComplete());
461 EXPECT_TRUE(pref_store->GetValue(kHomePage, nullptr));
463 // The JSON file looks like this:
465 // "homepage": "http://www.cnn.com",
466 // "some_directory": "/usr/local/",
468 // "new_windows_in_tabs": true,
473 RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
474 commit_pending_write_mode_, &task_environment_);
477 TEST_P(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
478 base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
479 ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
481 std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
482 new InterceptingPrefFilter());
483 InterceptingPrefFilter* raw_intercepting_pref_filter_ =
484 intercepting_pref_filter.get();
485 auto pref_store = base::MakeRefCounted<JsonPrefStore>(
486 input_file, std::move(intercepting_pref_filter));
488 MockPrefStoreObserver mock_observer;
489 pref_store->AddObserver(&mock_observer);
491 // Ownership of the |mock_error_delegate| is handed to the |pref_store| below.
492 MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
495 pref_store->ReadPrefsAsync(mock_error_delegate);
497 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0);
498 // EXPECT_CALL(*mock_error_delegate,
499 // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
500 task_environment_.RunUntilIdle();
502 EXPECT_FALSE(pref_store->ReadOnly());
503 EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
504 EXPECT_FALSE(pref_store->IsInitializationComplete());
505 EXPECT_FALSE(pref_store->GetValue(kHomePage, nullptr));
509 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
510 // EXPECT_CALL(*mock_error_delegate,
511 // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
513 raw_intercepting_pref_filter_->ReleasePrefs();
515 EXPECT_FALSE(pref_store->ReadOnly());
516 EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
517 EXPECT_TRUE(pref_store->IsInitializationComplete());
518 EXPECT_TRUE(pref_store->GetValue(kHomePage, nullptr));
521 pref_store->RemoveObserver(&mock_observer);
523 // The JSON file looks like this:
525 // "homepage": "http://www.cnn.com",
526 // "some_directory": "/usr/local/",
528 // "new_windows_in_tabs": true,
533 RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
534 commit_pending_write_mode_, &task_environment_);
537 TEST_P(JsonPrefStoreTest, RemoveValuesByPrefix) {
538 FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty.json");
540 auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
543 const std::string prefix = "pref";
544 const std::string subpref_name1 = "pref.a";
545 const std::string subpref_name2 = "pref.b";
546 const std::string other_name = "other";
548 pref_store->SetValue(subpref_name1, base::Value(42),
549 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
550 pref_store->SetValue(subpref_name2, base::Value(42),
551 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
552 pref_store->SetValue(other_name, base::Value(42),
553 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
555 pref_store->RemoveValuesByPrefixSilently(prefix);
556 EXPECT_FALSE(pref_store->GetValue(subpref_name1, &value));
557 EXPECT_FALSE(pref_store->GetValue(subpref_name2, &value));
558 EXPECT_TRUE(pref_store->GetValue(other_name, &value));
561 INSTANTIATE_TEST_SUITE_P(
562 JsonPrefStoreTestVariations,
564 ::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK,
565 CommitPendingWriteMode::WITH_CALLBACK,
566 CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
568 class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
570 JsonPrefStoreLossyWriteTest() = default;
572 JsonPrefStoreLossyWriteTest(const JsonPrefStoreLossyWriteTest&) = delete;
573 JsonPrefStoreLossyWriteTest& operator=(const JsonPrefStoreLossyWriteTest&) =
577 void SetUp() override {
578 JsonPrefStoreTest::SetUp();
579 test_file_ = temp_dir_.GetPath().AppendASCII("test.json");
582 scoped_refptr<JsonPrefStore> CreatePrefStore() {
583 return base::MakeRefCounted<JsonPrefStore>(test_file_);
586 // Return the ImportantFileWriter for a given JsonPrefStore.
587 ImportantFileWriter* GetImportantFileWriter(JsonPrefStore* pref_store) {
588 return &(pref_store->writer_);
591 // Get the contents of kTestFile. Pumps the message loop before returning the
593 std::string GetTestFileContents() {
594 task_environment_.RunUntilIdle();
595 std::string file_contents;
596 ReadFileToString(test_file_, &file_contents);
597 return file_contents;
601 base::FilePath test_file_;
604 TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteBasic) {
605 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
606 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
608 // Set a normal pref and check that it gets scheduled to be written.
609 ASSERT_FALSE(file_writer->HasPendingWrite());
610 pref_store->SetValue("normal", base::Value("normal"),
611 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
612 ASSERT_TRUE(file_writer->HasPendingWrite());
613 file_writer->DoScheduledWrite();
614 ASSERT_EQ("{\"normal\":\"normal\"}", GetTestFileContents());
615 ASSERT_FALSE(file_writer->HasPendingWrite());
617 // Set a lossy pref and check that it is not scheduled to be written.
618 // SetValue/RemoveValue.
619 pref_store->SetValue("lossy", base::Value("lossy"),
620 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
621 ASSERT_FALSE(file_writer->HasPendingWrite());
622 pref_store->RemoveValue("lossy", WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
623 ASSERT_FALSE(file_writer->HasPendingWrite());
625 // SetValueSilently/RemoveValueSilently.
626 pref_store->SetValueSilently("lossy", base::Value("lossy"),
627 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
628 ASSERT_FALSE(file_writer->HasPendingWrite());
629 pref_store->RemoveValueSilently("lossy",
630 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
631 ASSERT_FALSE(file_writer->HasPendingWrite());
633 // ReportValueChanged.
634 pref_store->SetValue("lossy", base::Value("lossy"),
635 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
636 ASSERT_FALSE(file_writer->HasPendingWrite());
637 pref_store->ReportValueChanged("lossy",
638 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
639 ASSERT_FALSE(file_writer->HasPendingWrite());
641 // Call CommitPendingWrite and check that the lossy pref and the normal pref
642 // are there with the last values set above.
643 pref_store->CommitPendingWrite(base::OnceClosure());
644 ASSERT_FALSE(file_writer->HasPendingWrite());
645 ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
646 GetTestFileContents());
649 TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) {
650 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
651 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
653 // Set a lossy pref and check that it is not scheduled to be written.
654 ASSERT_FALSE(file_writer->HasPendingWrite());
655 pref_store->SetValue("lossy", base::Value("lossy"),
656 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
657 ASSERT_FALSE(file_writer->HasPendingWrite());
659 // Set a normal pref and check that it is scheduled to be written.
660 pref_store->SetValue("normal", base::Value("normal"),
661 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
662 ASSERT_TRUE(file_writer->HasPendingWrite());
664 // Call DoScheduledWrite and check both prefs get written.
665 file_writer->DoScheduledWrite();
666 ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
667 GetTestFileContents());
668 ASSERT_FALSE(file_writer->HasPendingWrite());
671 TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) {
672 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
673 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
675 // Set a normal pref and check that it is scheduled to be written.
676 ASSERT_FALSE(file_writer->HasPendingWrite());
677 pref_store->SetValue("normal", base::Value("normal"),
678 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
679 ASSERT_TRUE(file_writer->HasPendingWrite());
681 // Set a lossy pref and check that the write is still scheduled.
682 pref_store->SetValue("lossy", base::Value("lossy"),
683 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
684 ASSERT_TRUE(file_writer->HasPendingWrite());
686 // Call DoScheduledWrite and check both prefs get written.
687 file_writer->DoScheduledWrite();
688 ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
689 GetTestFileContents());
690 ASSERT_FALSE(file_writer->HasPendingWrite());
693 TEST_P(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) {
694 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
695 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
697 // Set a lossy pref and check that it is not scheduled to be written.
698 pref_store->SetValue("lossy", base::Value("lossy"),
699 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
700 ASSERT_FALSE(file_writer->HasPendingWrite());
702 // Schedule pending lossy writes and check that it is scheduled.
703 pref_store->SchedulePendingLossyWrites();
704 ASSERT_TRUE(file_writer->HasPendingWrite());
706 // Call CommitPendingWrite and check that the lossy pref is there with the
707 // last value set above.
708 pref_store->CommitPendingWrite(base::OnceClosure());
709 ASSERT_FALSE(file_writer->HasPendingWrite());
710 ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents());
713 INSTANTIATE_TEST_SUITE_P(
714 JsonPrefStoreLossyWriteTestVariations,
715 JsonPrefStoreLossyWriteTest,
716 ::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK,
717 CommitPendingWriteMode::WITH_CALLBACK,
718 CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
720 class SuccessfulWriteReplyObserver {
722 SuccessfulWriteReplyObserver() = default;
724 SuccessfulWriteReplyObserver(const SuccessfulWriteReplyObserver&) = delete;
725 SuccessfulWriteReplyObserver& operator=(const SuccessfulWriteReplyObserver&) =
728 // Returns true if a successful write was observed via on_successful_write()
729 // and resets the observation state to false regardless.
730 bool GetAndResetObservationState() {
731 bool was_successful_write_observed = successful_write_reply_observed_;
732 successful_write_reply_observed_ = false;
733 return was_successful_write_observed;
736 // Register OnWrite() to be called on the next write of |json_pref_store|.
737 void ObserveNextWriteCallback(JsonPrefStore* json_pref_store);
739 void OnSuccessfulWrite() {
740 EXPECT_FALSE(successful_write_reply_observed_);
741 successful_write_reply_observed_ = true;
745 bool successful_write_reply_observed_ = false;
748 void SuccessfulWriteReplyObserver::ObserveNextWriteCallback(
749 JsonPrefStore* json_pref_store) {
750 json_pref_store->RegisterOnNextSuccessfulWriteReply(
751 base::BindOnce(&SuccessfulWriteReplyObserver::OnSuccessfulWrite,
752 base::Unretained(this)));
755 enum WriteCallbackObservationState {
761 class WriteCallbacksObserver {
763 WriteCallbacksObserver() = default;
765 WriteCallbacksObserver(const WriteCallbacksObserver&) = delete;
766 WriteCallbacksObserver& operator=(const WriteCallbacksObserver&) = delete;
768 // Register OnWrite() to be called on the next write of |json_pref_store|.
769 void ObserveNextWriteCallback(JsonPrefStore* json_pref_store);
771 // Returns whether OnPreWrite() was called, and resets the observation state
773 bool GetAndResetPreWriteObservationState();
775 // Returns the |WriteCallbackObservationState| which was observed, then resets
776 // it to |NOT_CALLED|.
777 WriteCallbackObservationState GetAndResetPostWriteObservationState();
779 JsonPrefStore::OnWriteCallbackPair GetCallbackPair() {
780 return std::make_pair(base::BindOnce(&WriteCallbacksObserver::OnPreWrite,
781 base::Unretained(this)),
782 base::BindOnce(&WriteCallbacksObserver::OnPostWrite,
783 base::Unretained(this)));
787 EXPECT_FALSE(pre_write_called_);
788 pre_write_called_ = true;
791 void OnPostWrite(bool success) {
792 EXPECT_EQ(NOT_CALLED, post_write_observation_state_);
793 post_write_observation_state_ =
794 success ? CALLED_WITH_SUCCESS : CALLED_WITH_ERROR;
798 bool pre_write_called_ = false;
799 WriteCallbackObservationState post_write_observation_state_ = NOT_CALLED;
802 void WriteCallbacksObserver::ObserveNextWriteCallback(JsonPrefStore* writer) {
803 writer->RegisterOnNextWriteSynchronousCallbacks(GetCallbackPair());
806 bool WriteCallbacksObserver::GetAndResetPreWriteObservationState() {
807 bool observation_state = pre_write_called_;
808 pre_write_called_ = false;
809 return observation_state;
812 WriteCallbackObservationState
813 WriteCallbacksObserver::GetAndResetPostWriteObservationState() {
814 WriteCallbackObservationState state = post_write_observation_state_;
815 pre_write_called_ = false;
816 post_write_observation_state_ = NOT_CALLED;
820 class JsonPrefStoreCallbackTest : public testing::Test {
822 JsonPrefStoreCallbackTest() = default;
824 JsonPrefStoreCallbackTest(const JsonPrefStoreCallbackTest&) = delete;
825 JsonPrefStoreCallbackTest& operator=(const JsonPrefStoreCallbackTest&) =
829 void SetUp() override {
830 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
831 test_file_ = temp_dir_.GetPath().AppendASCII("test.json");
834 scoped_refptr<JsonPrefStore> CreatePrefStore() {
835 return base::MakeRefCounted<JsonPrefStore>(test_file_);
838 // Return the ImportantFileWriter for a given JsonPrefStore.
839 ImportantFileWriter* GetImportantFileWriter(JsonPrefStore* pref_store) {
840 return &(pref_store->writer_);
843 void TriggerFakeWriteForCallback(JsonPrefStore* pref_store, bool success) {
844 JsonPrefStore::PostWriteCallback(
845 base::BindOnce(&JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback,
846 pref_store->AsWeakPtr()),
847 base::BindOnce(&WriteCallbacksObserver::OnPostWrite,
848 base::Unretained(&write_callback_observer_)),
849 base::SequencedTaskRunner::GetCurrentDefault(), success);
852 SuccessfulWriteReplyObserver successful_write_reply_observer_;
853 WriteCallbacksObserver write_callback_observer_;
856 base::test::TaskEnvironment task_environment_{
857 base::test::TaskEnvironment::MainThreadType::DEFAULT,
858 base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED};
860 base::ScopedTempDir temp_dir_;
863 base::FilePath test_file_;
866 TEST_F(JsonPrefStoreCallbackTest, TestSerializeDataCallbacks) {
867 base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
868 ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
870 std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
871 new InterceptingPrefFilter(write_callback_observer_.GetCallbackPair()));
872 auto pref_store = base::MakeRefCounted<JsonPrefStore>(
873 input_file, std::move(intercepting_pref_filter));
874 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
876 EXPECT_EQ(NOT_CALLED,
877 write_callback_observer_.GetAndResetPostWriteObservationState());
878 pref_store->SetValue("normal", base::Value("normal"),
879 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
880 file_writer->DoScheduledWrite();
882 // The observer should not be invoked right away.
883 EXPECT_FALSE(write_callback_observer_.GetAndResetPreWriteObservationState());
884 EXPECT_EQ(NOT_CALLED,
885 write_callback_observer_.GetAndResetPostWriteObservationState());
887 task_environment_.RunUntilIdle();
889 EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
890 EXPECT_EQ(CALLED_WITH_SUCCESS,
891 write_callback_observer_.GetAndResetPostWriteObservationState());
894 TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacks) {
895 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
896 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
898 // Test RegisterOnNextWriteSynchronousCallbacks after
899 // RegisterOnNextSuccessfulWriteReply.
900 successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
901 write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
902 file_writer->WriteNow("foo");
903 task_environment_.RunUntilIdle();
904 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
905 EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
906 EXPECT_EQ(CALLED_WITH_SUCCESS,
907 write_callback_observer_.GetAndResetPostWriteObservationState());
909 // Test RegisterOnNextSuccessfulWriteReply after
910 // RegisterOnNextWriteSynchronousCallbacks.
911 successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
912 write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
913 file_writer->WriteNow("foo");
914 task_environment_.RunUntilIdle();
915 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
916 EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
917 EXPECT_EQ(CALLED_WITH_SUCCESS,
918 write_callback_observer_.GetAndResetPostWriteObservationState());
920 // Test RegisterOnNextSuccessfulWriteReply only.
921 successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
922 file_writer->WriteNow("foo");
923 task_environment_.RunUntilIdle();
924 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
925 EXPECT_FALSE(write_callback_observer_.GetAndResetPreWriteObservationState());
926 EXPECT_EQ(NOT_CALLED,
927 write_callback_observer_.GetAndResetPostWriteObservationState());
929 // Test RegisterOnNextWriteSynchronousCallbacks only.
930 write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
931 file_writer->WriteNow("foo");
932 task_environment_.RunUntilIdle();
933 EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
934 EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
935 EXPECT_EQ(CALLED_WITH_SUCCESS,
936 write_callback_observer_.GetAndResetPostWriteObservationState());
939 TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksWithFakeFailure) {
940 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
942 // Confirm that the observers are invoked.
943 successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
944 TriggerFakeWriteForCallback(pref_store.get(), true);
945 task_environment_.RunUntilIdle();
946 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
947 EXPECT_EQ(CALLED_WITH_SUCCESS,
948 write_callback_observer_.GetAndResetPostWriteObservationState());
950 // Confirm that the observation states were reset.
951 EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
952 EXPECT_EQ(NOT_CALLED,
953 write_callback_observer_.GetAndResetPostWriteObservationState());
955 // Confirm that re-installing the observers works for another write.
956 successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
957 TriggerFakeWriteForCallback(pref_store.get(), true);
958 task_environment_.RunUntilIdle();
959 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
960 EXPECT_EQ(CALLED_WITH_SUCCESS,
961 write_callback_observer_.GetAndResetPostWriteObservationState());
963 // Confirm that the successful observer is not invoked by an unsuccessful
964 // write, and that the synchronous observer is invoked.
965 successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
966 TriggerFakeWriteForCallback(pref_store.get(), false);
967 task_environment_.RunUntilIdle();
968 EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
969 EXPECT_EQ(CALLED_WITH_ERROR,
970 write_callback_observer_.GetAndResetPostWriteObservationState());
972 // Do a real write, and confirm that the successful observer was invoked after
973 // being set by |PostWriteCallback| by the last TriggerFakeWriteCallback.
974 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
975 file_writer->WriteNow("foo");
976 task_environment_.RunUntilIdle();
977 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
978 EXPECT_EQ(NOT_CALLED,
979 write_callback_observer_.GetAndResetPostWriteObservationState());
982 TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksDuringProfileDeath) {
983 // Create a JsonPrefStore and attach observers to it, then delete it by making
984 // it go out of scope to simulate profile switch or Chrome shutdown.
986 scoped_refptr<JsonPrefStore> soon_out_of_scope_pref_store =
988 ImportantFileWriter* file_writer =
989 GetImportantFileWriter(soon_out_of_scope_pref_store.get());
990 successful_write_reply_observer_.ObserveNextWriteCallback(
991 soon_out_of_scope_pref_store.get());
992 write_callback_observer_.ObserveNextWriteCallback(
993 soon_out_of_scope_pref_store.get());
994 file_writer->WriteNow("foo");
996 task_environment_.RunUntilIdle();
997 EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
998 EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
999 EXPECT_EQ(CALLED_WITH_SUCCESS,
1000 write_callback_observer_.GetAndResetPostWriteObservationState());