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.
4
5#include "base/prefs/json_pref_store.h"
6
7#include "base/bind.h"
8#include "base/file_util.h"
9#include "base/files/scoped_temp_dir.h"
10#include "base/memory/ref_counted.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/message_loop/message_loop.h"
13#include "base/path_service.h"
14#include "base/prefs/pref_filter.h"
15#include "base/run_loop.h"
16#include "base/strings/string_number_conversions.h"
17#include "base/strings/string_util.h"
18#include "base/strings/utf_string_conversions.h"
19#include "base/threading/sequenced_worker_pool.h"
20#include "base/threading/thread.h"
21#include "base/values.h"
22#include "testing/gmock/include/gmock/gmock.h"
23#include "testing/gtest/include/gtest/gtest.h"
24
25namespace base {
26namespace {
27
28const char kHomePage[] = "homepage";
29
30// A PrefFilter that will intercept all calls to FilterOnLoad() and hold on
31// to the |prefs| until explicitly asked to release them.
32class InterceptingPrefFilter : public PrefFilter {
33 public:
34  InterceptingPrefFilter();
35  virtual ~InterceptingPrefFilter();
36
37  // PrefFilter implementation:
38  virtual void FilterOnLoad(
39      const PostFilterOnLoadCallback& post_filter_on_load_callback,
40      scoped_ptr<base::DictionaryValue> pref_store_contents) OVERRIDE;
41  virtual void FilterUpdate(const std::string& path) OVERRIDE {}
42  virtual void FilterSerializeData(
43      base::DictionaryValue* pref_store_contents) OVERRIDE {}
44
45  bool has_intercepted_prefs() const { return intercepted_prefs_ != NULL; }
46
47  // Finalize an intercepted read, handing |intercepted_prefs_| back to its
48  // JsonPrefStore.
49  void ReleasePrefs();
50
51 private:
52  PostFilterOnLoadCallback post_filter_on_load_callback_;
53  scoped_ptr<base::DictionaryValue> intercepted_prefs_;
54
55  DISALLOW_COPY_AND_ASSIGN(InterceptingPrefFilter);
56};
57
58InterceptingPrefFilter::InterceptingPrefFilter() {}
59InterceptingPrefFilter::~InterceptingPrefFilter() {}
60
61void InterceptingPrefFilter::FilterOnLoad(
62    const PostFilterOnLoadCallback& post_filter_on_load_callback,
63    scoped_ptr<base::DictionaryValue> pref_store_contents) {
64  post_filter_on_load_callback_ = post_filter_on_load_callback;
65  intercepted_prefs_ = pref_store_contents.Pass();
66}
67
68void InterceptingPrefFilter::ReleasePrefs() {
69  EXPECT_FALSE(post_filter_on_load_callback_.is_null());
70  post_filter_on_load_callback_.Run(intercepted_prefs_.Pass(), false);
71  post_filter_on_load_callback_.Reset();
72}
73
74class MockPrefStoreObserver : public PrefStore::Observer {
75 public:
76  MOCK_METHOD1(OnPrefValueChanged, void (const std::string&));
77  MOCK_METHOD1(OnInitializationCompleted, void (bool));
78};
79
80class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
81 public:
82  MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError));
83};
84
85}  // namespace
86
87class JsonPrefStoreTest : public testing::Test {
88 protected:
89  virtual void SetUp() OVERRIDE {
90    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
91
92    ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir_));
93    data_dir_ = data_dir_.AppendASCII("prefs");
94    ASSERT_TRUE(PathExists(data_dir_));
95  }
96
97  virtual void TearDown() OVERRIDE {
98    // Make sure all pending tasks have been processed (e.g., deleting the
99    // JsonPrefStore may post write tasks).
100    message_loop_.PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure());
101    message_loop_.Run();
102  }
103
104  // The path to temporary directory used to contain the test operations.
105  base::ScopedTempDir temp_dir_;
106  // The path to the directory where the test data is stored.
107  base::FilePath data_dir_;
108  // A message loop that we can use as the file thread message loop.
109  MessageLoop message_loop_;
110};
111
112// Test fallback behavior for a nonexistent file.
113TEST_F(JsonPrefStoreTest, NonExistentFile) {
114  base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
115  ASSERT_FALSE(PathExists(bogus_input_file));
116  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
117      bogus_input_file,
118      message_loop_.message_loop_proxy().get(),
119      scoped_ptr<PrefFilter>());
120  EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
121            pref_store->ReadPrefs());
122  EXPECT_FALSE(pref_store->ReadOnly());
123}
124
125// Test fallback behavior for a nonexistent file and alternate file.
126TEST_F(JsonPrefStoreTest, NonExistentFileAndAlternateFile) {
127  base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
128  base::FilePath bogus_alternate_input_file =
129      data_dir_.AppendASCII("read_alternate.txt");
130  ASSERT_FALSE(PathExists(bogus_input_file));
131  ASSERT_FALSE(PathExists(bogus_alternate_input_file));
132  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
133      bogus_input_file,
134      bogus_alternate_input_file,
135      message_loop_.message_loop_proxy().get(),
136      scoped_ptr<PrefFilter>());
137  EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
138            pref_store->ReadPrefs());
139  EXPECT_FALSE(pref_store->ReadOnly());
140}
141
142// Test fallback behavior for an invalid file.
143TEST_F(JsonPrefStoreTest, InvalidFile) {
144  base::FilePath invalid_file_original = data_dir_.AppendASCII("invalid.json");
145  base::FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json");
146  ASSERT_TRUE(base::CopyFile(invalid_file_original, invalid_file));
147  scoped_refptr<JsonPrefStore> pref_store =
148      new JsonPrefStore(invalid_file,
149                        message_loop_.message_loop_proxy().get(),
150                        scoped_ptr<PrefFilter>());
151  EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
152            pref_store->ReadPrefs());
153  EXPECT_FALSE(pref_store->ReadOnly());
154
155  // The file should have been moved aside.
156  EXPECT_FALSE(PathExists(invalid_file));
157  base::FilePath moved_aside = temp_dir_.path().AppendASCII("invalid.bad");
158  EXPECT_TRUE(PathExists(moved_aside));
159  EXPECT_TRUE(TextContentsEqual(invalid_file_original, moved_aside));
160}
161
162// This function is used to avoid code duplication while testing synchronous and
163// asynchronous version of the JsonPrefStore loading.
164void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
165                               const base::FilePath& output_file,
166                               const base::FilePath& golden_output_file) {
167  const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
168  const char kMaxTabs[] = "tabs.max_tabs";
169  const char kLongIntPref[] = "long_int.pref";
170
171  std::string cnn("http://www.cnn.com");
172
173  const Value* actual;
174  EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual));
175  std::string string_value;
176  EXPECT_TRUE(actual->GetAsString(&string_value));
177  EXPECT_EQ(cnn, string_value);
178
179  const char kSomeDirectory[] = "some_directory";
180
181  EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
182  base::FilePath::StringType path;
183  EXPECT_TRUE(actual->GetAsString(&path));
184  EXPECT_EQ(base::FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path);
185  base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/"));
186
187  pref_store->SetValue(kSomeDirectory, new StringValue(some_path.value()));
188  EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
189  EXPECT_TRUE(actual->GetAsString(&path));
190  EXPECT_EQ(some_path.value(), path);
191
192  // Test reading some other data types from sub-dictionaries.
193  EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
194  bool boolean = false;
195  EXPECT_TRUE(actual->GetAsBoolean(&boolean));
196  EXPECT_TRUE(boolean);
197
198  pref_store->SetValue(kNewWindowsInTabs, new FundamentalValue(false));
199  EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
200  EXPECT_TRUE(actual->GetAsBoolean(&boolean));
201  EXPECT_FALSE(boolean);
202
203  EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
204  int integer = 0;
205  EXPECT_TRUE(actual->GetAsInteger(&integer));
206  EXPECT_EQ(20, integer);
207  pref_store->SetValue(kMaxTabs, new FundamentalValue(10));
208  EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
209  EXPECT_TRUE(actual->GetAsInteger(&integer));
210  EXPECT_EQ(10, integer);
211
212  pref_store->SetValue(kLongIntPref,
213                       new StringValue(base::Int64ToString(214748364842LL)));
214  EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual));
215  EXPECT_TRUE(actual->GetAsString(&string_value));
216  int64 value;
217  base::StringToInt64(string_value, &value);
218  EXPECT_EQ(214748364842LL, value);
219
220  // Serialize and compare to expected output.
221  ASSERT_TRUE(PathExists(golden_output_file));
222  pref_store->CommitPendingWrite();
223  RunLoop().RunUntilIdle();
224  EXPECT_TRUE(TextContentsEqual(golden_output_file, output_file));
225  ASSERT_TRUE(base::DeleteFile(output_file, false));
226}
227
228TEST_F(JsonPrefStoreTest, Basic) {
229  ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
230                             temp_dir_.path().AppendASCII("write.json")));
231
232  // Test that the persistent value can be loaded.
233  base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
234  ASSERT_TRUE(PathExists(input_file));
235  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
236      input_file,
237      message_loop_.message_loop_proxy().get(),
238      scoped_ptr<PrefFilter>());
239  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
240  EXPECT_FALSE(pref_store->ReadOnly());
241  EXPECT_TRUE(pref_store->IsInitializationComplete());
242
243  // The JSON file looks like this:
244  // {
245  //   "homepage": "http://www.cnn.com",
246  //   "some_directory": "/usr/local/",
247  //   "tabs": {
248  //     "new_windows_in_tabs": true,
249  //     "max_tabs": 20
250  //   }
251  // }
252
253  RunBasicJsonPrefStoreTest(
254      pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
255}
256
257TEST_F(JsonPrefStoreTest, BasicAsync) {
258  ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
259                             temp_dir_.path().AppendASCII("write.json")));
260
261  // Test that the persistent value can be loaded.
262  base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
263  ASSERT_TRUE(PathExists(input_file));
264  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
265      input_file,
266      message_loop_.message_loop_proxy().get(),
267      scoped_ptr<PrefFilter>());
268
269  {
270    MockPrefStoreObserver mock_observer;
271    pref_store->AddObserver(&mock_observer);
272
273    MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
274    pref_store->ReadPrefsAsync(mock_error_delegate);
275
276    EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
277    EXPECT_CALL(*mock_error_delegate,
278                OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
279    RunLoop().RunUntilIdle();
280    pref_store->RemoveObserver(&mock_observer);
281
282    EXPECT_FALSE(pref_store->ReadOnly());
283    EXPECT_TRUE(pref_store->IsInitializationComplete());
284  }
285
286  // The JSON file looks like this:
287  // {
288  //   "homepage": "http://www.cnn.com",
289  //   "some_directory": "/usr/local/",
290  //   "tabs": {
291  //     "new_windows_in_tabs": true,
292  //     "max_tabs": 20
293  //   }
294  // }
295
296  RunBasicJsonPrefStoreTest(
297      pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
298}
299
300TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
301  FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json");
302
303  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
304      pref_file,
305      message_loop_.message_loop_proxy(),
306      scoped_ptr<PrefFilter>());
307
308  // Set some keys with empty values.
309  pref_store->SetValue("list", new base::ListValue);
310  pref_store->SetValue("dict", new base::DictionaryValue);
311
312  // Write to file.
313  pref_store->CommitPendingWrite();
314  MessageLoop::current()->RunUntilIdle();
315
316  // Reload.
317  pref_store = new JsonPrefStore(
318      pref_file,
319      message_loop_.message_loop_proxy(),
320      scoped_ptr<PrefFilter>());
321  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
322  ASSERT_FALSE(pref_store->ReadOnly());
323
324  // Check values.
325  const Value* result = NULL;
326  EXPECT_TRUE(pref_store->GetValue("list", &result));
327  EXPECT_TRUE(ListValue().Equals(result));
328  EXPECT_TRUE(pref_store->GetValue("dict", &result));
329  EXPECT_TRUE(DictionaryValue().Equals(result));
330}
331
332// This test is just documenting some potentially non-obvious behavior. It
333// shouldn't be taken as normative.
334TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) {
335  FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json");
336
337  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
338      pref_file,
339      message_loop_.message_loop_proxy(),
340      scoped_ptr<PrefFilter>());
341
342  base::DictionaryValue* dict = new base::DictionaryValue;
343  dict->SetString("key", "value");
344  pref_store->SetValue("dict", dict);
345
346  pref_store->RemoveValue("dict.key");
347
348  const base::Value* retrieved_dict = NULL;
349  bool has_dict = pref_store->GetValue("dict", &retrieved_dict);
350  EXPECT_FALSE(has_dict);
351}
352
353// Tests asynchronous reading of the file when there is no file.
354TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
355  base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
356  ASSERT_FALSE(PathExists(bogus_input_file));
357  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
358      bogus_input_file,
359      message_loop_.message_loop_proxy().get(),
360      scoped_ptr<PrefFilter>());
361  MockPrefStoreObserver mock_observer;
362  pref_store->AddObserver(&mock_observer);
363
364  MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate;
365  pref_store->ReadPrefsAsync(mock_error_delegate);
366
367  EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
368  EXPECT_CALL(*mock_error_delegate,
369              OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1);
370  RunLoop().RunUntilIdle();
371  pref_store->RemoveObserver(&mock_observer);
372
373  EXPECT_FALSE(pref_store->ReadOnly());
374}
375
376TEST_F(JsonPrefStoreTest, ReadWithInterceptor) {
377  ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
378                             temp_dir_.path().AppendASCII("write.json")));
379
380  // Test that the persistent value can be loaded.
381  base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
382  ASSERT_TRUE(PathExists(input_file));
383
384  scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter(
385      new InterceptingPrefFilter());
386  InterceptingPrefFilter* raw_intercepting_pref_filter_ =
387      intercepting_pref_filter.get();
388  scoped_refptr<JsonPrefStore> pref_store =
389      new JsonPrefStore(input_file,
390                        message_loop_.message_loop_proxy().get(),
391                        intercepting_pref_filter.PassAs<PrefFilter>());
392
393  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE,
394            pref_store->ReadPrefs());
395  EXPECT_FALSE(pref_store->ReadOnly());
396
397  // The store shouldn't be considered initialized until the interceptor
398  // returns.
399  EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
400  EXPECT_FALSE(pref_store->IsInitializationComplete());
401  EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL));
402
403  raw_intercepting_pref_filter_->ReleasePrefs();
404
405  EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
406  EXPECT_TRUE(pref_store->IsInitializationComplete());
407  EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL));
408
409  // The JSON file looks like this:
410  // {
411  //   "homepage": "http://www.cnn.com",
412  //   "some_directory": "/usr/local/",
413  //   "tabs": {
414  //     "new_windows_in_tabs": true,
415  //     "max_tabs": 20
416  //   }
417  // }
418
419  RunBasicJsonPrefStoreTest(
420      pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
421}
422
423TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
424  ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
425                             temp_dir_.path().AppendASCII("write.json")));
426
427  // Test that the persistent value can be loaded.
428  base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
429  ASSERT_TRUE(PathExists(input_file));
430
431  scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter(
432      new InterceptingPrefFilter());
433  InterceptingPrefFilter* raw_intercepting_pref_filter_ =
434      intercepting_pref_filter.get();
435  scoped_refptr<JsonPrefStore> pref_store =
436      new JsonPrefStore(input_file,
437                        message_loop_.message_loop_proxy().get(),
438                        intercepting_pref_filter.PassAs<PrefFilter>());
439
440  MockPrefStoreObserver mock_observer;
441  pref_store->AddObserver(&mock_observer);
442
443  // Ownership of the |mock_error_delegate| is handed to the |pref_store| below.
444  MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
445
446  {
447    pref_store->ReadPrefsAsync(mock_error_delegate);
448
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();
453
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));
458  }
459
460  {
461    EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
462    // EXPECT_CALL(*mock_error_delegate,
463    //             OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
464
465    raw_intercepting_pref_filter_->ReleasePrefs();
466
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));
471  }
472
473  pref_store->RemoveObserver(&mock_observer);
474
475  // The JSON file looks like this:
476  // {
477  //   "homepage": "http://www.cnn.com",
478  //   "some_directory": "/usr/local/",
479  //   "tabs": {
480  //     "new_windows_in_tabs": true,
481  //     "max_tabs": 20
482  //   }
483  // }
484
485  RunBasicJsonPrefStoreTest(
486      pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
487}
488
489TEST_F(JsonPrefStoreTest, AlternateFile) {
490  ASSERT_TRUE(
491      base::CopyFile(data_dir_.AppendASCII("read.json"),
492                     temp_dir_.path().AppendASCII("alternate.json")));
493
494  // Test that the alternate file is moved to the main file and read as-is from
495  // there.
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 = new JsonPrefStore(
502      input_file,
503      alternate_input_file,
504      message_loop_.message_loop_proxy().get(),
505      scoped_ptr<PrefFilter>());
506
507  ASSERT_FALSE(PathExists(input_file));
508  ASSERT_TRUE(PathExists(alternate_input_file));
509  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
510
511  ASSERT_TRUE(PathExists(input_file));
512  ASSERT_FALSE(PathExists(alternate_input_file));
513
514  EXPECT_FALSE(pref_store->ReadOnly());
515  EXPECT_TRUE(pref_store->IsInitializationComplete());
516
517  // The JSON file looks like this:
518  // {
519  //   "homepage": "http://www.cnn.com",
520  //   "some_directory": "/usr/local/",
521  //   "tabs": {
522  //     "new_windows_in_tabs": true,
523  //     "max_tabs": 20
524  //   }
525  // }
526
527  RunBasicJsonPrefStoreTest(
528      pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
529}
530
531TEST_F(JsonPrefStoreTest, AlternateFileIgnoredWhenMainFileExists) {
532  ASSERT_TRUE(
533      base::CopyFile(data_dir_.AppendASCII("read.json"),
534                     temp_dir_.path().AppendASCII("write.json")));
535  ASSERT_TRUE(
536      base::CopyFile(data_dir_.AppendASCII("invalid.json"),
537                     temp_dir_.path().AppendASCII("alternate.json")));
538
539  // Test that the alternate file is ignored and that the read occurs from the
540  // existing main file. There is no attempt at even deleting the alternate
541  // file as this scenario should never happen in normal user-data-dirs.
542  base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
543  base::FilePath alternate_input_file =
544      temp_dir_.path().AppendASCII("alternate.json");
545  ASSERT_TRUE(PathExists(input_file));
546  ASSERT_TRUE(PathExists(alternate_input_file));
547  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
548      input_file,
549      alternate_input_file,
550      message_loop_.message_loop_proxy().get(),
551      scoped_ptr<PrefFilter>());
552
553  ASSERT_TRUE(PathExists(input_file));
554  ASSERT_TRUE(PathExists(alternate_input_file));
555  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
556
557  ASSERT_TRUE(PathExists(input_file));
558  ASSERT_TRUE(PathExists(alternate_input_file));
559
560  EXPECT_FALSE(pref_store->ReadOnly());
561  EXPECT_TRUE(pref_store->IsInitializationComplete());
562
563  // The JSON file looks like this:
564  // {
565  //   "homepage": "http://www.cnn.com",
566  //   "some_directory": "/usr/local/",
567  //   "tabs": {
568  //     "new_windows_in_tabs": true,
569  //     "max_tabs": 20
570  //   }
571  // }
572
573  RunBasicJsonPrefStoreTest(
574      pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
575}
576
577TEST_F(JsonPrefStoreTest, AlternateFileDNE) {
578  ASSERT_TRUE(
579      base::CopyFile(data_dir_.AppendASCII("read.json"),
580                     temp_dir_.path().AppendASCII("write.json")));
581
582  // Test that the basic read works fine when an alternate file is specified but
583  // does not exist.
584  base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
585  base::FilePath alternate_input_file =
586      temp_dir_.path().AppendASCII("alternate.json");
587  ASSERT_TRUE(PathExists(input_file));
588  ASSERT_FALSE(PathExists(alternate_input_file));
589  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
590      input_file,
591      alternate_input_file,
592      message_loop_.message_loop_proxy().get(),
593      scoped_ptr<PrefFilter>());
594
595  ASSERT_TRUE(PathExists(input_file));
596  ASSERT_FALSE(PathExists(alternate_input_file));
597  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
598
599  ASSERT_TRUE(PathExists(input_file));
600  ASSERT_FALSE(PathExists(alternate_input_file));
601
602  EXPECT_FALSE(pref_store->ReadOnly());
603  EXPECT_TRUE(pref_store->IsInitializationComplete());
604
605  // The JSON file looks like this:
606  // {
607  //   "homepage": "http://www.cnn.com",
608  //   "some_directory": "/usr/local/",
609  //   "tabs": {
610  //     "new_windows_in_tabs": true,
611  //     "max_tabs": 20
612  //   }
613  // }
614
615  RunBasicJsonPrefStoreTest(
616      pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
617}
618
619TEST_F(JsonPrefStoreTest, BasicAsyncWithAlternateFile) {
620  ASSERT_TRUE(
621      base::CopyFile(data_dir_.AppendASCII("read.json"),
622                     temp_dir_.path().AppendASCII("alternate.json")));
623
624  // Test that the alternate file is moved to the main file and read as-is from
625  // there even when the read is made asynchronously.
626  base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
627  base::FilePath alternate_input_file =
628      temp_dir_.path().AppendASCII("alternate.json");
629  ASSERT_FALSE(PathExists(input_file));
630  ASSERT_TRUE(PathExists(alternate_input_file));
631  scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
632      input_file,
633      alternate_input_file,
634      message_loop_.message_loop_proxy().get(),
635      scoped_ptr<PrefFilter>());
636
637  ASSERT_FALSE(PathExists(input_file));
638  ASSERT_TRUE(PathExists(alternate_input_file));
639
640  {
641    MockPrefStoreObserver mock_observer;
642    pref_store->AddObserver(&mock_observer);
643
644    MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
645    pref_store->ReadPrefsAsync(mock_error_delegate);
646
647    EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
648    EXPECT_CALL(*mock_error_delegate,
649                OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
650    RunLoop().RunUntilIdle();
651    pref_store->RemoveObserver(&mock_observer);
652
653    EXPECT_FALSE(pref_store->ReadOnly());
654    EXPECT_TRUE(pref_store->IsInitializationComplete());
655  }
656
657  ASSERT_TRUE(PathExists(input_file));
658  ASSERT_FALSE(PathExists(alternate_input_file));
659
660  // The JSON file looks like this:
661  // {
662  //   "homepage": "http://www.cnn.com",
663  //   "some_directory": "/usr/local/",
664  //   "tabs": {
665  //     "new_windows_in_tabs": true,
666  //     "max_tabs": 20
667  //   }
668  // }
669
670  RunBasicJsonPrefStoreTest(
671      pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
672}
673
674}  // namespace base
675