blob: 3e2a767ac2313f702fc7636fcfcfe010ad3d75bc [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2012 The Chromium Authors
bauerb@chromium.org277404c22010-04-22 13:09:452// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
brettwf00b9b42016-02-01 22:11:385#include "components/prefs/json_pref_store.h"
tfarina@chromium.org00c87822012-11-27 19:09:176
avi9ef8bb02015-12-24 05:29:367#include <stdint.h>
8
dcheng5f043bc2016-04-22 19:09:069#include <memory>
danakj0c8d4aa2015-11-25 05:29:5810#include <utility>
11
Gabriel Charette788eaf62018-08-07 20:11:4612#include "base/compiler_specific.h"
brettw@chromium.orge3177dd52014-08-13 20:22:1413#include "base/files/file_util.h"
brettw@chromium.orgea1a3f62012-11-16 20:34:2314#include "base/files/scoped_temp_dir.h"
Avi Drissman12be0312023-01-11 09:16:0915#include "base/functional/bind.h"
16#include "base/functional/callback.h"
skyostil054861d2015-04-30 19:06:1517#include "base/location.h"
levin@chromium.org3b63f8f42011-03-28 01:54:1518#include "base/memory/ref_counted.h"
raymesbfb910a2015-04-29 07:43:0919#include "base/metrics/histogram_samples.h"
bauerb@chromium.org277404c22010-04-22 13:09:4520#include "base/path_service.h"
tfarina@chromium.orgf7b98b32013-02-05 08:14:1521#include "base/run_loop.h"
brettw@chromium.orgdfa049e2013-02-07 02:57:2222#include "base/strings/string_number_conversions.h"
avi@chromium.orgd529cb02013-06-10 19:06:5723#include "base/strings/string_util.h"
avi@chromium.orga4ea1f12013-06-07 18:37:0724#include "base/strings/utf_string_conversions.h"
Gabriel Charette788eaf62018-08-07 20:11:4625#include "base/synchronization/waitable_event.h"
Sean Maher52fa5a72022-11-14 15:53:2526#include "base/task/sequenced_task_runner.h"
Patrick Monette643cdf62021-10-15 19:13:4227#include "base/task/single_thread_task_runner.h"
Devlin Cronin69228f42018-06-01 17:25:1028#include "base/test/metrics/histogram_tester.h"
Patrick Nolandc48179742022-11-10 20:55:4329#include "base/test/scoped_feature_list.h"
Gabriel Charettec7108742019-08-23 03:31:4030#include "base/test/task_environment.h"
brettw@chromium.org34b99632011-01-01 01:01:0631#include "base/threading/thread.h"
bauerb@chromium.org277404c22010-04-22 13:09:4532#include "base/values.h"
Francois Doray405dd2d2017-06-09 15:23:3333#include "components/prefs/persistent_pref_store_unittest.h"
brettwf00b9b42016-02-01 22:11:3834#include "components/prefs/pref_filter.h"
altimofeev@chromium.org845b43a82011-05-11 10:14:4335#include "testing/gmock/include/gmock/gmock.h"
bauerb@chromium.org277404c22010-04-22 13:09:4536#include "testing/gtest/include/gtest/gtest.h"
37
tfarina@chromium.org7e3ec42c2012-12-16 05:13:2138namespace base {
altimofeev@chromium.org845b43a82011-05-11 10:14:4339namespace {
40
tfarina@chromium.org5bfdcfd2012-11-22 22:08:2441const char kHomePage[] = "homepage";
42
brettw58cd1f12016-01-30 05:56:0543const char kReadJson[] =
44 "{\n"
45 " \"homepage\": \"http://www.cnn.com\",\n"
46 " \"some_directory\": \"/usr/local/\",\n"
47 " \"tabs\": {\n"
48 " \"new_windows_in_tabs\": true,\n"
49 " \"max_tabs\": 20\n"
50 " }\n"
51 "}";
52
53const char kInvalidJson[] = "!@#$%^&";
54
55// Expected output for tests using RunBasicJsonPrefStoreTest().
56const 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}}";
61
gab@chromium.orge33c9512014-05-12 02:24:1362// A PrefFilter that will intercept all calls to FilterOnLoad() and hold on
63// to the |prefs| until explicitly asked to release them.
64class InterceptingPrefFilter : public PrefFilter {
65 public:
66 InterceptingPrefFilter();
probergec503d692016-09-28 19:51:0567 InterceptingPrefFilter(OnWriteCallbackPair callback_pair);
Peter Boström09c01822021-09-20 22:43:2768
69 InterceptingPrefFilter(const InterceptingPrefFilter&) = delete;
70 InterceptingPrefFilter& operator=(const InterceptingPrefFilter&) = delete;
71
dcheng56488182014-10-21 10:54:5172 ~InterceptingPrefFilter() override;
gab@chromium.orge33c9512014-05-12 02:24:1373
74 // PrefFilter implementation:
Nan Lind0432372022-12-16 21:58:2475 void FilterOnLoad(PostFilterOnLoadCallback post_filter_on_load_callback,
76 base::Value::Dict pref_store_contents) override;
dcheng56488182014-10-21 10:54:5177 void FilterUpdate(const std::string& path) override {}
probergec503d692016-09-28 19:51:0578 OnWriteCallbackPair FilterSerializeData(
Nan Lind0432372022-12-16 21:58:2479 base::Value::Dict& pref_store_contents) override {
Sylvain Defresne8812fcda2019-10-07 21:45:0080 return std::move(on_write_callback_pair_);
probergec503d692016-09-28 19:51:0581 }
proberge45e347282017-08-16 21:24:0582 void OnStoreDeletionFromDisk() override {}
gab@chromium.orge33c9512014-05-12 02:24:1383
Ivan Kotenkov75b1c3a2017-10-24 14:47:2484 bool has_intercepted_prefs() const { return intercepted_prefs_ != nullptr; }
gab@chromium.orge33c9512014-05-12 02:24:1385
gab@chromium.orgcfcf0e52014-06-20 18:29:4786 // Finalize an intercepted read, handing |intercepted_prefs_| back to its
gab@chromium.orge33c9512014-05-12 02:24:1387 // JsonPrefStore.
88 void ReleasePrefs();
89
90 private:
91 PostFilterOnLoadCallback post_filter_on_load_callback_;
Nan Lind0432372022-12-16 21:58:2492 std::unique_ptr<base::Value::Dict> intercepted_prefs_;
probergec503d692016-09-28 19:51:0593 OnWriteCallbackPair on_write_callback_pair_;
gab@chromium.orge33c9512014-05-12 02:24:1394};
95
96InterceptingPrefFilter::InterceptingPrefFilter() {}
probergec503d692016-09-28 19:51:0597
98InterceptingPrefFilter::InterceptingPrefFilter(
99 OnWriteCallbackPair callback_pair) {
Sylvain Defresne8812fcda2019-10-07 21:45:00100 on_write_callback_pair_ = std::move(callback_pair);
probergec503d692016-09-28 19:51:05101}
102
gab@chromium.orge33c9512014-05-12 02:24:13103InterceptingPrefFilter::~InterceptingPrefFilter() {}
104
105void InterceptingPrefFilter::FilterOnLoad(
Sylvain Defresned17a4252019-10-11 15:50:24106 PostFilterOnLoadCallback post_filter_on_load_callback,
Nan Lind0432372022-12-16 21:58:24107 base::Value::Dict pref_store_contents) {
Sylvain Defresned17a4252019-10-11 15:50:24108 post_filter_on_load_callback_ = std::move(post_filter_on_load_callback);
Nan Lind0432372022-12-16 21:58:24109 intercepted_prefs_ =
110 std::make_unique<base::Value::Dict>(std::move(pref_store_contents));
gab@chromium.orge33c9512014-05-12 02:24:13111}
112
113void InterceptingPrefFilter::ReleasePrefs() {
114 EXPECT_FALSE(post_filter_on_load_callback_.is_null());
Nan Lind0432372022-12-16 21:58:24115 std::unique_ptr<base::Value::Dict> prefs = std::move(intercepted_prefs_);
116 std::move(post_filter_on_load_callback_).Run(std::move(*prefs), false);
gab@chromium.orge33c9512014-05-12 02:24:13117}
118
altimofeev@chromium.org845b43a82011-05-11 10:14:43119class MockPrefStoreObserver : public PrefStore::Observer {
120 public:
121 MOCK_METHOD1(OnPrefValueChanged, void (const std::string&));
122 MOCK_METHOD1(OnInitializationCompleted, void (bool));
123};
124
125class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
126 public:
127 MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError));
128};
129
Francois Doray405dd2d2017-06-09 15:23:33130enum class CommitPendingWriteMode {
Gabriel Charette788eaf62018-08-07 20:11:46131 // Basic mode.
Francois Doray405dd2d2017-06-09 15:23:33132 WITHOUT_CALLBACK,
Gabriel Charette788eaf62018-08-07 20:11:46133 // With reply callback.
Francois Doray405dd2d2017-06-09 15:23:33134 WITH_CALLBACK,
Gabriel Charette788eaf62018-08-07 20:11:46135 // With synchronous notify callback (synchronous after the write -- shouldn't
136 // require pumping messages to observe).
137 WITH_SYNCHRONOUS_CALLBACK,
Francois Doray405dd2d2017-06-09 15:23:33138};
altimofeev@chromium.org845b43a82011-05-11 10:14:43139
Gabriel Charettedfa36042019-08-19 17:30:11140base::test::TaskEnvironment::ThreadPoolExecutionMode GetExecutionMode(
Gabriel Charette788eaf62018-08-07 20:11:46141 CommitPendingWriteMode commit_mode) {
142 switch (commit_mode) {
143 case CommitPendingWriteMode::WITHOUT_CALLBACK:
Roland Bock17c8f5f62022-01-04 15:25:54144 [[fallthrough]];
Gabriel Charette788eaf62018-08-07 20:11:46145 case CommitPendingWriteMode::WITH_CALLBACK:
Gabriel Charettedfa36042019-08-19 17:30:11146 return base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED;
Gabriel Charette788eaf62018-08-07 20:11:46147 case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK:
148 // Synchronous callbacks require async tasks to run on their own.
Gabriel Charettedfa36042019-08-19 17:30:11149 return base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC;
Gabriel Charette788eaf62018-08-07 20:11:46150 }
151}
152
Gabriel Charettedfa36042019-08-19 17:30:11153void CommitPendingWrite(JsonPrefStore* pref_store,
154 CommitPendingWriteMode commit_pending_write_mode,
155 base::test::TaskEnvironment* task_environment) {
Gabriel Charette788eaf62018-08-07 20:11:46156 switch (commit_pending_write_mode) {
157 case CommitPendingWriteMode::WITHOUT_CALLBACK: {
158 pref_store->CommitPendingWrite();
Gabriel Charettedfa36042019-08-19 17:30:11159 task_environment->RunUntilIdle();
Gabriel Charette788eaf62018-08-07 20:11:46160 break;
161 }
162 case CommitPendingWriteMode::WITH_CALLBACK: {
Gabriel Charettedfa36042019-08-19 17:30:11163 TestCommitPendingWriteWithCallback(pref_store, task_environment);
Gabriel Charette788eaf62018-08-07 20:11:46164 break;
165 }
166 case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK: {
167 base::WaitableEvent written;
168 pref_store->CommitPendingWrite(
169 base::OnceClosure(),
170 base::BindOnce(&base::WaitableEvent::Signal, Unretained(&written)));
171 written.Wait();
172 break;
173 }
Francois Doray405dd2d2017-06-09 15:23:33174 }
175}
176
177class JsonPrefStoreTest
Marc Treib85b56d542023-06-12 10:15:49178 : public testing::TestWithParam<CommitPendingWriteMode> {
probergefc46ac12016-09-21 18:03:00179 public:
Francois Doray5f547952017-07-25 21:02:05180 JsonPrefStoreTest()
Gabriel Charettedfa36042019-08-19 17:30:11181 : task_environment_(base::test::TaskEnvironment::MainThreadType::DEFAULT,
Marc Treib85b56d542023-06-12 10:15:49182 GetExecutionMode(GetParam())) {}
probergefc46ac12016-09-21 18:03:00183
Peter Boström9f667c382021-10-01 20:09:31184 JsonPrefStoreTest(const JsonPrefStoreTest&) = delete;
185 JsonPrefStoreTest& operator=(const JsonPrefStoreTest&) = delete;
186
bauerb@chromium.org277404c22010-04-22 13:09:45187 protected:
dcheng8aef37612014-12-23 02:56:47188 void SetUp() override {
Marc Treib85b56d542023-06-12 10:15:49189 commit_pending_write_mode_ = GetParam();
Patrick Nolandc48179742022-11-10 20:55:43190
mkwst@chromium.org3a305db2011-04-12 13:40:53191 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
bauerb@chromium.org277404c22010-04-22 13:09:45192 }
193
mkwst@chromium.org3a305db2011-04-12 13:40:53194 // The path to temporary directory used to contain the test operations.
brettw@chromium.orgea1a3f62012-11-16 20:34:23195 base::ScopedTempDir temp_dir_;
probergefc46ac12016-09-21 18:03:00196
Greg Thompson191ab5f2019-10-31 14:07:45197 base::test::TaskEnvironment task_environment_;
Patrick Nolandc48179742022-11-10 20:55:43198 CommitPendingWriteMode commit_pending_write_mode_;
bauerb@chromium.org277404c22010-04-22 13:09:45199};
200
Francois Doray405dd2d2017-06-09 15:23:33201} // namespace
202
bauerb@chromium.org277404c22010-04-22 13:09:45203// Test fallback behavior for a nonexistent file.
Gabriel Charette788eaf62018-08-07 20:11:46204TEST_P(JsonPrefStoreTest, NonExistentFile) {
vabr8023d872016-09-15 08:12:22205 base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
brettw@chromium.org7567484142013-07-11 17:36:07206 ASSERT_FALSE(PathExists(bogus_input_file));
Francois Doray405dd2d2017-06-09 15:23:33207 auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
mnissler@chromium.orgf2d1f612010-12-09 15:10:17208 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
battre@chromium.org9a8c4022011-01-25 14:25:33209 pref_store->ReadPrefs());
210 EXPECT_FALSE(pref_store->ReadOnly());
Dominic Battrec7911fe2020-11-26 21:13:18211 EXPECT_EQ(0u, pref_store->get_writer().previous_data_size());
bauerb@chromium.org277404c22010-04-22 13:09:45212}
213
214// Test fallback behavior for an invalid file.
Gabriel Charette788eaf62018-08-07 20:11:46215TEST_P(JsonPrefStoreTest, InvalidFile) {
vabr8023d872016-09-15 08:12:22216 base::FilePath invalid_file = temp_dir_.GetPath().AppendASCII("invalid.json");
Claudio DeSouzac3e85082023-02-27 16:07:15217 ASSERT_TRUE(base::WriteFile(invalid_file, kInvalidJson));
brettw58cd1f12016-01-30 05:56:05218
Francois Doray405dd2d2017-06-09 15:23:33219 auto pref_store = base::MakeRefCounted<JsonPrefStore>(invalid_file);
mnissler@chromium.orgf2d1f612010-12-09 15:10:17220 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
battre@chromium.org9a8c4022011-01-25 14:25:33221 pref_store->ReadPrefs());
222 EXPECT_FALSE(pref_store->ReadOnly());
bauerb@chromium.org277404c22010-04-22 13:09:45223
224 // The file should have been moved aside.
brettw@chromium.org7567484142013-07-11 17:36:07225 EXPECT_FALSE(PathExists(invalid_file));
vabr8023d872016-09-15 08:12:22226 base::FilePath moved_aside = temp_dir_.GetPath().AppendASCII("invalid.bad");
brettw@chromium.org7567484142013-07-11 17:36:07227 EXPECT_TRUE(PathExists(moved_aside));
brettw58cd1f12016-01-30 05:56:05228
229 std::string moved_aside_contents;
230 ASSERT_TRUE(base::ReadFileToString(moved_aside, &moved_aside_contents));
231 EXPECT_EQ(kInvalidJson, moved_aside_contents);
bauerb@chromium.org277404c22010-04-22 13:09:45232}
233
brettw58cd1f12016-01-30 05:56:05234// 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.
Gabriel Charettedfa36042019-08-19 17:30:11237void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
238 const base::FilePath& output_file,
239 CommitPendingWriteMode commit_pending_write_mode,
240 base::test::TaskEnvironment* task_environment) {
viettrungluu@chromium.org57ecc4b2010-08-11 03:02:51241 const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
242 const char kMaxTabs[] = "tabs.max_tabs";
243 const char kLongIntPref[] = "long_int.pref";
bauerb@chromium.org277404c22010-04-22 13:09:45244
viettrungluu@chromium.org57ecc4b2010-08-11 03:02:51245 std::string cnn("http://www.cnn.com");
bauerb@chromium.org277404c22010-04-22 13:09:45246
battre@chromium.org68bf41a2011-03-25 16:38:31247 const Value* actual;
tfarina@chromium.org5bfdcfd2012-11-22 22:08:24248 EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual));
Ayu Ishiie48b7942021-05-12 18:03:18249 EXPECT_TRUE(actual->is_string());
250 EXPECT_EQ(cnn, actual->GetString());
bauerb@chromium.org277404c22010-04-22 13:09:45251
viettrungluu@chromium.org57ecc4b2010-08-11 03:02:51252 const char kSomeDirectory[] = "some_directory";
bauerb@chromium.org277404c22010-04-22 13:09:45253
bauerb@chromium.org892f1d62012-11-08 18:24:34254 EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
Ayu Ishiie48b7942021-05-12 18:03:18255 EXPECT_TRUE(actual->is_string());
256 EXPECT_EQ("/usr/local/", actual->GetString());
brettw@chromium.org023ad6ab2013-02-17 05:07:23257 base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/"));
mnissler@chromium.orgf2d1f612010-12-09 15:10:17258
Roland Bocke01119d2022-09-29 01:21:41259 pref_store->SetValue(kSomeDirectory, Value(some_path.AsUTF8Unsafe()),
raymes76de1af2015-05-06 03:22:21260 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
bauerb@chromium.org892f1d62012-11-08 18:24:34261 EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
Ayu Ishiie48b7942021-05-12 18:03:18262 EXPECT_TRUE(actual->is_string());
263 EXPECT_EQ(some_path.AsUTF8Unsafe(), actual->GetString());
bauerb@chromium.org277404c22010-04-22 13:09:45264
265 // Test reading some other data types from sub-dictionaries.
bauerb@chromium.org892f1d62012-11-08 18:24:34266 EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
Ayu Ishiie48b7942021-05-12 18:03:18267 EXPECT_TRUE(actual->is_bool());
268 EXPECT_TRUE(actual->GetBool());
bauerb@chromium.org277404c22010-04-22 13:09:45269
Roland Bocke01119d2022-09-29 01:21:41270 pref_store->SetValue(kNewWindowsInTabs, Value(false),
raymes76de1af2015-05-06 03:22:21271 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
bauerb@chromium.org892f1d62012-11-08 18:24:34272 EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
Ayu Ishiie48b7942021-05-12 18:03:18273 EXPECT_TRUE(actual->is_bool());
274 EXPECT_FALSE(actual->GetBool());
bauerb@chromium.org277404c22010-04-22 13:09:45275
bauerb@chromium.org892f1d62012-11-08 18:24:34276 EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
Minoru Chikamune8dcb6e92021-04-17 00:54:26277 ASSERT_TRUE(actual->is_int());
278 EXPECT_EQ(20, actual->GetInt());
Roland Bocke01119d2022-09-29 01:21:41279 pref_store->SetValue(kMaxTabs, Value(10),
raymes76de1af2015-05-06 03:22:21280 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
bauerb@chromium.org892f1d62012-11-08 18:24:34281 EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
Minoru Chikamune8dcb6e92021-04-17 00:54:26282 ASSERT_TRUE(actual->is_int());
283 EXPECT_EQ(10, actual->GetInt());
bauerb@chromium.org277404c22010-04-22 13:09:45284
Roland Bocke01119d2022-09-29 01:21:41285 pref_store->SetValue(kLongIntPref,
286 Value(base::NumberToString(214748364842LL)),
287 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
bauerb@chromium.org892f1d62012-11-08 18:24:34288 EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual));
Ayu Ishiie48b7942021-05-12 18:03:18289 EXPECT_TRUE(actual->is_string());
avi9ef8bb02015-12-24 05:29:36290 int64_t value;
Ayu Ishiie48b7942021-05-12 18:03:18291 base::StringToInt64(actual->GetString(), &value);
brettw@chromium.orge83326f2010-07-31 17:29:25292 EXPECT_EQ(214748364842LL, value);
bauerb@chromium.org277404c22010-04-22 13:09:45293
294 // Serialize and compare to expected output.
Gabriel Charettedfa36042019-08-19 17:30:11295 CommitPendingWrite(pref_store, commit_pending_write_mode, task_environment);
brettw58cd1f12016-01-30 05:56:05296
297 std::string output_contents;
298 ASSERT_TRUE(base::ReadFileToString(output_file, &output_contents));
299 EXPECT_EQ(kWriteGolden, output_contents);
Lei Zhange6e8c1322020-07-08 20:04:12300 ASSERT_TRUE(base::DeleteFile(output_file));
bauerb@chromium.org277404c22010-04-22 13:09:45301}
altimofeev@chromium.org845b43a82011-05-11 10:14:43302
Francois Doray405dd2d2017-06-09 15:23:33303TEST_P(JsonPrefStoreTest, Basic) {
vabr8023d872016-09-15 08:12:22304 base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
Claudio DeSouzac3e85082023-02-27 16:07:15305 ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
altimofeev@chromium.org845b43a82011-05-11 10:14:43306
307 // Test that the persistent value can be loaded.
brettw@chromium.org7567484142013-07-11 17:36:07308 ASSERT_TRUE(PathExists(input_file));
Francois Doray405dd2d2017-06-09 15:23:33309 auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file);
altimofeev@chromium.org845b43a82011-05-11 10:14:43310 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
gab@chromium.orge33c9512014-05-12 02:24:13311 EXPECT_FALSE(pref_store->ReadOnly());
312 EXPECT_TRUE(pref_store->IsInitializationComplete());
Dominic Battrec7911fe2020-11-26 21:13:18313 EXPECT_GT(pref_store->get_writer().previous_data_size(), 0u);
altimofeev@chromium.org845b43a82011-05-11 10:14:43314
315 // The JSON file looks like this:
316 // {
317 // "homepage": "http://www.cnn.com",
318 // "some_directory": "/usr/local/",
319 // "tabs": {
320 // "new_windows_in_tabs": true,
321 // "max_tabs": 20
322 // }
323 // }
324
Patrick Nolandc48179742022-11-10 20:55:43325 RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
326 commit_pending_write_mode_, &task_environment_);
altimofeev@chromium.org845b43a82011-05-11 10:14:43327}
328
Francois Doray405dd2d2017-06-09 15:23:33329TEST_P(JsonPrefStoreTest, BasicAsync) {
vabr8023d872016-09-15 08:12:22330 base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
Claudio DeSouzac3e85082023-02-27 16:07:15331 ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
altimofeev@chromium.org845b43a82011-05-11 10:14:43332
333 // Test that the persistent value can be loaded.
Francois Doray405dd2d2017-06-09 15:23:33334 auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file);
altimofeev@chromium.org845b43a82011-05-11 10:14:43335
zelidrag@chromium.org0de615a2012-11-08 04:40:59336 {
337 MockPrefStoreObserver mock_observer;
338 pref_store->AddObserver(&mock_observer);
altimofeev@chromium.org845b43a82011-05-11 10:14:43339
zelidrag@chromium.org0de615a2012-11-08 04:40:59340 MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
341 pref_store->ReadPrefsAsync(mock_error_delegate);
altimofeev@chromium.org845b43a82011-05-11 10:14:43342
zelidrag@chromium.org0de615a2012-11-08 04:40:59343 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
344 EXPECT_CALL(*mock_error_delegate,
345 OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
Gabriel Charettedfa36042019-08-19 17:30:11346 task_environment_.RunUntilIdle();
zelidrag@chromium.org0de615a2012-11-08 04:40:59347 pref_store->RemoveObserver(&mock_observer);
altimofeev@chromium.org845b43a82011-05-11 10:14:43348
gab@chromium.orge33c9512014-05-12 02:24:13349 EXPECT_FALSE(pref_store->ReadOnly());
350 EXPECT_TRUE(pref_store->IsInitializationComplete());
Dominic Battrec7911fe2020-11-26 21:13:18351 EXPECT_GT(pref_store->get_writer().previous_data_size(), 0u);
zelidrag@chromium.org0de615a2012-11-08 04:40:59352 }
altimofeev@chromium.org845b43a82011-05-11 10:14:43353
354 // The JSON file looks like this:
355 // {
356 // "homepage": "http://www.cnn.com",
357 // "some_directory": "/usr/local/",
358 // "tabs": {
359 // "new_windows_in_tabs": true,
360 // "max_tabs": 20
361 // }
362 // }
363
Patrick Nolandc48179742022-11-10 20:55:43364 RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
365 commit_pending_write_mode_, &task_environment_);
altimofeev@chromium.org845b43a82011-05-11 10:14:43366}
367
Francois Doray405dd2d2017-06-09 15:23:33368TEST_P(JsonPrefStoreTest, PreserveEmptyValues) {
vabr8023d872016-09-15 08:12:22369 FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json");
gab@chromium.orgaa3283392013-11-27 01:38:24370
Francois Doray405dd2d2017-06-09 15:23:33371 auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
gab@chromium.orgaa3283392013-11-27 01:38:24372
373 // Set some keys with empty values.
Roland Bocke01119d2022-09-29 01:21:41374 pref_store->SetValue("list", base::Value(base::Value::List()),
raymes76de1af2015-05-06 03:22:21375 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
Roland Bocke01119d2022-09-29 01:21:41376 pref_store->SetValue("dict", base::Value(base::Value::Dict()),
raymes76de1af2015-05-06 03:22:21377 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
gab@chromium.orgaa3283392013-11-27 01:38:24378
379 // Write to file.
Patrick Nolandc48179742022-11-10 20:55:43380 CommitPendingWrite(pref_store.get(), commit_pending_write_mode_,
381 &task_environment_);
gab@chromium.orgaa3283392013-11-27 01:38:24382
383 // Reload.
Francois Doray405dd2d2017-06-09 15:23:33384 pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
gab@chromium.orgaa3283392013-11-27 01:38:24385 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
386 ASSERT_FALSE(pref_store->ReadOnly());
387
388 // Check values.
Ivan Kotenkov75b1c3a2017-10-24 14:47:24389 const Value* result = nullptr;
gab@chromium.orgaa3283392013-11-27 01:38:24390 EXPECT_TRUE(pref_store->GetValue("list", &result));
Matt Menke7b290b852023-01-04 12:51:51391 EXPECT_EQ(Value::List(), *result);
gab@chromium.orgaa3283392013-11-27 01:38:24392 EXPECT_TRUE(pref_store->GetValue("dict", &result));
Matt Menke7b290b852023-01-04 12:51:51393 EXPECT_EQ(Value::Dict(), *result);
gab@chromium.orgaa3283392013-11-27 01:38:24394}
395
dgrogan@chromium.orgeeedaa692014-01-30 09:22:27396// This test is just documenting some potentially non-obvious behavior. It
397// shouldn't be taken as normative.
Gabriel Charette788eaf62018-08-07 20:11:46398TEST_P(JsonPrefStoreTest, RemoveClearsEmptyParent) {
vabr8023d872016-09-15 08:12:22399 FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json");
dgrogan@chromium.orgeeedaa692014-01-30 09:22:27400
Francois Doray405dd2d2017-06-09 15:23:33401 auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
dgrogan@chromium.orgeeedaa692014-01-30 09:22:27402
Roland Bocke01119d2022-09-29 01:21:41403 base::Value::Dict dict;
404 dict.Set("key", "value");
405 pref_store->SetValue("dict", base::Value(std::move(dict)),
raymes76de1af2015-05-06 03:22:21406 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
dgrogan@chromium.orgeeedaa692014-01-30 09:22:27407
raymes76de1af2015-05-06 03:22:21408 pref_store->RemoveValue("dict.key",
409 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
dgrogan@chromium.orgeeedaa692014-01-30 09:22:27410
Ivan Kotenkov75b1c3a2017-10-24 14:47:24411 const base::Value* retrieved_dict = nullptr;
dgrogan@chromium.orgeeedaa692014-01-30 09:22:27412 bool has_dict = pref_store->GetValue("dict", &retrieved_dict);
413 EXPECT_FALSE(has_dict);
414}
415
altimofeev@chromium.org845b43a82011-05-11 10:14:43416// Tests asynchronous reading of the file when there is no file.
Gabriel Charette788eaf62018-08-07 20:11:46417TEST_P(JsonPrefStoreTest, AsyncNonExistingFile) {
vabr8023d872016-09-15 08:12:22418 base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
brettw@chromium.org7567484142013-07-11 17:36:07419 ASSERT_FALSE(PathExists(bogus_input_file));
Francois Doray405dd2d2017-06-09 15:23:33420 auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
altimofeev@chromium.org845b43a82011-05-11 10:14:43421 MockPrefStoreObserver mock_observer;
422 pref_store->AddObserver(&mock_observer);
423
424 MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate;
425 pref_store->ReadPrefsAsync(mock_error_delegate);
426
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);
Gabriel Charettedfa36042019-08-19 17:30:11430 task_environment_.RunUntilIdle();
altimofeev@chromium.org845b43a82011-05-11 10:14:43431 pref_store->RemoveObserver(&mock_observer);
432
433 EXPECT_FALSE(pref_store->ReadOnly());
434}
xiyuan@chromium.orgea3e4972012-04-12 03:41:37435
Francois Doray405dd2d2017-06-09 15:23:33436TEST_P(JsonPrefStoreTest, ReadWithInterceptor) {
vabr8023d872016-09-15 08:12:22437 base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
Claudio DeSouzac3e85082023-02-27 16:07:15438 ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
gab@chromium.orge33c9512014-05-12 02:24:13439
dcheng5f043bc2016-04-22 19:09:06440 std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
gab@chromium.orge33c9512014-05-12 02:24:13441 new InterceptingPrefFilter());
442 InterceptingPrefFilter* raw_intercepting_pref_filter_ =
443 intercepting_pref_filter.get();
Francois Doray405dd2d2017-06-09 15:23:33444 auto pref_store = base::MakeRefCounted<JsonPrefStore>(
Gabriel Charette1f53ca32018-08-07 21:31:17445 input_file, std::move(intercepting_pref_filter));
gab@chromium.orge33c9512014-05-12 02:24:13446
447 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE,
448 pref_store->ReadPrefs());
449 EXPECT_FALSE(pref_store->ReadOnly());
450
451 // The store shouldn't be considered initialized until the interceptor
452 // returns.
453 EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
454 EXPECT_FALSE(pref_store->IsInitializationComplete());
Ivan Kotenkov75b1c3a2017-10-24 14:47:24455 EXPECT_FALSE(pref_store->GetValue(kHomePage, nullptr));
gab@chromium.orge33c9512014-05-12 02:24:13456
457 raw_intercepting_pref_filter_->ReleasePrefs();
458
459 EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
460 EXPECT_TRUE(pref_store->IsInitializationComplete());
Ivan Kotenkov75b1c3a2017-10-24 14:47:24461 EXPECT_TRUE(pref_store->GetValue(kHomePage, nullptr));
gab@chromium.orge33c9512014-05-12 02:24:13462
463 // The JSON file looks like this:
464 // {
465 // "homepage": "http://www.cnn.com",
466 // "some_directory": "/usr/local/",
467 // "tabs": {
468 // "new_windows_in_tabs": true,
469 // "max_tabs": 20
470 // }
471 // }
472
Patrick Nolandc48179742022-11-10 20:55:43473 RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
474 commit_pending_write_mode_, &task_environment_);
gab@chromium.orge33c9512014-05-12 02:24:13475}
476
Francois Doray405dd2d2017-06-09 15:23:33477TEST_P(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
vabr8023d872016-09-15 08:12:22478 base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
Claudio DeSouzac3e85082023-02-27 16:07:15479 ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
gab@chromium.orge33c9512014-05-12 02:24:13480
dcheng5f043bc2016-04-22 19:09:06481 std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
gab@chromium.orge33c9512014-05-12 02:24:13482 new InterceptingPrefFilter());
483 InterceptingPrefFilter* raw_intercepting_pref_filter_ =
484 intercepting_pref_filter.get();
Francois Doray405dd2d2017-06-09 15:23:33485 auto pref_store = base::MakeRefCounted<JsonPrefStore>(
Gabriel Charette1f53ca32018-08-07 21:31:17486 input_file, std::move(intercepting_pref_filter));
gab@chromium.orge33c9512014-05-12 02:24:13487
488 MockPrefStoreObserver mock_observer;
489 pref_store->AddObserver(&mock_observer);
490
491 // Ownership of the |mock_error_delegate| is handed to the |pref_store| below.
492 MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
493
494 {
495 pref_store->ReadPrefsAsync(mock_error_delegate);
496
497 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0);
498 // EXPECT_CALL(*mock_error_delegate,
499 // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
Gabriel Charettedfa36042019-08-19 17:30:11500 task_environment_.RunUntilIdle();
gab@chromium.orge33c9512014-05-12 02:24:13501
502 EXPECT_FALSE(pref_store->ReadOnly());
503 EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
504 EXPECT_FALSE(pref_store->IsInitializationComplete());
Ivan Kotenkov75b1c3a2017-10-24 14:47:24505 EXPECT_FALSE(pref_store->GetValue(kHomePage, nullptr));
gab@chromium.orge33c9512014-05-12 02:24:13506 }
507
508 {
509 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
510 // EXPECT_CALL(*mock_error_delegate,
511 // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
512
513 raw_intercepting_pref_filter_->ReleasePrefs();
514
515 EXPECT_FALSE(pref_store->ReadOnly());
516 EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
517 EXPECT_TRUE(pref_store->IsInitializationComplete());
Ivan Kotenkov75b1c3a2017-10-24 14:47:24518 EXPECT_TRUE(pref_store->GetValue(kHomePage, nullptr));
gab@chromium.orge33c9512014-05-12 02:24:13519 }
520
521 pref_store->RemoveObserver(&mock_observer);
522
523 // The JSON file looks like this:
524 // {
525 // "homepage": "http://www.cnn.com",
526 // "some_directory": "/usr/local/",
527 // "tabs": {
528 // "new_windows_in_tabs": true,
529 // "max_tabs": 20
530 // }
531 // }
532
Patrick Nolandc48179742022-11-10 20:55:43533 RunBasicJsonPrefStoreTest(pref_store.get(), input_file,
534 commit_pending_write_mode_, &task_environment_);
gab@chromium.orge33c9512014-05-12 02:24:13535}
536
Anatoliy Potapchuk5d9d8de2020-04-21 02:09:26537TEST_P(JsonPrefStoreTest, RemoveValuesByPrefix) {
538 FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty.json");
539
540 auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
541
542 const Value* value;
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";
547
Roland Bocke01119d2022-09-29 01:21:41548 pref_store->SetValue(subpref_name1, base::Value(42),
Anatoliy Potapchuk5d9d8de2020-04-21 02:09:26549 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
Roland Bocke01119d2022-09-29 01:21:41550 pref_store->SetValue(subpref_name2, base::Value(42),
Anatoliy Potapchuk5d9d8de2020-04-21 02:09:26551 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
Roland Bocke01119d2022-09-29 01:21:41552 pref_store->SetValue(other_name, base::Value(42),
Anatoliy Potapchuk5d9d8de2020-04-21 02:09:26553 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
554
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));
559}
560
Victor Costan1559fac2019-02-13 13:29:50561INSTANTIATE_TEST_SUITE_P(
Patrick Nolandc48179742022-11-10 20:55:43562 JsonPrefStoreTestVariations,
Francois Doray405dd2d2017-06-09 15:23:33563 JsonPrefStoreTest,
Marc Treib85b56d542023-06-12 10:15:49564 ::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK,
565 CommitPendingWriteMode::WITH_CALLBACK,
566 CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
Francois Doray405dd2d2017-06-09 15:23:33567
raymes4b6e14e2015-05-12 00:10:30568class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
probergefc46ac12016-09-21 18:03:00569 public:
570 JsonPrefStoreLossyWriteTest() = default;
571
Peter Boström9f667c382021-10-01 20:09:31572 JsonPrefStoreLossyWriteTest(const JsonPrefStoreLossyWriteTest&) = delete;
573 JsonPrefStoreLossyWriteTest& operator=(const JsonPrefStoreLossyWriteTest&) =
574 delete;
575
raymes4b6e14e2015-05-12 00:10:30576 protected:
577 void SetUp() override {
578 JsonPrefStoreTest::SetUp();
vabr8023d872016-09-15 08:12:22579 test_file_ = temp_dir_.GetPath().AppendASCII("test.json");
raymes4b6e14e2015-05-12 00:10:30580 }
581
raymes4b6e14e2015-05-12 00:10:30582 scoped_refptr<JsonPrefStore> CreatePrefStore() {
Francois Doray405dd2d2017-06-09 15:23:33583 return base::MakeRefCounted<JsonPrefStore>(test_file_);
raymes4b6e14e2015-05-12 00:10:30584 }
585
586 // Return the ImportantFileWriter for a given JsonPrefStore.
probergefc46ac12016-09-21 18:03:00587 ImportantFileWriter* GetImportantFileWriter(JsonPrefStore* pref_store) {
raymes4b6e14e2015-05-12 00:10:30588 return &(pref_store->writer_);
589 }
590
591 // Get the contents of kTestFile. Pumps the message loop before returning the
592 // result.
593 std::string GetTestFileContents() {
Gabriel Charettedfa36042019-08-19 17:30:11594 task_environment_.RunUntilIdle();
raymes4b6e14e2015-05-12 00:10:30595 std::string file_contents;
596 ReadFileToString(test_file_, &file_contents);
597 return file_contents;
598 }
599
600 private:
601 base::FilePath test_file_;
602};
603
Gabriel Charette788eaf62018-08-07 20:11:46604TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteBasic) {
raymes4b6e14e2015-05-12 00:10:30605 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
probergefc46ac12016-09-21 18:03:00606 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
raymes4b6e14e2015-05-12 00:10:30607
608 // Set a normal pref and check that it gets scheduled to be written.
609 ASSERT_FALSE(file_writer->HasPendingWrite());
Roland Bocke01119d2022-09-29 01:21:41610 pref_store->SetValue("normal", base::Value("normal"),
raymes4b6e14e2015-05-12 00:10:30611 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());
616
617 // Set a lossy pref and check that it is not scheduled to be written.
618 // SetValue/RemoveValue.
Roland Bocke01119d2022-09-29 01:21:41619 pref_store->SetValue("lossy", base::Value("lossy"),
raymes4b6e14e2015-05-12 00:10:30620 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());
624
625 // SetValueSilently/RemoveValueSilently.
Roland Bocke01119d2022-09-29 01:21:41626 pref_store->SetValueSilently("lossy", base::Value("lossy"),
raymes4b6e14e2015-05-12 00:10:30627 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());
632
633 // ReportValueChanged.
Roland Bocke01119d2022-09-29 01:21:41634 pref_store->SetValue("lossy", base::Value("lossy"),
raymes4b6e14e2015-05-12 00:10:30635 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());
640
641 // Call CommitPendingWrite and check that the lossy pref and the normal pref
642 // are there with the last values set above.
Francois Doray405dd2d2017-06-09 15:23:33643 pref_store->CommitPendingWrite(base::OnceClosure());
raymes4b6e14e2015-05-12 00:10:30644 ASSERT_FALSE(file_writer->HasPendingWrite());
645 ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
646 GetTestFileContents());
647}
648
Gabriel Charette788eaf62018-08-07 20:11:46649TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) {
raymes4b6e14e2015-05-12 00:10:30650 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
probergefc46ac12016-09-21 18:03:00651 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
raymes4b6e14e2015-05-12 00:10:30652
653 // Set a lossy pref and check that it is not scheduled to be written.
654 ASSERT_FALSE(file_writer->HasPendingWrite());
Roland Bocke01119d2022-09-29 01:21:41655 pref_store->SetValue("lossy", base::Value("lossy"),
raymes4b6e14e2015-05-12 00:10:30656 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
657 ASSERT_FALSE(file_writer->HasPendingWrite());
658
659 // Set a normal pref and check that it is scheduled to be written.
Roland Bocke01119d2022-09-29 01:21:41660 pref_store->SetValue("normal", base::Value("normal"),
raymes4b6e14e2015-05-12 00:10:30661 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
662 ASSERT_TRUE(file_writer->HasPendingWrite());
663
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());
669}
670
Gabriel Charette788eaf62018-08-07 20:11:46671TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) {
raymes4b6e14e2015-05-12 00:10:30672 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
probergefc46ac12016-09-21 18:03:00673 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
raymes4b6e14e2015-05-12 00:10:30674
675 // Set a normal pref and check that it is scheduled to be written.
676 ASSERT_FALSE(file_writer->HasPendingWrite());
Roland Bocke01119d2022-09-29 01:21:41677 pref_store->SetValue("normal", base::Value("normal"),
raymes4b6e14e2015-05-12 00:10:30678 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
679 ASSERT_TRUE(file_writer->HasPendingWrite());
680
681 // Set a lossy pref and check that the write is still scheduled.
Roland Bocke01119d2022-09-29 01:21:41682 pref_store->SetValue("lossy", base::Value("lossy"),
raymes4b6e14e2015-05-12 00:10:30683 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
684 ASSERT_TRUE(file_writer->HasPendingWrite());
685
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());
691}
692
Gabriel Charette788eaf62018-08-07 20:11:46693TEST_P(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) {
benwells26730592015-05-28 13:08:08694 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
probergefc46ac12016-09-21 18:03:00695 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
benwells26730592015-05-28 13:08:08696
697 // Set a lossy pref and check that it is not scheduled to be written.
Roland Bocke01119d2022-09-29 01:21:41698 pref_store->SetValue("lossy", base::Value("lossy"),
benwells26730592015-05-28 13:08:08699 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
700 ASSERT_FALSE(file_writer->HasPendingWrite());
701
702 // Schedule pending lossy writes and check that it is scheduled.
703 pref_store->SchedulePendingLossyWrites();
704 ASSERT_TRUE(file_writer->HasPendingWrite());
705
706 // Call CommitPendingWrite and check that the lossy pref is there with the
707 // last value set above.
Francois Doray405dd2d2017-06-09 15:23:33708 pref_store->CommitPendingWrite(base::OnceClosure());
benwells26730592015-05-28 13:08:08709 ASSERT_FALSE(file_writer->HasPendingWrite());
710 ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents());
711}
712
Victor Costan1559fac2019-02-13 13:29:50713INSTANTIATE_TEST_SUITE_P(
Patrick Nolandc48179742022-11-10 20:55:43714 JsonPrefStoreLossyWriteTestVariations,
Gabriel Charette788eaf62018-08-07 20:11:46715 JsonPrefStoreLossyWriteTest,
Marc Treib85b56d542023-06-12 10:15:49716 ::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK,
717 CommitPendingWriteMode::WITH_CALLBACK,
718 CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
Gabriel Charette788eaf62018-08-07 20:11:46719
probergefc46ac12016-09-21 18:03:00720class SuccessfulWriteReplyObserver {
721 public:
722 SuccessfulWriteReplyObserver() = default;
723
Peter Boström9f667c382021-10-01 20:09:31724 SuccessfulWriteReplyObserver(const SuccessfulWriteReplyObserver&) = delete;
725 SuccessfulWriteReplyObserver& operator=(const SuccessfulWriteReplyObserver&) =
726 delete;
727
probergefc46ac12016-09-21 18:03:00728 // 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;
734 }
735
736 // Register OnWrite() to be called on the next write of |json_pref_store|.
737 void ObserveNextWriteCallback(JsonPrefStore* json_pref_store);
738
739 void OnSuccessfulWrite() {
740 EXPECT_FALSE(successful_write_reply_observed_);
741 successful_write_reply_observed_ = true;
742 }
743
744 private:
745 bool successful_write_reply_observed_ = false;
probergefc46ac12016-09-21 18:03:00746};
747
748void SuccessfulWriteReplyObserver::ObserveNextWriteCallback(
749 JsonPrefStore* json_pref_store) {
750 json_pref_store->RegisterOnNextSuccessfulWriteReply(
Sylvain Defresne073318e22019-10-14 11:06:02751 base::BindOnce(&SuccessfulWriteReplyObserver::OnSuccessfulWrite,
752 base::Unretained(this)));
probergefc46ac12016-09-21 18:03:00753}
754
755enum WriteCallbackObservationState {
756 NOT_CALLED,
757 CALLED_WITH_ERROR,
758 CALLED_WITH_SUCCESS,
759};
760
probergec503d692016-09-28 19:51:05761class WriteCallbacksObserver {
probergefc46ac12016-09-21 18:03:00762 public:
probergec503d692016-09-28 19:51:05763 WriteCallbacksObserver() = default;
probergefc46ac12016-09-21 18:03:00764
Peter Boström9f667c382021-10-01 20:09:31765 WriteCallbacksObserver(const WriteCallbacksObserver&) = delete;
766 WriteCallbacksObserver& operator=(const WriteCallbacksObserver&) = delete;
767
probergefc46ac12016-09-21 18:03:00768 // Register OnWrite() to be called on the next write of |json_pref_store|.
769 void ObserveNextWriteCallback(JsonPrefStore* json_pref_store);
770
probergec503d692016-09-28 19:51:05771 // Returns whether OnPreWrite() was called, and resets the observation state
772 // to false.
773 bool GetAndResetPreWriteObservationState();
probergefc46ac12016-09-21 18:03:00774
probergec503d692016-09-28 19:51:05775 // Returns the |WriteCallbackObservationState| which was observed, then resets
776 // it to |NOT_CALLED|.
777 WriteCallbackObservationState GetAndResetPostWriteObservationState();
778
779 JsonPrefStore::OnWriteCallbackPair GetCallbackPair() {
Sylvain Defresne073318e22019-10-14 11:06:02780 return std::make_pair(base::BindOnce(&WriteCallbacksObserver::OnPreWrite,
781 base::Unretained(this)),
782 base::BindOnce(&WriteCallbacksObserver::OnPostWrite,
783 base::Unretained(this)));
probergec503d692016-09-28 19:51:05784 }
785
786 void OnPreWrite() {
787 EXPECT_FALSE(pre_write_called_);
788 pre_write_called_ = true;
789 }
790
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;
probergefc46ac12016-09-21 18:03:00795 }
796
797 private:
probergec503d692016-09-28 19:51:05798 bool pre_write_called_ = false;
799 WriteCallbackObservationState post_write_observation_state_ = NOT_CALLED;
probergefc46ac12016-09-21 18:03:00800};
801
probergec503d692016-09-28 19:51:05802void WriteCallbacksObserver::ObserveNextWriteCallback(JsonPrefStore* writer) {
803 writer->RegisterOnNextWriteSynchronousCallbacks(GetCallbackPair());
804}
805
806bool WriteCallbacksObserver::GetAndResetPreWriteObservationState() {
807 bool observation_state = pre_write_called_;
808 pre_write_called_ = false;
809 return observation_state;
probergefc46ac12016-09-21 18:03:00810}
811
812WriteCallbackObservationState
probergec503d692016-09-28 19:51:05813WriteCallbacksObserver::GetAndResetPostWriteObservationState() {
814 WriteCallbackObservationState state = post_write_observation_state_;
815 pre_write_called_ = false;
816 post_write_observation_state_ = NOT_CALLED;
probergefc46ac12016-09-21 18:03:00817 return state;
818}
819
Gabriel Charette788eaf62018-08-07 20:11:46820class JsonPrefStoreCallbackTest : public testing::Test {
probergefc46ac12016-09-21 18:03:00821 public:
822 JsonPrefStoreCallbackTest() = default;
823
Peter Boström9f667c382021-10-01 20:09:31824 JsonPrefStoreCallbackTest(const JsonPrefStoreCallbackTest&) = delete;
825 JsonPrefStoreCallbackTest& operator=(const JsonPrefStoreCallbackTest&) =
826 delete;
827
probergefc46ac12016-09-21 18:03:00828 protected:
829 void SetUp() override {
Gabriel Charette788eaf62018-08-07 20:11:46830 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
vabr5d919d62016-09-30 08:55:36831 test_file_ = temp_dir_.GetPath().AppendASCII("test.json");
probergefc46ac12016-09-21 18:03:00832 }
833
834 scoped_refptr<JsonPrefStore> CreatePrefStore() {
Francois Doray405dd2d2017-06-09 15:23:33835 return base::MakeRefCounted<JsonPrefStore>(test_file_);
probergefc46ac12016-09-21 18:03:00836 }
837
838 // Return the ImportantFileWriter for a given JsonPrefStore.
839 ImportantFileWriter* GetImportantFileWriter(JsonPrefStore* pref_store) {
840 return &(pref_store->writer_);
841 }
842
843 void TriggerFakeWriteForCallback(JsonPrefStore* pref_store, bool success) {
844 JsonPrefStore::PostWriteCallback(
Sylvain Defresne073318e22019-10-14 11:06:02845 base::BindOnce(&JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback,
846 pref_store->AsWeakPtr()),
847 base::BindOnce(&WriteCallbacksObserver::OnPostWrite,
848 base::Unretained(&write_callback_observer_)),
Sean Maher52fa5a72022-11-14 15:53:25849 base::SequencedTaskRunner::GetCurrentDefault(), success);
probergefc46ac12016-09-21 18:03:00850 }
851
852 SuccessfulWriteReplyObserver successful_write_reply_observer_;
probergec503d692016-09-28 19:51:05853 WriteCallbacksObserver write_callback_observer_;
probergefc46ac12016-09-21 18:03:00854
Gabriel Charette788eaf62018-08-07 20:11:46855 protected:
Gabriel Charettedfa36042019-08-19 17:30:11856 base::test::TaskEnvironment task_environment_{
857 base::test::TaskEnvironment::MainThreadType::DEFAULT,
858 base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED};
Gabriel Charette788eaf62018-08-07 20:11:46859
860 base::ScopedTempDir temp_dir_;
861
probergefc46ac12016-09-21 18:03:00862 private:
863 base::FilePath test_file_;
probergefc46ac12016-09-21 18:03:00864};
865
Francois Doray5f547952017-07-25 21:02:05866TEST_F(JsonPrefStoreCallbackTest, TestSerializeDataCallbacks) {
probergec503d692016-09-28 19:51:05867 base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
Claudio DeSouzac3e85082023-02-27 16:07:15868 ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
probergec503d692016-09-28 19:51:05869
870 std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
871 new InterceptingPrefFilter(write_callback_observer_.GetCallbackPair()));
Francois Doray405dd2d2017-06-09 15:23:33872 auto pref_store = base::MakeRefCounted<JsonPrefStore>(
Gabriel Charette1f53ca32018-08-07 21:31:17873 input_file, std::move(intercepting_pref_filter));
probergec503d692016-09-28 19:51:05874 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
875
876 EXPECT_EQ(NOT_CALLED,
877 write_callback_observer_.GetAndResetPostWriteObservationState());
Roland Bocke01119d2022-09-29 01:21:41878 pref_store->SetValue("normal", base::Value("normal"),
probergec503d692016-09-28 19:51:05879 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
880 file_writer->DoScheduledWrite();
881
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());
886
Gabriel Charettedfa36042019-08-19 17:30:11887 task_environment_.RunUntilIdle();
probergec503d692016-09-28 19:51:05888
889 EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
890 EXPECT_EQ(CALLED_WITH_SUCCESS,
891 write_callback_observer_.GetAndResetPostWriteObservationState());
892}
893
894TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacks) {
probergefc46ac12016-09-21 18:03:00895 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
896 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
897
probergec503d692016-09-28 19:51:05898 // Test RegisterOnNextWriteSynchronousCallbacks after
probergefc46ac12016-09-21 18:03:00899 // RegisterOnNextSuccessfulWriteReply.
900 successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
901 write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
Claudio DeSouzaf3385c732023-03-13 12:36:23902 file_writer->WriteNow("foo");
Gabriel Charettedfa36042019-08-19 17:30:11903 task_environment_.RunUntilIdle();
probergefc46ac12016-09-21 18:03:00904 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
probergec503d692016-09-28 19:51:05905 EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
906 EXPECT_EQ(CALLED_WITH_SUCCESS,
907 write_callback_observer_.GetAndResetPostWriteObservationState());
probergefc46ac12016-09-21 18:03:00908
909 // Test RegisterOnNextSuccessfulWriteReply after
probergec503d692016-09-28 19:51:05910 // RegisterOnNextWriteSynchronousCallbacks.
probergefc46ac12016-09-21 18:03:00911 successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
912 write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
Claudio DeSouzaf3385c732023-03-13 12:36:23913 file_writer->WriteNow("foo");
Gabriel Charettedfa36042019-08-19 17:30:11914 task_environment_.RunUntilIdle();
probergefc46ac12016-09-21 18:03:00915 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
probergec503d692016-09-28 19:51:05916 EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
917 EXPECT_EQ(CALLED_WITH_SUCCESS,
918 write_callback_observer_.GetAndResetPostWriteObservationState());
probergefc46ac12016-09-21 18:03:00919
920 // Test RegisterOnNextSuccessfulWriteReply only.
921 successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
Claudio DeSouzaf3385c732023-03-13 12:36:23922 file_writer->WriteNow("foo");
Gabriel Charettedfa36042019-08-19 17:30:11923 task_environment_.RunUntilIdle();
probergefc46ac12016-09-21 18:03:00924 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
probergec503d692016-09-28 19:51:05925 EXPECT_FALSE(write_callback_observer_.GetAndResetPreWriteObservationState());
926 EXPECT_EQ(NOT_CALLED,
927 write_callback_observer_.GetAndResetPostWriteObservationState());
probergefc46ac12016-09-21 18:03:00928
probergec503d692016-09-28 19:51:05929 // Test RegisterOnNextWriteSynchronousCallbacks only.
probergefc46ac12016-09-21 18:03:00930 write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
Claudio DeSouzaf3385c732023-03-13 12:36:23931 file_writer->WriteNow("foo");
Gabriel Charettedfa36042019-08-19 17:30:11932 task_environment_.RunUntilIdle();
probergefc46ac12016-09-21 18:03:00933 EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
probergec503d692016-09-28 19:51:05934 EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
935 EXPECT_EQ(CALLED_WITH_SUCCESS,
936 write_callback_observer_.GetAndResetPostWriteObservationState());
probergefc46ac12016-09-21 18:03:00937}
938
probergec503d692016-09-28 19:51:05939TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksWithFakeFailure) {
probergefc46ac12016-09-21 18:03:00940 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
941
942 // Confirm that the observers are invoked.
943 successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
944 TriggerFakeWriteForCallback(pref_store.get(), true);
Gabriel Charettedfa36042019-08-19 17:30:11945 task_environment_.RunUntilIdle();
probergefc46ac12016-09-21 18:03:00946 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
947 EXPECT_EQ(CALLED_WITH_SUCCESS,
probergec503d692016-09-28 19:51:05948 write_callback_observer_.GetAndResetPostWriteObservationState());
probergefc46ac12016-09-21 18:03:00949
950 // Confirm that the observation states were reset.
951 EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
probergec503d692016-09-28 19:51:05952 EXPECT_EQ(NOT_CALLED,
953 write_callback_observer_.GetAndResetPostWriteObservationState());
probergefc46ac12016-09-21 18:03:00954
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);
Gabriel Charettedfa36042019-08-19 17:30:11958 task_environment_.RunUntilIdle();
probergefc46ac12016-09-21 18:03:00959 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
960 EXPECT_EQ(CALLED_WITH_SUCCESS,
probergec503d692016-09-28 19:51:05961 write_callback_observer_.GetAndResetPostWriteObservationState());
probergefc46ac12016-09-21 18:03:00962
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);
Gabriel Charettedfa36042019-08-19 17:30:11967 task_environment_.RunUntilIdle();
probergefc46ac12016-09-21 18:03:00968 EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
969 EXPECT_EQ(CALLED_WITH_ERROR,
probergec503d692016-09-28 19:51:05970 write_callback_observer_.GetAndResetPostWriteObservationState());
probergefc46ac12016-09-21 18:03:00971
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());
Claudio DeSouzaf3385c732023-03-13 12:36:23975 file_writer->WriteNow("foo");
Gabriel Charettedfa36042019-08-19 17:30:11976 task_environment_.RunUntilIdle();
probergefc46ac12016-09-21 18:03:00977 EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
probergec503d692016-09-28 19:51:05978 EXPECT_EQ(NOT_CALLED,
979 write_callback_observer_.GetAndResetPostWriteObservationState());
probergefc46ac12016-09-21 18:03:00980}
981
probergec503d692016-09-28 19:51:05982TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksDuringProfileDeath) {
probergefc46ac12016-09-21 18:03:00983 // 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.
985 {
986 scoped_refptr<JsonPrefStore> soon_out_of_scope_pref_store =
987 CreatePrefStore();
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());
Claudio DeSouzaf3385c732023-03-13 12:36:23994 file_writer->WriteNow("foo");
probergefc46ac12016-09-21 18:03:00995 }
Gabriel Charettedfa36042019-08-19 17:30:11996 task_environment_.RunUntilIdle();
probergefc46ac12016-09-21 18:03:00997 EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
probergec503d692016-09-28 19:51:05998 EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
probergefc46ac12016-09-21 18:03:00999 EXPECT_EQ(CALLED_WITH_SUCCESS,
probergec503d692016-09-28 19:51:051000 write_callback_observer_.GetAndResetPostWriteObservationState());
probergefc46ac12016-09-21 18:03:001001}
1002
vabr5d919d62016-09-30 08:55:361003} // namespace base