Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / base / prefs / json_pref_store_unittest.cc
blob5195a18c436d8221e80f3acc7d4f8e8d7a30ff85
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/prefs/json_pref_store.h"
7 #include "base/bind.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/location.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram_samples.h"
15 #include "base/metrics/statistics_recorder.h"
16 #include "base/path_service.h"
17 #include "base/prefs/pref_filter.h"
18 #include "base/run_loop.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/test/simple_test_clock.h"
24 #include "base/threading/sequenced_worker_pool.h"
25 #include "base/threading/thread.h"
26 #include "base/values.h"
27 #include "testing/gmock/include/gmock/gmock.h"
28 #include "testing/gtest/include/gtest/gtest.h"
30 namespace base {
31 namespace {
33 const char kHomePage[] = "homepage";
35 // Set the time on the given SimpleTestClock to the given time in minutes.
36 void SetCurrentTimeInMinutes(double minutes, base::SimpleTestClock* clock) {
37 const int32_t kBaseTimeMins = 100;
38 clock->SetNow(base::Time::FromDoubleT((kBaseTimeMins + minutes) * 60));
41 // A PrefFilter that will intercept all calls to FilterOnLoad() and hold on
42 // to the |prefs| until explicitly asked to release them.
43 class InterceptingPrefFilter : public PrefFilter {
44 public:
45 InterceptingPrefFilter();
46 ~InterceptingPrefFilter() override;
48 // PrefFilter implementation:
49 void FilterOnLoad(
50 const PostFilterOnLoadCallback& post_filter_on_load_callback,
51 scoped_ptr<base::DictionaryValue> pref_store_contents) override;
52 void FilterUpdate(const std::string& path) override {}
53 void FilterSerializeData(
54 base::DictionaryValue* pref_store_contents) override {}
56 bool has_intercepted_prefs() const { return intercepted_prefs_ != NULL; }
58 // Finalize an intercepted read, handing |intercepted_prefs_| back to its
59 // JsonPrefStore.
60 void ReleasePrefs();
62 private:
63 PostFilterOnLoadCallback post_filter_on_load_callback_;
64 scoped_ptr<base::DictionaryValue> intercepted_prefs_;
66 DISALLOW_COPY_AND_ASSIGN(InterceptingPrefFilter);
69 InterceptingPrefFilter::InterceptingPrefFilter() {}
70 InterceptingPrefFilter::~InterceptingPrefFilter() {}
72 void InterceptingPrefFilter::FilterOnLoad(
73 const PostFilterOnLoadCallback& post_filter_on_load_callback,
74 scoped_ptr<base::DictionaryValue> pref_store_contents) {
75 post_filter_on_load_callback_ = post_filter_on_load_callback;
76 intercepted_prefs_ = pref_store_contents.Pass();
79 void InterceptingPrefFilter::ReleasePrefs() {
80 EXPECT_FALSE(post_filter_on_load_callback_.is_null());
81 post_filter_on_load_callback_.Run(intercepted_prefs_.Pass(), false);
82 post_filter_on_load_callback_.Reset();
85 class MockPrefStoreObserver : public PrefStore::Observer {
86 public:
87 MOCK_METHOD1(OnPrefValueChanged, void (const std::string&));
88 MOCK_METHOD1(OnInitializationCompleted, void (bool));
91 class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
92 public:
93 MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError));
96 } // namespace
98 class JsonPrefStoreTest : public testing::Test {
99 protected:
100 void SetUp() override {
101 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
103 ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir_));
104 data_dir_ = data_dir_.AppendASCII("prefs");
105 ASSERT_TRUE(PathExists(data_dir_));
108 void TearDown() override {
109 // Make sure all pending tasks have been processed (e.g., deleting the
110 // JsonPrefStore may post write tasks).
111 RunLoop().RunUntilIdle();
114 // The path to temporary directory used to contain the test operations.
115 base::ScopedTempDir temp_dir_;
116 // The path to the directory where the test data is stored.
117 base::FilePath data_dir_;
118 // A message loop that we can use as the file thread message loop.
119 MessageLoop message_loop_;
121 private:
122 // Ensure histograms are reset for each test.
123 StatisticsRecorder statistics_recorder_;
126 // Test fallback behavior for a nonexistent file.
127 TEST_F(JsonPrefStoreTest, NonExistentFile) {
128 base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt");
129 ASSERT_FALSE(PathExists(bogus_input_file));
130 scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
131 bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
132 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
133 pref_store->ReadPrefs());
134 EXPECT_FALSE(pref_store->ReadOnly());
137 // Test fallback behavior for a nonexistent file and alternate file.
138 TEST_F(JsonPrefStoreTest, NonExistentFileAndAlternateFile) {
139 base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt");
140 base::FilePath bogus_alternate_input_file =
141 temp_dir_.path().AppendASCII("read_alternate.txt");
142 ASSERT_FALSE(PathExists(bogus_input_file));
143 ASSERT_FALSE(PathExists(bogus_alternate_input_file));
144 scoped_refptr<JsonPrefStore> pref_store =
145 new JsonPrefStore(bogus_input_file, bogus_alternate_input_file,
146 message_loop_.task_runner(), scoped_ptr<PrefFilter>());
147 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
148 pref_store->ReadPrefs());
149 EXPECT_FALSE(pref_store->ReadOnly());
152 // Test fallback behavior for an invalid file.
153 TEST_F(JsonPrefStoreTest, InvalidFile) {
154 base::FilePath invalid_file_original = data_dir_.AppendASCII("invalid.json");
155 base::FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json");
156 ASSERT_TRUE(base::CopyFile(invalid_file_original, invalid_file));
157 scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
158 invalid_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
159 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
160 pref_store->ReadPrefs());
161 EXPECT_FALSE(pref_store->ReadOnly());
163 // The file should have been moved aside.
164 EXPECT_FALSE(PathExists(invalid_file));
165 base::FilePath moved_aside = temp_dir_.path().AppendASCII("invalid.bad");
166 EXPECT_TRUE(PathExists(moved_aside));
167 EXPECT_TRUE(TextContentsEqual(invalid_file_original, moved_aside));
170 // This function is used to avoid code duplication while testing synchronous and
171 // asynchronous version of the JsonPrefStore loading.
172 void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
173 const base::FilePath& output_file,
174 const base::FilePath& golden_output_file) {
175 const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
176 const char kMaxTabs[] = "tabs.max_tabs";
177 const char kLongIntPref[] = "long_int.pref";
179 std::string cnn("http://www.cnn.com");
181 const Value* actual;
182 EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual));
183 std::string string_value;
184 EXPECT_TRUE(actual->GetAsString(&string_value));
185 EXPECT_EQ(cnn, string_value);
187 const char kSomeDirectory[] = "some_directory";
189 EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
190 base::FilePath::StringType path;
191 EXPECT_TRUE(actual->GetAsString(&path));
192 EXPECT_EQ(base::FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path);
193 base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/"));
195 pref_store->SetValue(kSomeDirectory,
196 make_scoped_ptr(new StringValue(some_path.value())),
197 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
198 EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
199 EXPECT_TRUE(actual->GetAsString(&path));
200 EXPECT_EQ(some_path.value(), path);
202 // Test reading some other data types from sub-dictionaries.
203 EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
204 bool boolean = false;
205 EXPECT_TRUE(actual->GetAsBoolean(&boolean));
206 EXPECT_TRUE(boolean);
208 pref_store->SetValue(kNewWindowsInTabs,
209 make_scoped_ptr(new FundamentalValue(false)),
210 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
211 EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
212 EXPECT_TRUE(actual->GetAsBoolean(&boolean));
213 EXPECT_FALSE(boolean);
215 EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
216 int integer = 0;
217 EXPECT_TRUE(actual->GetAsInteger(&integer));
218 EXPECT_EQ(20, integer);
219 pref_store->SetValue(kMaxTabs, make_scoped_ptr(new FundamentalValue(10)),
220 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
221 EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
222 EXPECT_TRUE(actual->GetAsInteger(&integer));
223 EXPECT_EQ(10, integer);
225 pref_store->SetValue(
226 kLongIntPref,
227 make_scoped_ptr(new StringValue(base::Int64ToString(214748364842LL))),
228 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
229 EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual));
230 EXPECT_TRUE(actual->GetAsString(&string_value));
231 int64 value;
232 base::StringToInt64(string_value, &value);
233 EXPECT_EQ(214748364842LL, value);
235 // Serialize and compare to expected output.
236 ASSERT_TRUE(PathExists(golden_output_file));
237 pref_store->CommitPendingWrite();
238 RunLoop().RunUntilIdle();
239 EXPECT_TRUE(TextContentsEqual(golden_output_file, output_file));
240 ASSERT_TRUE(base::DeleteFile(output_file, false));
243 TEST_F(JsonPrefStoreTest, Basic) {
244 ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
245 temp_dir_.path().AppendASCII("write.json")));
247 // Test that the persistent value can be loaded.
248 base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
249 ASSERT_TRUE(PathExists(input_file));
250 scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
251 input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
252 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
253 EXPECT_FALSE(pref_store->ReadOnly());
254 EXPECT_TRUE(pref_store->IsInitializationComplete());
256 // The JSON file looks like this:
257 // {
258 // "homepage": "http://www.cnn.com",
259 // "some_directory": "/usr/local/",
260 // "tabs": {
261 // "new_windows_in_tabs": true,
262 // "max_tabs": 20
263 // }
264 // }
266 RunBasicJsonPrefStoreTest(
267 pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
270 TEST_F(JsonPrefStoreTest, BasicAsync) {
271 ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
272 temp_dir_.path().AppendASCII("write.json")));
274 // Test that the persistent value can be loaded.
275 base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
276 ASSERT_TRUE(PathExists(input_file));
277 scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
278 input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
281 MockPrefStoreObserver mock_observer;
282 pref_store->AddObserver(&mock_observer);
284 MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
285 pref_store->ReadPrefsAsync(mock_error_delegate);
287 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
288 EXPECT_CALL(*mock_error_delegate,
289 OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
290 RunLoop().RunUntilIdle();
291 pref_store->RemoveObserver(&mock_observer);
293 EXPECT_FALSE(pref_store->ReadOnly());
294 EXPECT_TRUE(pref_store->IsInitializationComplete());
297 // The JSON file looks like this:
298 // {
299 // "homepage": "http://www.cnn.com",
300 // "some_directory": "/usr/local/",
301 // "tabs": {
302 // "new_windows_in_tabs": true,
303 // "max_tabs": 20
304 // }
305 // }
307 RunBasicJsonPrefStoreTest(
308 pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
311 TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
312 FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json");
314 scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
315 pref_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
317 // Set some keys with empty values.
318 pref_store->SetValue("list", make_scoped_ptr(new base::ListValue),
319 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
320 pref_store->SetValue("dict", make_scoped_ptr(new base::DictionaryValue),
321 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
323 // Write to file.
324 pref_store->CommitPendingWrite();
325 RunLoop().RunUntilIdle();
327 // Reload.
328 pref_store = new JsonPrefStore(pref_file, message_loop_.task_runner(),
329 scoped_ptr<PrefFilter>());
330 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
331 ASSERT_FALSE(pref_store->ReadOnly());
333 // Check values.
334 const Value* result = NULL;
335 EXPECT_TRUE(pref_store->GetValue("list", &result));
336 EXPECT_TRUE(ListValue().Equals(result));
337 EXPECT_TRUE(pref_store->GetValue("dict", &result));
338 EXPECT_TRUE(DictionaryValue().Equals(result));
341 // This test is just documenting some potentially non-obvious behavior. It
342 // shouldn't be taken as normative.
343 TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) {
344 FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json");
346 scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
347 pref_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
349 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
350 dict->SetString("key", "value");
351 pref_store->SetValue("dict", dict.Pass(),
352 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
354 pref_store->RemoveValue("dict.key",
355 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
357 const base::Value* retrieved_dict = NULL;
358 bool has_dict = pref_store->GetValue("dict", &retrieved_dict);
359 EXPECT_FALSE(has_dict);
362 // Tests asynchronous reading of the file when there is no file.
363 TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
364 base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt");
365 ASSERT_FALSE(PathExists(bogus_input_file));
366 scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
367 bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
368 MockPrefStoreObserver mock_observer;
369 pref_store->AddObserver(&mock_observer);
371 MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate;
372 pref_store->ReadPrefsAsync(mock_error_delegate);
374 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
375 EXPECT_CALL(*mock_error_delegate,
376 OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1);
377 RunLoop().RunUntilIdle();
378 pref_store->RemoveObserver(&mock_observer);
380 EXPECT_FALSE(pref_store->ReadOnly());
383 TEST_F(JsonPrefStoreTest, ReadWithInterceptor) {
384 ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
385 temp_dir_.path().AppendASCII("write.json")));
387 // Test that the persistent value can be loaded.
388 base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
389 ASSERT_TRUE(PathExists(input_file));
391 scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter(
392 new InterceptingPrefFilter());
393 InterceptingPrefFilter* raw_intercepting_pref_filter_ =
394 intercepting_pref_filter.get();
395 scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
396 input_file, message_loop_.task_runner(), intercepting_pref_filter.Pass());
398 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE,
399 pref_store->ReadPrefs());
400 EXPECT_FALSE(pref_store->ReadOnly());
402 // The store shouldn't be considered initialized until the interceptor
403 // returns.
404 EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
405 EXPECT_FALSE(pref_store->IsInitializationComplete());
406 EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL));
408 raw_intercepting_pref_filter_->ReleasePrefs();
410 EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
411 EXPECT_TRUE(pref_store->IsInitializationComplete());
412 EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL));
414 // The JSON file looks like this:
415 // {
416 // "homepage": "http://www.cnn.com",
417 // "some_directory": "/usr/local/",
418 // "tabs": {
419 // "new_windows_in_tabs": true,
420 // "max_tabs": 20
421 // }
422 // }
424 RunBasicJsonPrefStoreTest(
425 pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
428 TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
429 ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
430 temp_dir_.path().AppendASCII("write.json")));
432 // Test that the persistent value can be loaded.
433 base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
434 ASSERT_TRUE(PathExists(input_file));
436 scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter(
437 new InterceptingPrefFilter());
438 InterceptingPrefFilter* raw_intercepting_pref_filter_ =
439 intercepting_pref_filter.get();
440 scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
441 input_file, message_loop_.task_runner(), intercepting_pref_filter.Pass());
443 MockPrefStoreObserver mock_observer;
444 pref_store->AddObserver(&mock_observer);
446 // Ownership of the |mock_error_delegate| is handed to the |pref_store| below.
447 MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
450 pref_store->ReadPrefsAsync(mock_error_delegate);
452 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0);
453 // EXPECT_CALL(*mock_error_delegate,
454 // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
455 RunLoop().RunUntilIdle();
457 EXPECT_FALSE(pref_store->ReadOnly());
458 EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
459 EXPECT_FALSE(pref_store->IsInitializationComplete());
460 EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL));
464 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
465 // EXPECT_CALL(*mock_error_delegate,
466 // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
468 raw_intercepting_pref_filter_->ReleasePrefs();
470 EXPECT_FALSE(pref_store->ReadOnly());
471 EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
472 EXPECT_TRUE(pref_store->IsInitializationComplete());
473 EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL));
476 pref_store->RemoveObserver(&mock_observer);
478 // The JSON file looks like this:
479 // {
480 // "homepage": "http://www.cnn.com",
481 // "some_directory": "/usr/local/",
482 // "tabs": {
483 // "new_windows_in_tabs": true,
484 // "max_tabs": 20
485 // }
486 // }
488 RunBasicJsonPrefStoreTest(
489 pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
492 TEST_F(JsonPrefStoreTest, AlternateFile) {
493 ASSERT_TRUE(
494 base::CopyFile(data_dir_.AppendASCII("read.json"),
495 temp_dir_.path().AppendASCII("alternate.json")));
497 // Test that the alternate file is moved to the main file and read as-is from
498 // there.
499 base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
500 base::FilePath alternate_input_file =
501 temp_dir_.path().AppendASCII("alternate.json");
502 ASSERT_FALSE(PathExists(input_file));
503 ASSERT_TRUE(PathExists(alternate_input_file));
504 scoped_refptr<JsonPrefStore> pref_store =
505 new JsonPrefStore(input_file, alternate_input_file,
506 message_loop_.task_runner(), scoped_ptr<PrefFilter>());
508 ASSERT_FALSE(PathExists(input_file));
509 ASSERT_TRUE(PathExists(alternate_input_file));
510 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
512 ASSERT_TRUE(PathExists(input_file));
513 ASSERT_FALSE(PathExists(alternate_input_file));
515 EXPECT_FALSE(pref_store->ReadOnly());
516 EXPECT_TRUE(pref_store->IsInitializationComplete());
518 // The JSON file looks like this:
519 // {
520 // "homepage": "http://www.cnn.com",
521 // "some_directory": "/usr/local/",
522 // "tabs": {
523 // "new_windows_in_tabs": true,
524 // "max_tabs": 20
525 // }
526 // }
528 RunBasicJsonPrefStoreTest(
529 pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
532 TEST_F(JsonPrefStoreTest, AlternateFileIgnoredWhenMainFileExists) {
533 ASSERT_TRUE(
534 base::CopyFile(data_dir_.AppendASCII("read.json"),
535 temp_dir_.path().AppendASCII("write.json")));
536 ASSERT_TRUE(
537 base::CopyFile(data_dir_.AppendASCII("invalid.json"),
538 temp_dir_.path().AppendASCII("alternate.json")));
540 // Test that the alternate file is ignored and that the read occurs from the
541 // existing main file. There is no attempt at even deleting the alternate
542 // file as this scenario should never happen in normal user-data-dirs.
543 base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
544 base::FilePath alternate_input_file =
545 temp_dir_.path().AppendASCII("alternate.json");
546 ASSERT_TRUE(PathExists(input_file));
547 ASSERT_TRUE(PathExists(alternate_input_file));
548 scoped_refptr<JsonPrefStore> pref_store =
549 new JsonPrefStore(input_file, alternate_input_file,
550 message_loop_.task_runner(), scoped_ptr<PrefFilter>());
552 ASSERT_TRUE(PathExists(input_file));
553 ASSERT_TRUE(PathExists(alternate_input_file));
554 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
556 ASSERT_TRUE(PathExists(input_file));
557 ASSERT_TRUE(PathExists(alternate_input_file));
559 EXPECT_FALSE(pref_store->ReadOnly());
560 EXPECT_TRUE(pref_store->IsInitializationComplete());
562 // The JSON file looks like this:
563 // {
564 // "homepage": "http://www.cnn.com",
565 // "some_directory": "/usr/local/",
566 // "tabs": {
567 // "new_windows_in_tabs": true,
568 // "max_tabs": 20
569 // }
570 // }
572 RunBasicJsonPrefStoreTest(
573 pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
576 TEST_F(JsonPrefStoreTest, AlternateFileDNE) {
577 ASSERT_TRUE(
578 base::CopyFile(data_dir_.AppendASCII("read.json"),
579 temp_dir_.path().AppendASCII("write.json")));
581 // Test that the basic read works fine when an alternate file is specified but
582 // does not exist.
583 base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
584 base::FilePath alternate_input_file =
585 temp_dir_.path().AppendASCII("alternate.json");
586 ASSERT_TRUE(PathExists(input_file));
587 ASSERT_FALSE(PathExists(alternate_input_file));
588 scoped_refptr<JsonPrefStore> pref_store =
589 new JsonPrefStore(input_file, alternate_input_file,
590 message_loop_.task_runner(), scoped_ptr<PrefFilter>());
592 ASSERT_TRUE(PathExists(input_file));
593 ASSERT_FALSE(PathExists(alternate_input_file));
594 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
596 ASSERT_TRUE(PathExists(input_file));
597 ASSERT_FALSE(PathExists(alternate_input_file));
599 EXPECT_FALSE(pref_store->ReadOnly());
600 EXPECT_TRUE(pref_store->IsInitializationComplete());
602 // The JSON file looks like this:
603 // {
604 // "homepage": "http://www.cnn.com",
605 // "some_directory": "/usr/local/",
606 // "tabs": {
607 // "new_windows_in_tabs": true,
608 // "max_tabs": 20
609 // }
610 // }
612 RunBasicJsonPrefStoreTest(
613 pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
616 TEST_F(JsonPrefStoreTest, BasicAsyncWithAlternateFile) {
617 ASSERT_TRUE(
618 base::CopyFile(data_dir_.AppendASCII("read.json"),
619 temp_dir_.path().AppendASCII("alternate.json")));
621 // Test that the alternate file is moved to the main file and read as-is from
622 // there even when the read is made asynchronously.
623 base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
624 base::FilePath alternate_input_file =
625 temp_dir_.path().AppendASCII("alternate.json");
626 ASSERT_FALSE(PathExists(input_file));
627 ASSERT_TRUE(PathExists(alternate_input_file));
628 scoped_refptr<JsonPrefStore> pref_store =
629 new JsonPrefStore(input_file, alternate_input_file,
630 message_loop_.task_runner(), scoped_ptr<PrefFilter>());
632 ASSERT_FALSE(PathExists(input_file));
633 ASSERT_TRUE(PathExists(alternate_input_file));
636 MockPrefStoreObserver mock_observer;
637 pref_store->AddObserver(&mock_observer);
639 MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
640 pref_store->ReadPrefsAsync(mock_error_delegate);
642 EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
643 EXPECT_CALL(*mock_error_delegate,
644 OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
645 RunLoop().RunUntilIdle();
646 pref_store->RemoveObserver(&mock_observer);
648 EXPECT_FALSE(pref_store->ReadOnly());
649 EXPECT_TRUE(pref_store->IsInitializationComplete());
652 ASSERT_TRUE(PathExists(input_file));
653 ASSERT_FALSE(PathExists(alternate_input_file));
655 // The JSON file looks like this:
656 // {
657 // "homepage": "http://www.cnn.com",
658 // "some_directory": "/usr/local/",
659 // "tabs": {
660 // "new_windows_in_tabs": true,
661 // "max_tabs": 20
662 // }
663 // }
665 RunBasicJsonPrefStoreTest(
666 pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
669 TEST_F(JsonPrefStoreTest, WriteCountHistogramTestBasic) {
670 SimpleTestClock* test_clock = new SimpleTestClock;
671 SetCurrentTimeInMinutes(0, test_clock);
672 JsonPrefStore::WriteCountHistogram histogram(
673 base::TimeDelta::FromSeconds(10),
674 base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
675 scoped_ptr<base::Clock>(test_clock));
676 int32 report_interval =
677 JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins;
679 histogram.RecordWriteOccured();
681 SetCurrentTimeInMinutes(1.5 * report_interval, test_clock);
682 histogram.ReportOutstandingWrites();
683 scoped_ptr<HistogramSamples> samples =
684 histogram.GetHistogram()->SnapshotSamples();
685 ASSERT_EQ(1, samples->GetCount(1));
686 ASSERT_EQ(1, samples->TotalCount());
688 ASSERT_EQ("Settings.JsonDataWriteCount.Local_State",
689 histogram.GetHistogram()->histogram_name());
690 ASSERT_TRUE(histogram.GetHistogram()->HasConstructionArguments(1, 30, 31));
693 TEST_F(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod) {
694 SimpleTestClock* test_clock = new SimpleTestClock;
695 SetCurrentTimeInMinutes(0, test_clock);
696 JsonPrefStore::WriteCountHistogram histogram(
697 base::TimeDelta::FromSeconds(10),
698 base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
699 scoped_ptr<base::Clock>(test_clock));
700 int32 report_interval =
701 JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins;
703 histogram.RecordWriteOccured();
704 SetCurrentTimeInMinutes(0.5 * report_interval, test_clock);
705 histogram.RecordWriteOccured();
706 SetCurrentTimeInMinutes(0.7 * report_interval, test_clock);
707 histogram.RecordWriteOccured();
709 // Nothing should be recorded until the report period has elapsed.
710 scoped_ptr<HistogramSamples> samples =
711 histogram.GetHistogram()->SnapshotSamples();
712 ASSERT_EQ(0, samples->TotalCount());
714 SetCurrentTimeInMinutes(1.3 * report_interval, test_clock);
715 histogram.RecordWriteOccured();
717 // Now the report period has elapsed.
718 samples = histogram.GetHistogram()->SnapshotSamples();
719 ASSERT_EQ(1, samples->GetCount(3));
720 ASSERT_EQ(1, samples->TotalCount());
722 // The last write won't be recorded because the second count period hasn't
723 // fully elapsed.
724 SetCurrentTimeInMinutes(1.5 * report_interval, test_clock);
725 histogram.ReportOutstandingWrites();
727 samples = histogram.GetHistogram()->SnapshotSamples();
728 ASSERT_EQ(1, samples->GetCount(3));
729 ASSERT_EQ(1, samples->TotalCount());
732 TEST_F(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods) {
733 SimpleTestClock* test_clock = new SimpleTestClock;
734 SetCurrentTimeInMinutes(0, test_clock);
735 JsonPrefStore::WriteCountHistogram histogram(
736 base::TimeDelta::FromSeconds(10),
737 base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
738 scoped_ptr<base::Clock>(test_clock));
739 int32 report_interval =
740 JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins;
742 histogram.RecordWriteOccured();
743 SetCurrentTimeInMinutes(0.5 * report_interval, test_clock);
744 histogram.RecordWriteOccured();
745 SetCurrentTimeInMinutes(0.7 * report_interval, test_clock);
746 histogram.RecordWriteOccured();
747 SetCurrentTimeInMinutes(1.3 * report_interval, test_clock);
748 histogram.RecordWriteOccured();
749 SetCurrentTimeInMinutes(1.5 * report_interval, test_clock);
750 histogram.RecordWriteOccured();
751 SetCurrentTimeInMinutes(2.1 * report_interval, test_clock);
752 histogram.RecordWriteOccured();
753 SetCurrentTimeInMinutes(2.5 * report_interval, test_clock);
754 histogram.RecordWriteOccured();
755 SetCurrentTimeInMinutes(2.7 * report_interval, test_clock);
756 histogram.RecordWriteOccured();
757 SetCurrentTimeInMinutes(3.3 * report_interval, test_clock);
758 histogram.RecordWriteOccured();
760 // The last write won't be recorded because the second count period hasn't
761 // fully elapsed
762 SetCurrentTimeInMinutes(3.5 * report_interval, test_clock);
763 histogram.ReportOutstandingWrites();
764 scoped_ptr<HistogramSamples> samples =
765 histogram.GetHistogram()->SnapshotSamples();
766 ASSERT_EQ(2, samples->GetCount(3));
767 ASSERT_EQ(1, samples->GetCount(2));
768 ASSERT_EQ(3, samples->TotalCount());
771 TEST_F(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps) {
772 SimpleTestClock* test_clock = new SimpleTestClock;
773 SetCurrentTimeInMinutes(0, test_clock);
774 JsonPrefStore::WriteCountHistogram histogram(
775 base::TimeDelta::FromSeconds(10),
776 base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
777 scoped_ptr<base::Clock>(test_clock));
778 int32 report_interval =
779 JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins;
781 // 1 write in the first period.
782 histogram.RecordWriteOccured();
784 // No writes in the second and third periods.
786 // 2 writes in the fourth period.
787 SetCurrentTimeInMinutes(3.1 * report_interval, test_clock);
788 histogram.RecordWriteOccured();
789 SetCurrentTimeInMinutes(3.3 * report_interval, test_clock);
790 histogram.RecordWriteOccured();
792 // No writes in the fifth period.
794 // 3 writes in the sixth period.
795 SetCurrentTimeInMinutes(5.1 * report_interval, test_clock);
796 histogram.RecordWriteOccured();
797 SetCurrentTimeInMinutes(5.3 * report_interval, test_clock);
798 histogram.RecordWriteOccured();
799 SetCurrentTimeInMinutes(5.5 * report_interval, test_clock);
800 histogram.RecordWriteOccured();
802 SetCurrentTimeInMinutes(6.1 * report_interval, test_clock);
803 histogram.ReportOutstandingWrites();
804 scoped_ptr<HistogramSamples> samples =
805 histogram.GetHistogram()->SnapshotSamples();
806 ASSERT_EQ(3, samples->GetCount(0));
807 ASSERT_EQ(1, samples->GetCount(1));
808 ASSERT_EQ(1, samples->GetCount(2));
809 ASSERT_EQ(1, samples->GetCount(3));
810 ASSERT_EQ(6, samples->TotalCount());
813 class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
814 protected:
815 void SetUp() override {
816 JsonPrefStoreTest::SetUp();
817 test_file_ = temp_dir_.path().AppendASCII("test.json");
820 // Creates a JsonPrefStore with the given |file_writer|.
821 scoped_refptr<JsonPrefStore> CreatePrefStore() {
822 return new JsonPrefStore(test_file_, message_loop_.task_runner(),
823 scoped_ptr<PrefFilter>());
826 // Return the ImportantFileWriter for a given JsonPrefStore.
827 ImportantFileWriter* GetImportantFileWriter(
828 scoped_refptr<JsonPrefStore> pref_store) {
829 return &(pref_store->writer_);
832 // Get the contents of kTestFile. Pumps the message loop before returning the
833 // result.
834 std::string GetTestFileContents() {
835 RunLoop().RunUntilIdle();
836 std::string file_contents;
837 ReadFileToString(test_file_, &file_contents);
838 return file_contents;
841 private:
842 base::FilePath test_file_;
845 TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteBasic) {
846 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
847 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
849 // Set a normal pref and check that it gets scheduled to be written.
850 ASSERT_FALSE(file_writer->HasPendingWrite());
851 pref_store->SetValue("normal",
852 make_scoped_ptr(new base::StringValue("normal")),
853 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
854 ASSERT_TRUE(file_writer->HasPendingWrite());
855 file_writer->DoScheduledWrite();
856 ASSERT_EQ("{\"normal\":\"normal\"}", GetTestFileContents());
857 ASSERT_FALSE(file_writer->HasPendingWrite());
859 // Set a lossy pref and check that it is not scheduled to be written.
860 // SetValue/RemoveValue.
861 pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
862 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
863 ASSERT_FALSE(file_writer->HasPendingWrite());
864 pref_store->RemoveValue("lossy", WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
865 ASSERT_FALSE(file_writer->HasPendingWrite());
867 // SetValueSilently/RemoveValueSilently.
868 pref_store->SetValueSilently("lossy",
869 make_scoped_ptr(new base::StringValue("lossy")),
870 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
871 ASSERT_FALSE(file_writer->HasPendingWrite());
872 pref_store->RemoveValueSilently("lossy",
873 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
874 ASSERT_FALSE(file_writer->HasPendingWrite());
876 // ReportValueChanged.
877 pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
878 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
879 ASSERT_FALSE(file_writer->HasPendingWrite());
880 pref_store->ReportValueChanged("lossy",
881 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
882 ASSERT_FALSE(file_writer->HasPendingWrite());
884 // Call CommitPendingWrite and check that the lossy pref and the normal pref
885 // are there with the last values set above.
886 pref_store->CommitPendingWrite();
887 ASSERT_FALSE(file_writer->HasPendingWrite());
888 ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
889 GetTestFileContents());
892 TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) {
893 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
894 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
896 // Set a lossy pref and check that it is not scheduled to be written.
897 ASSERT_FALSE(file_writer->HasPendingWrite());
898 pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
899 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
900 ASSERT_FALSE(file_writer->HasPendingWrite());
902 // Set a normal pref and check that it is scheduled to be written.
903 pref_store->SetValue("normal",
904 make_scoped_ptr(new base::StringValue("normal")),
905 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
906 ASSERT_TRUE(file_writer->HasPendingWrite());
908 // Call DoScheduledWrite and check both prefs get written.
909 file_writer->DoScheduledWrite();
910 ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
911 GetTestFileContents());
912 ASSERT_FALSE(file_writer->HasPendingWrite());
915 TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) {
916 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
917 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
919 // Set a normal pref and check that it is scheduled to be written.
920 ASSERT_FALSE(file_writer->HasPendingWrite());
921 pref_store->SetValue("normal",
922 make_scoped_ptr(new base::StringValue("normal")),
923 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
924 ASSERT_TRUE(file_writer->HasPendingWrite());
926 // Set a lossy pref and check that the write is still scheduled.
927 pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
928 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
929 ASSERT_TRUE(file_writer->HasPendingWrite());
931 // Call DoScheduledWrite and check both prefs get written.
932 file_writer->DoScheduledWrite();
933 ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
934 GetTestFileContents());
935 ASSERT_FALSE(file_writer->HasPendingWrite());
938 TEST_F(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) {
939 scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
940 ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
942 // Set a lossy pref and check that it is not scheduled to be written.
943 pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
944 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
945 ASSERT_FALSE(file_writer->HasPendingWrite());
947 // Schedule pending lossy writes and check that it is scheduled.
948 pref_store->SchedulePendingLossyWrites();
949 ASSERT_TRUE(file_writer->HasPendingWrite());
951 // Call CommitPendingWrite and check that the lossy pref is there with the
952 // last value set above.
953 pref_store->CommitPendingWrite();
954 ASSERT_FALSE(file_writer->HasPendingWrite());
955 ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents());
958 } // namespace base