Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / base / prefs / json_pref_store_unittest.cc
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
25 namespace base {
26 namespace {
27
28 const 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.
32 class 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       const base::DictionaryValue* pref_store_contents) OVERRIDE {}
44
45   bool has_intercepted_prefs() const { return intercepted_prefs_ != NULL; }
46
47   // Finalize an intercepted read, handing |intercept_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
58 InterceptingPrefFilter::InterceptingPrefFilter() {}
59 InterceptingPrefFilter::~InterceptingPrefFilter() {}
60
61 void 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
68 void 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
74 class MockPrefStoreObserver : public PrefStore::Observer {
75  public:
76   MOCK_METHOD1(OnPrefValueChanged, void (const std::string&));
77   MOCK_METHOD1(OnInitializationCompleted, void (bool));
78 };
79
80 class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
81  public:
82   MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError));
83 };
84
85 }  // namespace
86
87 class 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.
113 TEST_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 an invalid file.
126 TEST_F(JsonPrefStoreTest, InvalidFile) {
127   base::FilePath invalid_file_original = data_dir_.AppendASCII("invalid.json");
128   base::FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json");
129   ASSERT_TRUE(base::CopyFile(invalid_file_original, invalid_file));
130   scoped_refptr<JsonPrefStore> pref_store =
131       new JsonPrefStore(invalid_file,
132                         message_loop_.message_loop_proxy().get(),
133                         scoped_ptr<PrefFilter>());
134   EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
135             pref_store->ReadPrefs());
136   EXPECT_FALSE(pref_store->ReadOnly());
137
138   // The file should have been moved aside.
139   EXPECT_FALSE(PathExists(invalid_file));
140   base::FilePath moved_aside = temp_dir_.path().AppendASCII("invalid.bad");
141   EXPECT_TRUE(PathExists(moved_aside));
142   EXPECT_TRUE(TextContentsEqual(invalid_file_original, moved_aside));
143 }
144
145 // This function is used to avoid code duplication while testing synchronous and
146 // asynchronous version of the JsonPrefStore loading.
147 void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
148                                const base::FilePath& output_file,
149                                const base::FilePath& golden_output_file) {
150   const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
151   const char kMaxTabs[] = "tabs.max_tabs";
152   const char kLongIntPref[] = "long_int.pref";
153
154   std::string cnn("http://www.cnn.com");
155
156   const Value* actual;
157   EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual));
158   std::string string_value;
159   EXPECT_TRUE(actual->GetAsString(&string_value));
160   EXPECT_EQ(cnn, string_value);
161
162   const char kSomeDirectory[] = "some_directory";
163
164   EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
165   base::FilePath::StringType path;
166   EXPECT_TRUE(actual->GetAsString(&path));
167   EXPECT_EQ(base::FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path);
168   base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/"));
169
170   pref_store->SetValue(kSomeDirectory, new StringValue(some_path.value()));
171   EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
172   EXPECT_TRUE(actual->GetAsString(&path));
173   EXPECT_EQ(some_path.value(), path);
174
175   // Test reading some other data types from sub-dictionaries.
176   EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
177   bool boolean = false;
178   EXPECT_TRUE(actual->GetAsBoolean(&boolean));
179   EXPECT_TRUE(boolean);
180
181   pref_store->SetValue(kNewWindowsInTabs, new FundamentalValue(false));
182   EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
183   EXPECT_TRUE(actual->GetAsBoolean(&boolean));
184   EXPECT_FALSE(boolean);
185
186   EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
187   int integer = 0;
188   EXPECT_TRUE(actual->GetAsInteger(&integer));
189   EXPECT_EQ(20, integer);
190   pref_store->SetValue(kMaxTabs, new FundamentalValue(10));
191   EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
192   EXPECT_TRUE(actual->GetAsInteger(&integer));
193   EXPECT_EQ(10, integer);
194
195   pref_store->SetValue(kLongIntPref,
196                        new StringValue(base::Int64ToString(214748364842LL)));
197   EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual));
198   EXPECT_TRUE(actual->GetAsString(&string_value));
199   int64 value;
200   base::StringToInt64(string_value, &value);
201   EXPECT_EQ(214748364842LL, value);
202
203   // Serialize and compare to expected output.
204   ASSERT_TRUE(PathExists(golden_output_file));
205   pref_store->CommitPendingWrite();
206   RunLoop().RunUntilIdle();
207   EXPECT_TRUE(TextContentsEqual(golden_output_file, output_file));
208   ASSERT_TRUE(base::DeleteFile(output_file, false));
209 }
210
211 TEST_F(JsonPrefStoreTest, Basic) {
212   ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
213                              temp_dir_.path().AppendASCII("write.json")));
214
215   // Test that the persistent value can be loaded.
216   base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
217   ASSERT_TRUE(PathExists(input_file));
218   scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
219       input_file,
220       message_loop_.message_loop_proxy().get(),
221       scoped_ptr<PrefFilter>());
222   ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
223   EXPECT_FALSE(pref_store->ReadOnly());
224   EXPECT_TRUE(pref_store->IsInitializationComplete());
225
226   // The JSON file looks like this:
227   // {
228   //   "homepage": "http://www.cnn.com",
229   //   "some_directory": "/usr/local/",
230   //   "tabs": {
231   //     "new_windows_in_tabs": true,
232   //     "max_tabs": 20
233   //   }
234   // }
235
236   RunBasicJsonPrefStoreTest(
237       pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
238 }
239
240 TEST_F(JsonPrefStoreTest, BasicAsync) {
241   ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
242                              temp_dir_.path().AppendASCII("write.json")));
243
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,
249       message_loop_.message_loop_proxy().get(),
250       scoped_ptr<PrefFilter>());
251
252   {
253     MockPrefStoreObserver mock_observer;
254     pref_store->AddObserver(&mock_observer);
255
256     MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
257     pref_store->ReadPrefsAsync(mock_error_delegate);
258
259     EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
260     EXPECT_CALL(*mock_error_delegate,
261                 OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
262     RunLoop().RunUntilIdle();
263     pref_store->RemoveObserver(&mock_observer);
264
265     EXPECT_FALSE(pref_store->ReadOnly());
266     EXPECT_TRUE(pref_store->IsInitializationComplete());
267   }
268
269   // The JSON file looks like this:
270   // {
271   //   "homepage": "http://www.cnn.com",
272   //   "some_directory": "/usr/local/",
273   //   "tabs": {
274   //     "new_windows_in_tabs": true,
275   //     "max_tabs": 20
276   //   }
277   // }
278
279   RunBasicJsonPrefStoreTest(
280       pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
281 }
282
283 TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
284   FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json");
285
286   scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
287       pref_file,
288       message_loop_.message_loop_proxy(),
289       scoped_ptr<PrefFilter>());
290
291   // Set some keys with empty values.
292   pref_store->SetValue("list", new base::ListValue);
293   pref_store->SetValue("dict", new base::DictionaryValue);
294
295   // Write to file.
296   pref_store->CommitPendingWrite();
297   MessageLoop::current()->RunUntilIdle();
298
299   // Reload.
300   pref_store = new JsonPrefStore(
301       pref_file,
302       message_loop_.message_loop_proxy(),
303       scoped_ptr<PrefFilter>());
304   ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
305   ASSERT_FALSE(pref_store->ReadOnly());
306
307   // Check values.
308   const Value* result = NULL;
309   EXPECT_TRUE(pref_store->GetValue("list", &result));
310   EXPECT_TRUE(ListValue().Equals(result));
311   EXPECT_TRUE(pref_store->GetValue("dict", &result));
312   EXPECT_TRUE(DictionaryValue().Equals(result));
313 }
314
315 // This test is just documenting some potentially non-obvious behavior. It
316 // shouldn't be taken as normative.
317 TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) {
318   FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json");
319
320   scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
321       pref_file,
322       message_loop_.message_loop_proxy(),
323       scoped_ptr<PrefFilter>());
324
325   base::DictionaryValue* dict = new base::DictionaryValue;
326   dict->SetString("key", "value");
327   pref_store->SetValue("dict", dict);
328
329   pref_store->RemoveValue("dict.key");
330
331   const base::Value* retrieved_dict = NULL;
332   bool has_dict = pref_store->GetValue("dict", &retrieved_dict);
333   EXPECT_FALSE(has_dict);
334 }
335
336 // Tests asynchronous reading of the file when there is no file.
337 TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
338   base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
339   ASSERT_FALSE(PathExists(bogus_input_file));
340   scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
341       bogus_input_file,
342       message_loop_.message_loop_proxy().get(),
343       scoped_ptr<PrefFilter>());
344   MockPrefStoreObserver mock_observer;
345   pref_store->AddObserver(&mock_observer);
346
347   MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate;
348   pref_store->ReadPrefsAsync(mock_error_delegate);
349
350   EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
351   EXPECT_CALL(*mock_error_delegate,
352               OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1);
353   RunLoop().RunUntilIdle();
354   pref_store->RemoveObserver(&mock_observer);
355
356   EXPECT_FALSE(pref_store->ReadOnly());
357 }
358
359 TEST_F(JsonPrefStoreTest, ReadWithInterceptor) {
360   ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
361                              temp_dir_.path().AppendASCII("write.json")));
362
363   // Test that the persistent value can be loaded.
364   base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
365   ASSERT_TRUE(PathExists(input_file));
366
367   scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter(
368       new InterceptingPrefFilter());
369   InterceptingPrefFilter* raw_intercepting_pref_filter_ =
370       intercepting_pref_filter.get();
371   scoped_refptr<JsonPrefStore> pref_store =
372       new JsonPrefStore(input_file,
373                         message_loop_.message_loop_proxy().get(),
374                         intercepting_pref_filter.PassAs<PrefFilter>());
375
376   ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE,
377             pref_store->ReadPrefs());
378   EXPECT_FALSE(pref_store->ReadOnly());
379
380   // The store shouldn't be considered initialized until the interceptor
381   // returns.
382   EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
383   EXPECT_FALSE(pref_store->IsInitializationComplete());
384   EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL));
385
386   raw_intercepting_pref_filter_->ReleasePrefs();
387
388   EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
389   EXPECT_TRUE(pref_store->IsInitializationComplete());
390   EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL));
391
392   // The JSON file looks like this:
393   // {
394   //   "homepage": "http://www.cnn.com",
395   //   "some_directory": "/usr/local/",
396   //   "tabs": {
397   //     "new_windows_in_tabs": true,
398   //     "max_tabs": 20
399   //   }
400   // }
401
402   RunBasicJsonPrefStoreTest(
403       pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
404 }
405
406 TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
407   ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
408                              temp_dir_.path().AppendASCII("write.json")));
409
410   // Test that the persistent value can be loaded.
411   base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
412   ASSERT_TRUE(PathExists(input_file));
413
414   scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter(
415       new InterceptingPrefFilter());
416   InterceptingPrefFilter* raw_intercepting_pref_filter_ =
417       intercepting_pref_filter.get();
418   scoped_refptr<JsonPrefStore> pref_store =
419       new JsonPrefStore(input_file,
420                         message_loop_.message_loop_proxy().get(),
421                         intercepting_pref_filter.PassAs<PrefFilter>());
422
423   MockPrefStoreObserver mock_observer;
424   pref_store->AddObserver(&mock_observer);
425
426   // Ownership of the |mock_error_delegate| is handed to the |pref_store| below.
427   MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
428
429   {
430     pref_store->ReadPrefsAsync(mock_error_delegate);
431
432     EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0);
433     // EXPECT_CALL(*mock_error_delegate,
434     //             OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
435     RunLoop().RunUntilIdle();
436
437     EXPECT_FALSE(pref_store->ReadOnly());
438     EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
439     EXPECT_FALSE(pref_store->IsInitializationComplete());
440     EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL));
441   }
442
443   {
444     EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
445     // EXPECT_CALL(*mock_error_delegate,
446     //             OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
447
448     raw_intercepting_pref_filter_->ReleasePrefs();
449
450     EXPECT_FALSE(pref_store->ReadOnly());
451     EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
452     EXPECT_TRUE(pref_store->IsInitializationComplete());
453     EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL));
454   }
455
456   pref_store->RemoveObserver(&mock_observer);
457
458   // The JSON file looks like this:
459   // {
460   //   "homepage": "http://www.cnn.com",
461   //   "some_directory": "/usr/local/",
462   //   "tabs": {
463   //     "new_windows_in_tabs": true,
464   //     "max_tabs": 20
465   //   }
466   // }
467
468   RunBasicJsonPrefStoreTest(
469       pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
470 }
471
472 }  // namespace base