Home | History | Annotate | Download | only in prefs
      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/file_util.h"
      8 #include "base/files/scoped_temp_dir.h"
      9 #include "base/memory/ref_counted.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/path_service.h"
     12 #include "base/run_loop.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/strings/string_util.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "base/threading/sequenced_worker_pool.h"
     17 #include "base/threading/thread.h"
     18 #include "base/values.h"
     19 #include "testing/gmock/include/gmock/gmock.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 
     22 namespace base {
     23 namespace {
     24 
     25 const char kHomePage[] = "homepage";
     26 
     27 class MockPrefStoreObserver : public PrefStore::Observer {
     28  public:
     29   MOCK_METHOD1(OnPrefValueChanged, void (const std::string&));
     30   MOCK_METHOD1(OnInitializationCompleted, void (bool));
     31 };
     32 
     33 class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
     34  public:
     35   MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError));
     36 };
     37 
     38 }  // namespace
     39 
     40 class JsonPrefStoreTest : public testing::Test {
     41  protected:
     42   virtual void SetUp() OVERRIDE {
     43     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     44 
     45     ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir_));
     46     data_dir_ = data_dir_.AppendASCII("prefs");
     47     ASSERT_TRUE(PathExists(data_dir_));
     48   }
     49 
     50   // The path to temporary directory used to contain the test operations.
     51   base::ScopedTempDir temp_dir_;
     52   // The path to the directory where the test data is stored.
     53   base::FilePath data_dir_;
     54   // A message loop that we can use as the file thread message loop.
     55   MessageLoop message_loop_;
     56 };
     57 
     58 // Test fallback behavior for a nonexistent file.
     59 TEST_F(JsonPrefStoreTest, NonExistentFile) {
     60   base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
     61   ASSERT_FALSE(PathExists(bogus_input_file));
     62   scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
     63       bogus_input_file, message_loop_.message_loop_proxy().get());
     64   EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
     65             pref_store->ReadPrefs());
     66   EXPECT_FALSE(pref_store->ReadOnly());
     67 }
     68 
     69 // Test fallback behavior for an invalid file.
     70 TEST_F(JsonPrefStoreTest, InvalidFile) {
     71   base::FilePath invalid_file_original = data_dir_.AppendASCII("invalid.json");
     72   base::FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json");
     73   ASSERT_TRUE(base::CopyFile(invalid_file_original, invalid_file));
     74   scoped_refptr<JsonPrefStore> pref_store =
     75       new JsonPrefStore(invalid_file, message_loop_.message_loop_proxy().get());
     76   EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
     77             pref_store->ReadPrefs());
     78   EXPECT_FALSE(pref_store->ReadOnly());
     79 
     80   // The file should have been moved aside.
     81   EXPECT_FALSE(PathExists(invalid_file));
     82   base::FilePath moved_aside = temp_dir_.path().AppendASCII("invalid.bad");
     83   EXPECT_TRUE(PathExists(moved_aside));
     84   EXPECT_TRUE(TextContentsEqual(invalid_file_original, moved_aside));
     85 }
     86 
     87 // This function is used to avoid code duplication while testing synchronous and
     88 // asynchronous version of the JsonPrefStore loading.
     89 void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
     90                                const base::FilePath& output_file,
     91                                const base::FilePath& golden_output_file) {
     92   const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
     93   const char kMaxTabs[] = "tabs.max_tabs";
     94   const char kLongIntPref[] = "long_int.pref";
     95 
     96   std::string cnn("http://www.cnn.com");
     97 
     98   const Value* actual;
     99   EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual));
    100   std::string string_value;
    101   EXPECT_TRUE(actual->GetAsString(&string_value));
    102   EXPECT_EQ(cnn, string_value);
    103 
    104   const char kSomeDirectory[] = "some_directory";
    105 
    106   EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
    107   base::FilePath::StringType path;
    108   EXPECT_TRUE(actual->GetAsString(&path));
    109   EXPECT_EQ(base::FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path);
    110   base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/"));
    111 
    112   pref_store->SetValue(kSomeDirectory, new StringValue(some_path.value()));
    113   EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
    114   EXPECT_TRUE(actual->GetAsString(&path));
    115   EXPECT_EQ(some_path.value(), path);
    116 
    117   // Test reading some other data types from sub-dictionaries.
    118   EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
    119   bool boolean = false;
    120   EXPECT_TRUE(actual->GetAsBoolean(&boolean));
    121   EXPECT_TRUE(boolean);
    122 
    123   pref_store->SetValue(kNewWindowsInTabs, new FundamentalValue(false));
    124   EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
    125   EXPECT_TRUE(actual->GetAsBoolean(&boolean));
    126   EXPECT_FALSE(boolean);
    127 
    128   EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
    129   int integer = 0;
    130   EXPECT_TRUE(actual->GetAsInteger(&integer));
    131   EXPECT_EQ(20, integer);
    132   pref_store->SetValue(kMaxTabs, new FundamentalValue(10));
    133   EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
    134   EXPECT_TRUE(actual->GetAsInteger(&integer));
    135   EXPECT_EQ(10, integer);
    136 
    137   pref_store->SetValue(kLongIntPref,
    138                        new StringValue(base::Int64ToString(214748364842LL)));
    139   EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual));
    140   EXPECT_TRUE(actual->GetAsString(&string_value));
    141   int64 value;
    142   base::StringToInt64(string_value, &value);
    143   EXPECT_EQ(214748364842LL, value);
    144 
    145   // Serialize and compare to expected output.
    146   ASSERT_TRUE(PathExists(golden_output_file));
    147   pref_store->CommitPendingWrite();
    148   RunLoop().RunUntilIdle();
    149   EXPECT_TRUE(TextContentsEqual(golden_output_file, output_file));
    150   ASSERT_TRUE(base::DeleteFile(output_file, false));
    151 }
    152 
    153 TEST_F(JsonPrefStoreTest, Basic) {
    154   ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
    155                                   temp_dir_.path().AppendASCII("write.json")));
    156 
    157   // Test that the persistent value can be loaded.
    158   base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
    159   ASSERT_TRUE(PathExists(input_file));
    160   scoped_refptr<JsonPrefStore> pref_store =
    161       new JsonPrefStore(input_file, message_loop_.message_loop_proxy().get());
    162   ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
    163   ASSERT_FALSE(pref_store->ReadOnly());
    164 
    165   // The JSON file looks like this:
    166   // {
    167   //   "homepage": "http://www.cnn.com",
    168   //   "some_directory": "/usr/local/",
    169   //   "tabs": {
    170   //     "new_windows_in_tabs": true,
    171   //     "max_tabs": 20
    172   //   }
    173   // }
    174 
    175   RunBasicJsonPrefStoreTest(
    176       pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
    177 }
    178 
    179 TEST_F(JsonPrefStoreTest, BasicAsync) {
    180   ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
    181                                   temp_dir_.path().AppendASCII("write.json")));
    182 
    183   // Test that the persistent value can be loaded.
    184   base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
    185   ASSERT_TRUE(PathExists(input_file));
    186   scoped_refptr<JsonPrefStore> pref_store =
    187       new JsonPrefStore(input_file, message_loop_.message_loop_proxy().get());
    188 
    189   {
    190     MockPrefStoreObserver mock_observer;
    191     pref_store->AddObserver(&mock_observer);
    192 
    193     MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
    194     pref_store->ReadPrefsAsync(mock_error_delegate);
    195 
    196     EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
    197     EXPECT_CALL(*mock_error_delegate,
    198                 OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
    199     RunLoop().RunUntilIdle();
    200     pref_store->RemoveObserver(&mock_observer);
    201 
    202     ASSERT_FALSE(pref_store->ReadOnly());
    203   }
    204 
    205   // The JSON file looks like this:
    206   // {
    207   //   "homepage": "http://www.cnn.com",
    208   //   "some_directory": "/usr/local/",
    209   //   "tabs": {
    210   //     "new_windows_in_tabs": true,
    211   //     "max_tabs": 20
    212   //   }
    213   // }
    214 
    215   RunBasicJsonPrefStoreTest(
    216       pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
    217 }
    218 
    219 TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
    220   FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json");
    221 
    222   scoped_refptr<JsonPrefStore> pref_store =
    223       new JsonPrefStore(pref_file, message_loop_.message_loop_proxy());
    224 
    225   // Set some keys with empty values.
    226   pref_store->SetValue("list", new base::ListValue);
    227   pref_store->SetValue("dict", new base::DictionaryValue);
    228 
    229   // Write to file.
    230   pref_store->CommitPendingWrite();
    231   MessageLoop::current()->RunUntilIdle();
    232 
    233   // Reload.
    234   pref_store = new JsonPrefStore(pref_file, message_loop_.message_loop_proxy());
    235   ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
    236   ASSERT_FALSE(pref_store->ReadOnly());
    237 
    238   // Check values.
    239   const Value* result = NULL;
    240   EXPECT_TRUE(pref_store->GetValue("list", &result));
    241   EXPECT_TRUE(ListValue().Equals(result));
    242   EXPECT_TRUE(pref_store->GetValue("dict", &result));
    243   EXPECT_TRUE(DictionaryValue().Equals(result));
    244 }
    245 
    246 // Tests asynchronous reading of the file when there is no file.
    247 TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
    248   base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
    249   ASSERT_FALSE(PathExists(bogus_input_file));
    250   scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
    251       bogus_input_file, message_loop_.message_loop_proxy().get());
    252   MockPrefStoreObserver mock_observer;
    253   pref_store->AddObserver(&mock_observer);
    254 
    255   MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate;
    256   pref_store->ReadPrefsAsync(mock_error_delegate);
    257 
    258   EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
    259   EXPECT_CALL(*mock_error_delegate,
    260               OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1);
    261   RunLoop().RunUntilIdle();
    262   pref_store->RemoveObserver(&mock_observer);
    263 
    264   EXPECT_FALSE(pref_store->ReadOnly());
    265 }
    266 
    267 }  // namespace base
    268