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"
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/prefs/pref_filter.h"
13 #include "base/run_loop.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/threading/sequenced_worker_pool.h"
18 #include "base/threading/thread.h"
19 #include "base/values.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
26 const char kHomePage
[] = "homepage";
28 class MockPrefStoreObserver
: public PrefStore::Observer
{
30 MOCK_METHOD1(OnPrefValueChanged
, void (const std::string
&));
31 MOCK_METHOD1(OnInitializationCompleted
, void (bool));
34 class MockReadErrorDelegate
: public PersistentPrefStore::ReadErrorDelegate
{
36 MOCK_METHOD1(OnError
, void(PersistentPrefStore::PrefReadError
));
41 class JsonPrefStoreTest
: public testing::Test
{
43 virtual void SetUp() OVERRIDE
{
44 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
46 ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA
, &data_dir_
));
47 data_dir_
= data_dir_
.AppendASCII("prefs");
48 ASSERT_TRUE(PathExists(data_dir_
));
51 // The path to temporary directory used to contain the test operations.
52 base::ScopedTempDir temp_dir_
;
53 // The path to the directory where the test data is stored.
54 base::FilePath data_dir_
;
55 // A message loop that we can use as the file thread message loop.
56 MessageLoop message_loop_
;
59 // Test fallback behavior for a nonexistent file.
60 TEST_F(JsonPrefStoreTest
, NonExistentFile
) {
61 base::FilePath bogus_input_file
= data_dir_
.AppendASCII("read.txt");
62 ASSERT_FALSE(PathExists(bogus_input_file
));
63 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
65 message_loop_
.message_loop_proxy().get(),
66 scoped_ptr
<PrefFilter
>());
67 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE
,
68 pref_store
->ReadPrefs());
69 EXPECT_FALSE(pref_store
->ReadOnly());
72 // Test fallback behavior for an invalid file.
73 TEST_F(JsonPrefStoreTest
, InvalidFile
) {
74 base::FilePath invalid_file_original
= data_dir_
.AppendASCII("invalid.json");
75 base::FilePath invalid_file
= temp_dir_
.path().AppendASCII("invalid.json");
76 ASSERT_TRUE(base::CopyFile(invalid_file_original
, invalid_file
));
77 scoped_refptr
<JsonPrefStore
> pref_store
=
78 new JsonPrefStore(invalid_file
,
79 message_loop_
.message_loop_proxy().get(),
80 scoped_ptr
<PrefFilter
>());
81 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE
,
82 pref_store
->ReadPrefs());
83 EXPECT_FALSE(pref_store
->ReadOnly());
85 // The file should have been moved aside.
86 EXPECT_FALSE(PathExists(invalid_file
));
87 base::FilePath moved_aside
= temp_dir_
.path().AppendASCII("invalid.bad");
88 EXPECT_TRUE(PathExists(moved_aside
));
89 EXPECT_TRUE(TextContentsEqual(invalid_file_original
, moved_aside
));
92 // This function is used to avoid code duplication while testing synchronous and
93 // asynchronous version of the JsonPrefStore loading.
94 void RunBasicJsonPrefStoreTest(JsonPrefStore
* pref_store
,
95 const base::FilePath
& output_file
,
96 const base::FilePath
& golden_output_file
) {
97 const char kNewWindowsInTabs
[] = "tabs.new_windows_in_tabs";
98 const char kMaxTabs
[] = "tabs.max_tabs";
99 const char kLongIntPref
[] = "long_int.pref";
101 std::string
cnn("http://www.cnn.com");
104 EXPECT_TRUE(pref_store
->GetValue(kHomePage
, &actual
));
105 std::string string_value
;
106 EXPECT_TRUE(actual
->GetAsString(&string_value
));
107 EXPECT_EQ(cnn
, string_value
);
109 const char kSomeDirectory
[] = "some_directory";
111 EXPECT_TRUE(pref_store
->GetValue(kSomeDirectory
, &actual
));
112 base::FilePath::StringType path
;
113 EXPECT_TRUE(actual
->GetAsString(&path
));
114 EXPECT_EQ(base::FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path
);
115 base::FilePath
some_path(FILE_PATH_LITERAL("/usr/sbin/"));
117 pref_store
->SetValue(kSomeDirectory
, new StringValue(some_path
.value()));
118 EXPECT_TRUE(pref_store
->GetValue(kSomeDirectory
, &actual
));
119 EXPECT_TRUE(actual
->GetAsString(&path
));
120 EXPECT_EQ(some_path
.value(), path
);
122 // Test reading some other data types from sub-dictionaries.
123 EXPECT_TRUE(pref_store
->GetValue(kNewWindowsInTabs
, &actual
));
124 bool boolean
= false;
125 EXPECT_TRUE(actual
->GetAsBoolean(&boolean
));
126 EXPECT_TRUE(boolean
);
128 pref_store
->SetValue(kNewWindowsInTabs
, new FundamentalValue(false));
129 EXPECT_TRUE(pref_store
->GetValue(kNewWindowsInTabs
, &actual
));
130 EXPECT_TRUE(actual
->GetAsBoolean(&boolean
));
131 EXPECT_FALSE(boolean
);
133 EXPECT_TRUE(pref_store
->GetValue(kMaxTabs
, &actual
));
135 EXPECT_TRUE(actual
->GetAsInteger(&integer
));
136 EXPECT_EQ(20, integer
);
137 pref_store
->SetValue(kMaxTabs
, new FundamentalValue(10));
138 EXPECT_TRUE(pref_store
->GetValue(kMaxTabs
, &actual
));
139 EXPECT_TRUE(actual
->GetAsInteger(&integer
));
140 EXPECT_EQ(10, integer
);
142 pref_store
->SetValue(kLongIntPref
,
143 new StringValue(base::Int64ToString(214748364842LL)));
144 EXPECT_TRUE(pref_store
->GetValue(kLongIntPref
, &actual
));
145 EXPECT_TRUE(actual
->GetAsString(&string_value
));
147 base::StringToInt64(string_value
, &value
);
148 EXPECT_EQ(214748364842LL, value
);
150 // Serialize and compare to expected output.
151 ASSERT_TRUE(PathExists(golden_output_file
));
152 pref_store
->CommitPendingWrite();
153 RunLoop().RunUntilIdle();
154 EXPECT_TRUE(TextContentsEqual(golden_output_file
, output_file
));
155 ASSERT_TRUE(base::DeleteFile(output_file
, false));
158 TEST_F(JsonPrefStoreTest
, Basic
) {
159 ASSERT_TRUE(base::CopyFile(data_dir_
.AppendASCII("read.json"),
160 temp_dir_
.path().AppendASCII("write.json")));
162 // Test that the persistent value can be loaded.
163 base::FilePath input_file
= temp_dir_
.path().AppendASCII("write.json");
164 ASSERT_TRUE(PathExists(input_file
));
165 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
167 message_loop_
.message_loop_proxy().get(),
168 scoped_ptr
<PrefFilter
>());
169 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE
, pref_store
->ReadPrefs());
170 ASSERT_FALSE(pref_store
->ReadOnly());
172 // The JSON file looks like this:
174 // "homepage": "http://www.cnn.com",
175 // "some_directory": "/usr/local/",
177 // "new_windows_in_tabs": true,
182 RunBasicJsonPrefStoreTest(
183 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
186 TEST_F(JsonPrefStoreTest
, BasicAsync
) {
187 ASSERT_TRUE(base::CopyFile(data_dir_
.AppendASCII("read.json"),
188 temp_dir_
.path().AppendASCII("write.json")));
190 // Test that the persistent value can be loaded.
191 base::FilePath input_file
= temp_dir_
.path().AppendASCII("write.json");
192 ASSERT_TRUE(PathExists(input_file
));
193 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
195 message_loop_
.message_loop_proxy().get(),
196 scoped_ptr
<PrefFilter
>());
199 MockPrefStoreObserver mock_observer
;
200 pref_store
->AddObserver(&mock_observer
);
202 MockReadErrorDelegate
* mock_error_delegate
= new MockReadErrorDelegate
;
203 pref_store
->ReadPrefsAsync(mock_error_delegate
);
205 EXPECT_CALL(mock_observer
, OnInitializationCompleted(true)).Times(1);
206 EXPECT_CALL(*mock_error_delegate
,
207 OnError(PersistentPrefStore::PREF_READ_ERROR_NONE
)).Times(0);
208 RunLoop().RunUntilIdle();
209 pref_store
->RemoveObserver(&mock_observer
);
211 ASSERT_FALSE(pref_store
->ReadOnly());
214 // The JSON file looks like this:
216 // "homepage": "http://www.cnn.com",
217 // "some_directory": "/usr/local/",
219 // "new_windows_in_tabs": true,
224 RunBasicJsonPrefStoreTest(
225 pref_store
.get(), input_file
, data_dir_
.AppendASCII("write.golden.json"));
228 TEST_F(JsonPrefStoreTest
, PreserveEmptyValues
) {
229 FilePath pref_file
= temp_dir_
.path().AppendASCII("empty_values.json");
231 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
233 message_loop_
.message_loop_proxy(),
234 scoped_ptr
<PrefFilter
>());
236 // Set some keys with empty values.
237 pref_store
->SetValue("list", new base::ListValue
);
238 pref_store
->SetValue("dict", new base::DictionaryValue
);
241 pref_store
->CommitPendingWrite();
242 MessageLoop::current()->RunUntilIdle();
245 pref_store
= new JsonPrefStore(
247 message_loop_
.message_loop_proxy(),
248 scoped_ptr
<PrefFilter
>());
249 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE
, pref_store
->ReadPrefs());
250 ASSERT_FALSE(pref_store
->ReadOnly());
253 const Value
* result
= NULL
;
254 EXPECT_TRUE(pref_store
->GetValue("list", &result
));
255 EXPECT_TRUE(ListValue().Equals(result
));
256 EXPECT_TRUE(pref_store
->GetValue("dict", &result
));
257 EXPECT_TRUE(DictionaryValue().Equals(result
));
260 // This test is just documenting some potentially non-obvious behavior. It
261 // shouldn't be taken as normative.
262 TEST_F(JsonPrefStoreTest
, RemoveClearsEmptyParent
) {
263 FilePath pref_file
= temp_dir_
.path().AppendASCII("empty_values.json");
265 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
267 message_loop_
.message_loop_proxy(),
268 scoped_ptr
<PrefFilter
>());
270 base::DictionaryValue
* dict
= new base::DictionaryValue
;
271 dict
->SetString("key", "value");
272 pref_store
->SetValue("dict", dict
);
274 pref_store
->RemoveValue("dict.key");
276 const base::Value
* retrieved_dict
= NULL
;
277 bool has_dict
= pref_store
->GetValue("dict", &retrieved_dict
);
278 EXPECT_FALSE(has_dict
);
281 // Tests asynchronous reading of the file when there is no file.
282 TEST_F(JsonPrefStoreTest
, AsyncNonExistingFile
) {
283 base::FilePath bogus_input_file
= data_dir_
.AppendASCII("read.txt");
284 ASSERT_FALSE(PathExists(bogus_input_file
));
285 scoped_refptr
<JsonPrefStore
> pref_store
= new JsonPrefStore(
287 message_loop_
.message_loop_proxy().get(),
288 scoped_ptr
<PrefFilter
>());
289 MockPrefStoreObserver mock_observer
;
290 pref_store
->AddObserver(&mock_observer
);
292 MockReadErrorDelegate
*mock_error_delegate
= new MockReadErrorDelegate
;
293 pref_store
->ReadPrefsAsync(mock_error_delegate
);
295 EXPECT_CALL(mock_observer
, OnInitializationCompleted(true)).Times(1);
296 EXPECT_CALL(*mock_error_delegate
,
297 OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE
)).Times(1);
298 RunLoop().RunUntilIdle();
299 pref_store
->RemoveObserver(&mock_observer
);
301 EXPECT_FALSE(pref_store
->ReadOnly());