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
,
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
));
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(
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
));
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:
258 // "homepage": "http://www.cnn.com",
259 // "some_directory": "/usr/local/",
261 // "new_windows_in_tabs": true,
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:
299 // "homepage": "http://www.cnn.com",
300 // "some_directory": "/usr/local/",
302 // "new_windows_in_tabs": true,
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
);
324 pref_store
->CommitPendingWrite();
325 RunLoop().RunUntilIdle();
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());
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
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:
416 // "homepage": "http://www.cnn.com",
417 // "some_directory": "/usr/local/",
419 // "new_windows_in_tabs": true,
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:
480 // "homepage": "http://www.cnn.com",
481 // "some_directory": "/usr/local/",
483 // "new_windows_in_tabs": true,
488 RunBasicJsonPrefStoreTest(
489 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
492 TEST_F(JsonPrefStoreTest
, AlternateFile
) {
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
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:
520 // "homepage": "http://www.cnn.com",
521 // "some_directory": "/usr/local/",
523 // "new_windows_in_tabs": true,
528 RunBasicJsonPrefStoreTest(
529 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
532 TEST_F(JsonPrefStoreTest
, AlternateFileIgnoredWhenMainFileExists
) {
534 base::CopyFile(data_dir_
.AppendASCII("read.json"),
535 temp_dir_
.path().AppendASCII("write.json")));
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:
564 // "homepage": "http://www.cnn.com",
565 // "some_directory": "/usr/local/",
567 // "new_windows_in_tabs": true,
572 RunBasicJsonPrefStoreTest(
573 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
576 TEST_F(JsonPrefStoreTest
, AlternateFileDNE
) {
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
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:
604 // "homepage": "http://www.cnn.com",
605 // "some_directory": "/usr/local/",
607 // "new_windows_in_tabs": true,
612 RunBasicJsonPrefStoreTest(
613 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
616 TEST_F(JsonPrefStoreTest
, BasicAsyncWithAlternateFile
) {
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:
657 // "homepage": "http://www.cnn.com",
658 // "some_directory": "/usr/local/",
660 // "new_windows_in_tabs": true,
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
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
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
{
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
834 std::string
GetTestFileContents() {
835 RunLoop().RunUntilIdle();
836 std::string file_contents
;
837 ReadFileToString(test_file_
, &file_contents
);
838 return file_contents
;
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());