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"
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"
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
{
45 InterceptingPrefFilter();
46 ~InterceptingPrefFilter() override
;
48 // PrefFilter implementation:
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
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
{
87 MOCK_METHOD1(OnPrefValueChanged
, void (const std::string
&));
88 MOCK_METHOD1(OnInitializationCompleted
, void (bool));
91 class MockReadErrorDelegate
: public PersistentPrefStore::ReadErrorDelegate
{
93 MOCK_METHOD1(OnError
, void(PersistentPrefStore::PrefReadError
));
98 class JsonPrefStoreTest
: public testing::Test
{
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_
;
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");
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
, new StringValue(some_path
.value()),
196 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
197 EXPECT_TRUE(pref_store
->GetValue(kSomeDirectory
, &actual
));
198 EXPECT_TRUE(actual
->GetAsString(&path
));
199 EXPECT_EQ(some_path
.value(), path
);
201 // Test reading some other data types from sub-dictionaries.
202 EXPECT_TRUE(pref_store
->GetValue(kNewWindowsInTabs
, &actual
));
203 bool boolean
= false;
204 EXPECT_TRUE(actual
->GetAsBoolean(&boolean
));
205 EXPECT_TRUE(boolean
);
207 pref_store
->SetValue(kNewWindowsInTabs
, new FundamentalValue(false),
208 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
209 EXPECT_TRUE(pref_store
->GetValue(kNewWindowsInTabs
, &actual
));
210 EXPECT_TRUE(actual
->GetAsBoolean(&boolean
));
211 EXPECT_FALSE(boolean
);
213 EXPECT_TRUE(pref_store
->GetValue(kMaxTabs
, &actual
));
215 EXPECT_TRUE(actual
->GetAsInteger(&integer
));
216 EXPECT_EQ(20, integer
);
217 pref_store
->SetValue(kMaxTabs
, new FundamentalValue(10),
218 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
219 EXPECT_TRUE(pref_store
->GetValue(kMaxTabs
, &actual
));
220 EXPECT_TRUE(actual
->GetAsInteger(&integer
));
221 EXPECT_EQ(10, integer
);
223 pref_store
->SetValue(kLongIntPref
,
224 new StringValue(base::Int64ToString(214748364842LL)),
225 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
226 EXPECT_TRUE(pref_store
->GetValue(kLongIntPref
, &actual
));
227 EXPECT_TRUE(actual
->GetAsString(&string_value
));
229 base::StringToInt64(string_value
, &value
);
230 EXPECT_EQ(214748364842LL, value
);
232 // Serialize and compare to expected output.
233 ASSERT_TRUE(PathExists(golden_output_file
));
234 pref_store
->CommitPendingWrite();
235 RunLoop().RunUntilIdle();
236 EXPECT_TRUE(TextContentsEqual(golden_output_file
, output_file
));
237 ASSERT_TRUE(base::DeleteFile(output_file
, false));
240 TEST_F(JsonPrefStoreTest
, Basic
) {
241 ASSERT_TRUE(base::CopyFile(data_dir_
.AppendASCII("read.json"),
242 temp_dir_
.path().AppendASCII("write.json")));
244 // Test that the persistent value can be loaded.
245 base::FilePath input_file
= temp_dir_
.path().AppendASCII("write.json");
246 ASSERT_TRUE(PathExists(input_file
));
247 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
248 input_file
, message_loop_
.task_runner(), scoped_ptr
<PrefFilter
>());
249 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE
, pref_store
->ReadPrefs());
250 EXPECT_FALSE(pref_store
->ReadOnly());
251 EXPECT_TRUE(pref_store
->IsInitializationComplete());
253 // The JSON file looks like this:
255 // "homepage": "http://www.cnn.com",
256 // "some_directory": "/usr/local/",
258 // "new_windows_in_tabs": true,
263 RunBasicJsonPrefStoreTest(
264 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
267 TEST_F(JsonPrefStoreTest
, BasicAsync
) {
268 ASSERT_TRUE(base::CopyFile(data_dir_
.AppendASCII("read.json"),
269 temp_dir_
.path().AppendASCII("write.json")));
271 // Test that the persistent value can be loaded.
272 base::FilePath input_file
= temp_dir_
.path().AppendASCII("write.json");
273 ASSERT_TRUE(PathExists(input_file
));
274 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
275 input_file
, message_loop_
.task_runner(), scoped_ptr
<PrefFilter
>());
278 MockPrefStoreObserver mock_observer
;
279 pref_store
->AddObserver(&mock_observer
);
281 MockReadErrorDelegate
* mock_error_delegate
= new MockReadErrorDelegate
;
282 pref_store
->ReadPrefsAsync(mock_error_delegate
);
284 EXPECT_CALL(mock_observer
, OnInitializationCompleted(true)).Times(1);
285 EXPECT_CALL(*mock_error_delegate
,
286 OnError(PersistentPrefStore::PREF_READ_ERROR_NONE
)).Times(0);
287 RunLoop().RunUntilIdle();
288 pref_store
->RemoveObserver(&mock_observer
);
290 EXPECT_FALSE(pref_store
->ReadOnly());
291 EXPECT_TRUE(pref_store
->IsInitializationComplete());
294 // The JSON file looks like this:
296 // "homepage": "http://www.cnn.com",
297 // "some_directory": "/usr/local/",
299 // "new_windows_in_tabs": true,
304 RunBasicJsonPrefStoreTest(
305 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
308 TEST_F(JsonPrefStoreTest
, PreserveEmptyValues
) {
309 FilePath pref_file
= temp_dir_
.path().AppendASCII("empty_values.json");
311 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
312 pref_file
, message_loop_
.task_runner(), scoped_ptr
<PrefFilter
>());
314 // Set some keys with empty values.
315 pref_store
->SetValue("list", new base::ListValue
,
316 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
317 pref_store
->SetValue("dict", new base::DictionaryValue
,
318 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
321 pref_store
->CommitPendingWrite();
322 RunLoop().RunUntilIdle();
325 pref_store
= new JsonPrefStore(pref_file
, message_loop_
.task_runner(),
326 scoped_ptr
<PrefFilter
>());
327 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE
, pref_store
->ReadPrefs());
328 ASSERT_FALSE(pref_store
->ReadOnly());
331 const Value
* result
= NULL
;
332 EXPECT_TRUE(pref_store
->GetValue("list", &result
));
333 EXPECT_TRUE(ListValue().Equals(result
));
334 EXPECT_TRUE(pref_store
->GetValue("dict", &result
));
335 EXPECT_TRUE(DictionaryValue().Equals(result
));
338 // This test is just documenting some potentially non-obvious behavior. It
339 // shouldn't be taken as normative.
340 TEST_F(JsonPrefStoreTest
, RemoveClearsEmptyParent
) {
341 FilePath pref_file
= temp_dir_
.path().AppendASCII("empty_values.json");
343 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
344 pref_file
, message_loop_
.task_runner(), scoped_ptr
<PrefFilter
>());
346 base::DictionaryValue
* dict
= new base::DictionaryValue
;
347 dict
->SetString("key", "value");
348 pref_store
->SetValue("dict", dict
,
349 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
351 pref_store
->RemoveValue("dict.key",
352 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
354 const base::Value
* retrieved_dict
= NULL
;
355 bool has_dict
= pref_store
->GetValue("dict", &retrieved_dict
);
356 EXPECT_FALSE(has_dict
);
359 // Tests asynchronous reading of the file when there is no file.
360 TEST_F(JsonPrefStoreTest
, AsyncNonExistingFile
) {
361 base::FilePath bogus_input_file
= temp_dir_
.path().AppendASCII("read.txt");
362 ASSERT_FALSE(PathExists(bogus_input_file
));
363 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
364 bogus_input_file
, message_loop_
.task_runner(), scoped_ptr
<PrefFilter
>());
365 MockPrefStoreObserver mock_observer
;
366 pref_store
->AddObserver(&mock_observer
);
368 MockReadErrorDelegate
*mock_error_delegate
= new MockReadErrorDelegate
;
369 pref_store
->ReadPrefsAsync(mock_error_delegate
);
371 EXPECT_CALL(mock_observer
, OnInitializationCompleted(true)).Times(1);
372 EXPECT_CALL(*mock_error_delegate
,
373 OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE
)).Times(1);
374 RunLoop().RunUntilIdle();
375 pref_store
->RemoveObserver(&mock_observer
);
377 EXPECT_FALSE(pref_store
->ReadOnly());
380 TEST_F(JsonPrefStoreTest
, ReadWithInterceptor
) {
381 ASSERT_TRUE(base::CopyFile(data_dir_
.AppendASCII("read.json"),
382 temp_dir_
.path().AppendASCII("write.json")));
384 // Test that the persistent value can be loaded.
385 base::FilePath input_file
= temp_dir_
.path().AppendASCII("write.json");
386 ASSERT_TRUE(PathExists(input_file
));
388 scoped_ptr
<InterceptingPrefFilter
> intercepting_pref_filter(
389 new InterceptingPrefFilter());
390 InterceptingPrefFilter
* raw_intercepting_pref_filter_
=
391 intercepting_pref_filter
.get();
392 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
393 input_file
, message_loop_
.task_runner(), intercepting_pref_filter
.Pass());
395 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE
,
396 pref_store
->ReadPrefs());
397 EXPECT_FALSE(pref_store
->ReadOnly());
399 // The store shouldn't be considered initialized until the interceptor
401 EXPECT_TRUE(raw_intercepting_pref_filter_
->has_intercepted_prefs());
402 EXPECT_FALSE(pref_store
->IsInitializationComplete());
403 EXPECT_FALSE(pref_store
->GetValue(kHomePage
, NULL
));
405 raw_intercepting_pref_filter_
->ReleasePrefs();
407 EXPECT_FALSE(raw_intercepting_pref_filter_
->has_intercepted_prefs());
408 EXPECT_TRUE(pref_store
->IsInitializationComplete());
409 EXPECT_TRUE(pref_store
->GetValue(kHomePage
, NULL
));
411 // The JSON file looks like this:
413 // "homepage": "http://www.cnn.com",
414 // "some_directory": "/usr/local/",
416 // "new_windows_in_tabs": true,
421 RunBasicJsonPrefStoreTest(
422 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
425 TEST_F(JsonPrefStoreTest
, ReadAsyncWithInterceptor
) {
426 ASSERT_TRUE(base::CopyFile(data_dir_
.AppendASCII("read.json"),
427 temp_dir_
.path().AppendASCII("write.json")));
429 // Test that the persistent value can be loaded.
430 base::FilePath input_file
= temp_dir_
.path().AppendASCII("write.json");
431 ASSERT_TRUE(PathExists(input_file
));
433 scoped_ptr
<InterceptingPrefFilter
> intercepting_pref_filter(
434 new InterceptingPrefFilter());
435 InterceptingPrefFilter
* raw_intercepting_pref_filter_
=
436 intercepting_pref_filter
.get();
437 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
438 input_file
, message_loop_
.task_runner(), intercepting_pref_filter
.Pass());
440 MockPrefStoreObserver mock_observer
;
441 pref_store
->AddObserver(&mock_observer
);
443 // Ownership of the |mock_error_delegate| is handed to the |pref_store| below.
444 MockReadErrorDelegate
* mock_error_delegate
= new MockReadErrorDelegate
;
447 pref_store
->ReadPrefsAsync(mock_error_delegate
);
449 EXPECT_CALL(mock_observer
, OnInitializationCompleted(true)).Times(0);
450 // EXPECT_CALL(*mock_error_delegate,
451 // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
452 RunLoop().RunUntilIdle();
454 EXPECT_FALSE(pref_store
->ReadOnly());
455 EXPECT_TRUE(raw_intercepting_pref_filter_
->has_intercepted_prefs());
456 EXPECT_FALSE(pref_store
->IsInitializationComplete());
457 EXPECT_FALSE(pref_store
->GetValue(kHomePage
, NULL
));
461 EXPECT_CALL(mock_observer
, OnInitializationCompleted(true)).Times(1);
462 // EXPECT_CALL(*mock_error_delegate,
463 // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
465 raw_intercepting_pref_filter_
->ReleasePrefs();
467 EXPECT_FALSE(pref_store
->ReadOnly());
468 EXPECT_FALSE(raw_intercepting_pref_filter_
->has_intercepted_prefs());
469 EXPECT_TRUE(pref_store
->IsInitializationComplete());
470 EXPECT_TRUE(pref_store
->GetValue(kHomePage
, NULL
));
473 pref_store
->RemoveObserver(&mock_observer
);
475 // The JSON file looks like this:
477 // "homepage": "http://www.cnn.com",
478 // "some_directory": "/usr/local/",
480 // "new_windows_in_tabs": true,
485 RunBasicJsonPrefStoreTest(
486 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
489 TEST_F(JsonPrefStoreTest
, AlternateFile
) {
491 base::CopyFile(data_dir_
.AppendASCII("read.json"),
492 temp_dir_
.path().AppendASCII("alternate.json")));
494 // Test that the alternate file is moved to the main file and read as-is from
496 base::FilePath input_file
= temp_dir_
.path().AppendASCII("write.json");
497 base::FilePath alternate_input_file
=
498 temp_dir_
.path().AppendASCII("alternate.json");
499 ASSERT_FALSE(PathExists(input_file
));
500 ASSERT_TRUE(PathExists(alternate_input_file
));
501 scoped_refptr
<JsonPrefStore
> pref_store
=
502 new JsonPrefStore(input_file
, alternate_input_file
,
503 message_loop_
.task_runner(), scoped_ptr
<PrefFilter
>());
505 ASSERT_FALSE(PathExists(input_file
));
506 ASSERT_TRUE(PathExists(alternate_input_file
));
507 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE
, pref_store
->ReadPrefs());
509 ASSERT_TRUE(PathExists(input_file
));
510 ASSERT_FALSE(PathExists(alternate_input_file
));
512 EXPECT_FALSE(pref_store
->ReadOnly());
513 EXPECT_TRUE(pref_store
->IsInitializationComplete());
515 // The JSON file looks like this:
517 // "homepage": "http://www.cnn.com",
518 // "some_directory": "/usr/local/",
520 // "new_windows_in_tabs": true,
525 RunBasicJsonPrefStoreTest(
526 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
529 TEST_F(JsonPrefStoreTest
, AlternateFileIgnoredWhenMainFileExists
) {
531 base::CopyFile(data_dir_
.AppendASCII("read.json"),
532 temp_dir_
.path().AppendASCII("write.json")));
534 base::CopyFile(data_dir_
.AppendASCII("invalid.json"),
535 temp_dir_
.path().AppendASCII("alternate.json")));
537 // Test that the alternate file is ignored and that the read occurs from the
538 // existing main file. There is no attempt at even deleting the alternate
539 // file as this scenario should never happen in normal user-data-dirs.
540 base::FilePath input_file
= temp_dir_
.path().AppendASCII("write.json");
541 base::FilePath alternate_input_file
=
542 temp_dir_
.path().AppendASCII("alternate.json");
543 ASSERT_TRUE(PathExists(input_file
));
544 ASSERT_TRUE(PathExists(alternate_input_file
));
545 scoped_refptr
<JsonPrefStore
> pref_store
=
546 new JsonPrefStore(input_file
, alternate_input_file
,
547 message_loop_
.task_runner(), scoped_ptr
<PrefFilter
>());
549 ASSERT_TRUE(PathExists(input_file
));
550 ASSERT_TRUE(PathExists(alternate_input_file
));
551 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE
, pref_store
->ReadPrefs());
553 ASSERT_TRUE(PathExists(input_file
));
554 ASSERT_TRUE(PathExists(alternate_input_file
));
556 EXPECT_FALSE(pref_store
->ReadOnly());
557 EXPECT_TRUE(pref_store
->IsInitializationComplete());
559 // The JSON file looks like this:
561 // "homepage": "http://www.cnn.com",
562 // "some_directory": "/usr/local/",
564 // "new_windows_in_tabs": true,
569 RunBasicJsonPrefStoreTest(
570 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
573 TEST_F(JsonPrefStoreTest
, AlternateFileDNE
) {
575 base::CopyFile(data_dir_
.AppendASCII("read.json"),
576 temp_dir_
.path().AppendASCII("write.json")));
578 // Test that the basic read works fine when an alternate file is specified but
580 base::FilePath input_file
= temp_dir_
.path().AppendASCII("write.json");
581 base::FilePath alternate_input_file
=
582 temp_dir_
.path().AppendASCII("alternate.json");
583 ASSERT_TRUE(PathExists(input_file
));
584 ASSERT_FALSE(PathExists(alternate_input_file
));
585 scoped_refptr
<JsonPrefStore
> pref_store
=
586 new JsonPrefStore(input_file
, alternate_input_file
,
587 message_loop_
.task_runner(), scoped_ptr
<PrefFilter
>());
589 ASSERT_TRUE(PathExists(input_file
));
590 ASSERT_FALSE(PathExists(alternate_input_file
));
591 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE
, pref_store
->ReadPrefs());
593 ASSERT_TRUE(PathExists(input_file
));
594 ASSERT_FALSE(PathExists(alternate_input_file
));
596 EXPECT_FALSE(pref_store
->ReadOnly());
597 EXPECT_TRUE(pref_store
->IsInitializationComplete());
599 // The JSON file looks like this:
601 // "homepage": "http://www.cnn.com",
602 // "some_directory": "/usr/local/",
604 // "new_windows_in_tabs": true,
609 RunBasicJsonPrefStoreTest(
610 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
613 TEST_F(JsonPrefStoreTest
, BasicAsyncWithAlternateFile
) {
615 base::CopyFile(data_dir_
.AppendASCII("read.json"),
616 temp_dir_
.path().AppendASCII("alternate.json")));
618 // Test that the alternate file is moved to the main file and read as-is from
619 // there even when the read is made asynchronously.
620 base::FilePath input_file
= temp_dir_
.path().AppendASCII("write.json");
621 base::FilePath alternate_input_file
=
622 temp_dir_
.path().AppendASCII("alternate.json");
623 ASSERT_FALSE(PathExists(input_file
));
624 ASSERT_TRUE(PathExists(alternate_input_file
));
625 scoped_refptr
<JsonPrefStore
> pref_store
=
626 new JsonPrefStore(input_file
, alternate_input_file
,
627 message_loop_
.task_runner(), scoped_ptr
<PrefFilter
>());
629 ASSERT_FALSE(PathExists(input_file
));
630 ASSERT_TRUE(PathExists(alternate_input_file
));
633 MockPrefStoreObserver mock_observer
;
634 pref_store
->AddObserver(&mock_observer
);
636 MockReadErrorDelegate
* mock_error_delegate
= new MockReadErrorDelegate
;
637 pref_store
->ReadPrefsAsync(mock_error_delegate
);
639 EXPECT_CALL(mock_observer
, OnInitializationCompleted(true)).Times(1);
640 EXPECT_CALL(*mock_error_delegate
,
641 OnError(PersistentPrefStore::PREF_READ_ERROR_NONE
)).Times(0);
642 RunLoop().RunUntilIdle();
643 pref_store
->RemoveObserver(&mock_observer
);
645 EXPECT_FALSE(pref_store
->ReadOnly());
646 EXPECT_TRUE(pref_store
->IsInitializationComplete());
649 ASSERT_TRUE(PathExists(input_file
));
650 ASSERT_FALSE(PathExists(alternate_input_file
));
652 // The JSON file looks like this:
654 // "homepage": "http://www.cnn.com",
655 // "some_directory": "/usr/local/",
657 // "new_windows_in_tabs": true,
662 RunBasicJsonPrefStoreTest(
663 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
666 TEST_F(JsonPrefStoreTest
, WriteCountHistogramTestBasic
) {
667 SimpleTestClock
* test_clock
= new SimpleTestClock
;
668 SetCurrentTimeInMinutes(0, test_clock
);
669 JsonPrefStore::WriteCountHistogram
histogram(
670 base::TimeDelta::FromSeconds(10),
671 base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
672 scoped_ptr
<base::Clock
>(test_clock
));
673 int32 report_interval
=
674 JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins
;
676 histogram
.RecordWriteOccured();
678 SetCurrentTimeInMinutes(1.5 * report_interval
, test_clock
);
679 histogram
.ReportOutstandingWrites();
680 scoped_ptr
<HistogramSamples
> samples
=
681 histogram
.GetHistogram()->SnapshotSamples();
682 ASSERT_EQ(1, samples
->GetCount(1));
683 ASSERT_EQ(1, samples
->TotalCount());
685 ASSERT_EQ("Settings.JsonDataWriteCount.Local_State",
686 histogram
.GetHistogram()->histogram_name());
687 ASSERT_TRUE(histogram
.GetHistogram()->HasConstructionArguments(1, 30, 31));
690 TEST_F(JsonPrefStoreTest
, WriteCountHistogramTestSinglePeriod
) {
691 SimpleTestClock
* test_clock
= new SimpleTestClock
;
692 SetCurrentTimeInMinutes(0, test_clock
);
693 JsonPrefStore::WriteCountHistogram
histogram(
694 base::TimeDelta::FromSeconds(10),
695 base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
696 scoped_ptr
<base::Clock
>(test_clock
));
697 int32 report_interval
=
698 JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins
;
700 histogram
.RecordWriteOccured();
701 SetCurrentTimeInMinutes(0.5 * report_interval
, test_clock
);
702 histogram
.RecordWriteOccured();
703 SetCurrentTimeInMinutes(0.7 * report_interval
, test_clock
);
704 histogram
.RecordWriteOccured();
706 // Nothing should be recorded until the report period has elapsed.
707 scoped_ptr
<HistogramSamples
> samples
=
708 histogram
.GetHistogram()->SnapshotSamples();
709 ASSERT_EQ(0, samples
->TotalCount());
711 SetCurrentTimeInMinutes(1.3 * report_interval
, test_clock
);
712 histogram
.RecordWriteOccured();
714 // Now the report period has elapsed.
715 samples
= histogram
.GetHistogram()->SnapshotSamples();
716 ASSERT_EQ(1, samples
->GetCount(3));
717 ASSERT_EQ(1, samples
->TotalCount());
719 // The last write won't be recorded because the second count period hasn't
721 SetCurrentTimeInMinutes(1.5 * report_interval
, test_clock
);
722 histogram
.ReportOutstandingWrites();
724 samples
= histogram
.GetHistogram()->SnapshotSamples();
725 ASSERT_EQ(1, samples
->GetCount(3));
726 ASSERT_EQ(1, samples
->TotalCount());
729 TEST_F(JsonPrefStoreTest
, WriteCountHistogramTestMultiplePeriods
) {
730 SimpleTestClock
* test_clock
= new SimpleTestClock
;
731 SetCurrentTimeInMinutes(0, test_clock
);
732 JsonPrefStore::WriteCountHistogram
histogram(
733 base::TimeDelta::FromSeconds(10),
734 base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
735 scoped_ptr
<base::Clock
>(test_clock
));
736 int32 report_interval
=
737 JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins
;
739 histogram
.RecordWriteOccured();
740 SetCurrentTimeInMinutes(0.5 * report_interval
, test_clock
);
741 histogram
.RecordWriteOccured();
742 SetCurrentTimeInMinutes(0.7 * report_interval
, test_clock
);
743 histogram
.RecordWriteOccured();
744 SetCurrentTimeInMinutes(1.3 * report_interval
, test_clock
);
745 histogram
.RecordWriteOccured();
746 SetCurrentTimeInMinutes(1.5 * report_interval
, test_clock
);
747 histogram
.RecordWriteOccured();
748 SetCurrentTimeInMinutes(2.1 * report_interval
, test_clock
);
749 histogram
.RecordWriteOccured();
750 SetCurrentTimeInMinutes(2.5 * report_interval
, test_clock
);
751 histogram
.RecordWriteOccured();
752 SetCurrentTimeInMinutes(2.7 * report_interval
, test_clock
);
753 histogram
.RecordWriteOccured();
754 SetCurrentTimeInMinutes(3.3 * report_interval
, test_clock
);
755 histogram
.RecordWriteOccured();
757 // The last write won't be recorded because the second count period hasn't
759 SetCurrentTimeInMinutes(3.5 * report_interval
, test_clock
);
760 histogram
.ReportOutstandingWrites();
761 scoped_ptr
<HistogramSamples
> samples
=
762 histogram
.GetHistogram()->SnapshotSamples();
763 ASSERT_EQ(2, samples
->GetCount(3));
764 ASSERT_EQ(1, samples
->GetCount(2));
765 ASSERT_EQ(3, samples
->TotalCount());
768 TEST_F(JsonPrefStoreTest
, WriteCountHistogramTestPeriodWithGaps
) {
769 SimpleTestClock
* test_clock
= new SimpleTestClock
;
770 SetCurrentTimeInMinutes(0, test_clock
);
771 JsonPrefStore::WriteCountHistogram
histogram(
772 base::TimeDelta::FromSeconds(10),
773 base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
774 scoped_ptr
<base::Clock
>(test_clock
));
775 int32 report_interval
=
776 JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins
;
778 // 1 write in the first period.
779 histogram
.RecordWriteOccured();
781 // No writes in the second and third periods.
783 // 2 writes in the fourth period.
784 SetCurrentTimeInMinutes(3.1 * report_interval
, test_clock
);
785 histogram
.RecordWriteOccured();
786 SetCurrentTimeInMinutes(3.3 * report_interval
, test_clock
);
787 histogram
.RecordWriteOccured();
789 // No writes in the fifth period.
791 // 3 writes in the sixth period.
792 SetCurrentTimeInMinutes(5.1 * report_interval
, test_clock
);
793 histogram
.RecordWriteOccured();
794 SetCurrentTimeInMinutes(5.3 * report_interval
, test_clock
);
795 histogram
.RecordWriteOccured();
796 SetCurrentTimeInMinutes(5.5 * report_interval
, test_clock
);
797 histogram
.RecordWriteOccured();
799 SetCurrentTimeInMinutes(6.1 * report_interval
, test_clock
);
800 histogram
.ReportOutstandingWrites();
801 scoped_ptr
<HistogramSamples
> samples
=
802 histogram
.GetHistogram()->SnapshotSamples();
803 ASSERT_EQ(3, samples
->GetCount(0));
804 ASSERT_EQ(1, samples
->GetCount(1));
805 ASSERT_EQ(1, samples
->GetCount(2));
806 ASSERT_EQ(1, samples
->GetCount(3));
807 ASSERT_EQ(6, samples
->TotalCount());
810 class JsonPrefStoreLossyWriteTest
: public JsonPrefStoreTest
{
812 void SetUp() override
{
813 JsonPrefStoreTest::SetUp();
814 test_file_
= temp_dir_
.path().AppendASCII("test.json");
817 // Creates a JsonPrefStore with the given |file_writer|.
818 scoped_refptr
<JsonPrefStore
> CreatePrefStore() {
819 return new JsonPrefStore(test_file_
, message_loop_
.task_runner(),
820 scoped_ptr
<PrefFilter
>());
823 // Return the ImportantFileWriter for a given JsonPrefStore.
824 ImportantFileWriter
* GetImportantFileWriter(
825 scoped_refptr
<JsonPrefStore
> pref_store
) {
826 return &(pref_store
->writer_
);
829 // Get the contents of kTestFile. Pumps the message loop before returning the
831 std::string
GetTestFileContents() {
832 RunLoop().RunUntilIdle();
833 std::string file_contents
;
834 ReadFileToString(test_file_
, &file_contents
);
835 return file_contents
;
839 base::FilePath test_file_
;
842 TEST_F(JsonPrefStoreLossyWriteTest
, LossyWriteBasic
) {
843 scoped_refptr
<JsonPrefStore
> pref_store
= CreatePrefStore();
844 ImportantFileWriter
* file_writer
= GetImportantFileWriter(pref_store
);
846 // Set a normal pref and check that it gets scheduled to be written.
847 ASSERT_FALSE(file_writer
->HasPendingWrite());
848 pref_store
->SetValue("normal", new base::StringValue("normal"),
849 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
850 ASSERT_TRUE(file_writer
->HasPendingWrite());
851 file_writer
->DoScheduledWrite();
852 ASSERT_EQ("{\"normal\":\"normal\"}", GetTestFileContents());
853 ASSERT_FALSE(file_writer
->HasPendingWrite());
855 // Set a lossy pref and check that it is not scheduled to be written.
856 // SetValue/RemoveValue.
857 pref_store
->SetValue("lossy", new base::StringValue("lossy"),
858 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG
);
859 ASSERT_FALSE(file_writer
->HasPendingWrite());
860 pref_store
->RemoveValue("lossy", WriteablePrefStore::LOSSY_PREF_WRITE_FLAG
);
861 ASSERT_FALSE(file_writer
->HasPendingWrite());
863 // SetValueSilently/RemoveValueSilently.
864 pref_store
->SetValueSilently("lossy", new base::StringValue("lossy"),
865 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG
);
866 ASSERT_FALSE(file_writer
->HasPendingWrite());
867 pref_store
->RemoveValueSilently("lossy",
868 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG
);
869 ASSERT_FALSE(file_writer
->HasPendingWrite());
871 // ReportValueChanged.
872 pref_store
->SetValue("lossy", new base::StringValue("lossy"),
873 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG
);
874 ASSERT_FALSE(file_writer
->HasPendingWrite());
875 pref_store
->ReportValueChanged("lossy",
876 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG
);
877 ASSERT_FALSE(file_writer
->HasPendingWrite());
879 // Call CommitPendingWrite and check that the lossy pref and the normal pref
880 // are there with the last values set above.
881 pref_store
->CommitPendingWrite();
882 ASSERT_FALSE(file_writer
->HasPendingWrite());
883 ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
884 GetTestFileContents());
887 TEST_F(JsonPrefStoreLossyWriteTest
, LossyWriteMixedLossyFirst
) {
888 scoped_refptr
<JsonPrefStore
> pref_store
= CreatePrefStore();
889 ImportantFileWriter
* file_writer
= GetImportantFileWriter(pref_store
);
891 // Set a lossy pref and check that it is not scheduled to be written.
892 ASSERT_FALSE(file_writer
->HasPendingWrite());
893 pref_store
->SetValue("lossy", new base::StringValue("lossy"),
894 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG
);
895 ASSERT_FALSE(file_writer
->HasPendingWrite());
897 // Set a normal pref and check that it is scheduled to be written.
898 pref_store
->SetValue("normal", new base::StringValue("normal"),
899 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
900 ASSERT_TRUE(file_writer
->HasPendingWrite());
902 // Call DoScheduledWrite and check both prefs get written.
903 file_writer
->DoScheduledWrite();
904 ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
905 GetTestFileContents());
906 ASSERT_FALSE(file_writer
->HasPendingWrite());
909 TEST_F(JsonPrefStoreLossyWriteTest
, LossyWriteMixedLossySecond
) {
910 scoped_refptr
<JsonPrefStore
> pref_store
= CreatePrefStore();
911 ImportantFileWriter
* file_writer
= GetImportantFileWriter(pref_store
);
913 // Set a normal pref and check that it is scheduled to be written.
914 ASSERT_FALSE(file_writer
->HasPendingWrite());
915 pref_store
->SetValue("normal", new base::StringValue("normal"),
916 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS
);
917 ASSERT_TRUE(file_writer
->HasPendingWrite());
919 // Set a lossy pref and check that the write is still scheduled.
920 pref_store
->SetValue("lossy", new base::StringValue("lossy"),
921 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG
);
922 ASSERT_TRUE(file_writer
->HasPendingWrite());
924 // Call DoScheduledWrite and check both prefs get written.
925 file_writer
->DoScheduledWrite();
926 ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
927 GetTestFileContents());
928 ASSERT_FALSE(file_writer
->HasPendingWrite());