1 // Copyright 2014 The Chromium Authors
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 "components/prefs/segregated_pref_store.h"
6
7 #include <memory>
8 #include <set>
9 #include <string>
10 #include <utility>
11
12 #include "base/functional/bind.h"
13 #include "base/functional/callback.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/run_loop.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/test/task_environment.h"
19 #include "base/values.h"
20 #include "components/prefs/persistent_pref_store.h"
21 #include "components/prefs/pref_name_set.h"
22 #include "components/prefs/pref_store_observer_mock.h"
23 #include "components/prefs/testing_pref_store.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 namespace {
27
28 const char kSelectedPref[] = "selected_pref";
29 const char kUnselectedPref[] = "unselected_pref";
30 const char kSharedPref[] = "shared_pref";
31
32 const char kValue1[] = "value1";
33 const char kValue2[] = "value2";
34
35 class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
36 public:
37 struct Data {
Data__anon3b84fe4b0111::MockReadErrorDelegate::Data38 Data(bool invoked_in, PersistentPrefStore::PrefReadError read_error_in)
39 : invoked(invoked_in), read_error(read_error_in) {}
40
41 bool invoked;
42 PersistentPrefStore::PrefReadError read_error;
43 };
44
MockReadErrorDelegate(Data * data)45 explicit MockReadErrorDelegate(Data* data) : data_(data) {
46 DCHECK(data_);
47 EXPECT_FALSE(data_->invoked);
48 }
49
50 // PersistentPrefStore::ReadErrorDelegate implementation
OnError(PersistentPrefStore::PrefReadError read_error)51 void OnError(PersistentPrefStore::PrefReadError read_error) override {
52 EXPECT_FALSE(data_->invoked);
53 data_->invoked = true;
54 data_->read_error = read_error;
55 }
56
57 private:
58 raw_ptr<Data> data_;
59 };
60
61 enum class CommitPendingWriteMode {
62 // Basic mode.
63 WITHOUT_CALLBACK,
64 // With reply callback.
65 WITH_CALLBACK,
66 // With synchronous notify callback (synchronous after the write -- shouldn't
67 // require pumping messages to observe).
68 WITH_SYNCHRONOUS_CALLBACK,
69 };
70
71 class SegregatedPrefStoreTest
72 : public testing::TestWithParam<CommitPendingWriteMode> {
73 public:
SegregatedPrefStoreTest()74 SegregatedPrefStoreTest()
75 : read_error_delegate_data_(false,
76 PersistentPrefStore::PREF_READ_ERROR_NONE),
77 read_error_delegate_(
78 new MockReadErrorDelegate(&read_error_delegate_data_)) {}
79
SetUp()80 void SetUp() override {
81 selected_store_ = new TestingPrefStore;
82 default_store_ = new TestingPrefStore;
83
84 selected_pref_names_.insert(kSelectedPref);
85 selected_pref_names_.insert(kSharedPref);
86 segregated_store_ = new SegregatedPrefStore(default_store_, selected_store_,
87 selected_pref_names_);
88 segregated_store_->AddObserver(&observer_);
89 }
90
TearDown()91 void TearDown() override { segregated_store_->RemoveObserver(&observer_); }
92
93 protected:
94 std::unique_ptr<PersistentPrefStore::ReadErrorDelegate>
GetReadErrorDelegate()95 GetReadErrorDelegate() {
96 EXPECT_TRUE(read_error_delegate_);
97 return std::move(read_error_delegate_);
98 }
99
100 base::test::TaskEnvironment task_environment_;
101
102 PrefStoreObserverMock observer_;
103
104 scoped_refptr<TestingPrefStore> default_store_;
105 scoped_refptr<TestingPrefStore> selected_store_;
106 scoped_refptr<SegregatedPrefStore> segregated_store_;
107
108 PrefNameSet selected_pref_names_;
109 MockReadErrorDelegate::Data read_error_delegate_data_;
110
111 private:
112 std::unique_ptr<MockReadErrorDelegate> read_error_delegate_;
113 };
114
115 } // namespace
116
TEST_P(SegregatedPrefStoreTest,StoreValues)117 TEST_P(SegregatedPrefStoreTest, StoreValues) {
118 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
119 segregated_store_->ReadPrefs());
120
121 // Properly stores new values.
122 segregated_store_->SetValue(kSelectedPref, base::Value(kValue1),
123 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
124 segregated_store_->SetValue(kUnselectedPref, base::Value(kValue2),
125 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
126
127 ASSERT_TRUE(selected_store_->GetValue(kSelectedPref, NULL));
128 ASSERT_FALSE(selected_store_->GetValue(kUnselectedPref, NULL));
129 ASSERT_FALSE(default_store_->GetValue(kSelectedPref, NULL));
130 ASSERT_TRUE(default_store_->GetValue(kUnselectedPref, NULL));
131
132 ASSERT_TRUE(segregated_store_->GetValue(kSelectedPref, NULL));
133 ASSERT_TRUE(segregated_store_->GetValue(kUnselectedPref, NULL));
134
135 ASSERT_FALSE(selected_store_->committed());
136 ASSERT_FALSE(default_store_->committed());
137
138 switch (GetParam()) {
139 case CommitPendingWriteMode::WITHOUT_CALLBACK: {
140 segregated_store_->CommitPendingWrite();
141 base::RunLoop().RunUntilIdle();
142 break;
143 }
144
145 case CommitPendingWriteMode::WITH_CALLBACK: {
146 base::RunLoop run_loop;
147 segregated_store_->CommitPendingWrite(run_loop.QuitClosure());
148 run_loop.Run();
149 break;
150 }
151
152 case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK: {
153 base::WaitableEvent written;
154 segregated_store_->CommitPendingWrite(
155 base::OnceClosure(),
156 base::BindOnce(&base::WaitableEvent::Signal, Unretained(&written)));
157 written.Wait();
158 break;
159 }
160 }
161
162 ASSERT_TRUE(selected_store_->committed());
163 ASSERT_TRUE(default_store_->committed());
164 }
165
TEST_F(SegregatedPrefStoreTest,ReadValues)166 TEST_F(SegregatedPrefStoreTest, ReadValues) {
167 selected_store_->SetValue(kSelectedPref, base::Value(kValue1),
168 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
169 default_store_->SetValue(kUnselectedPref, base::Value(kValue2),
170 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
171
172 // Works properly with values that are already there.
173 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
174 segregated_store_->ReadPrefs());
175 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
176 segregated_store_->GetReadError());
177
178 ASSERT_TRUE(selected_store_->GetValue(kSelectedPref, NULL));
179 ASSERT_FALSE(selected_store_->GetValue(kUnselectedPref, NULL));
180 ASSERT_FALSE(default_store_->GetValue(kSelectedPref, NULL));
181 ASSERT_TRUE(default_store_->GetValue(kUnselectedPref, NULL));
182
183 ASSERT_TRUE(segregated_store_->GetValue(kSelectedPref, NULL));
184 ASSERT_TRUE(segregated_store_->GetValue(kUnselectedPref, NULL));
185 }
186
TEST_F(SegregatedPrefStoreTest,RemoveValuesByPrefix)187 TEST_F(SegregatedPrefStoreTest, RemoveValuesByPrefix) {
188 const std::string subpref_name1 = kSelectedPref;
189 const std::string subpref_name2 = std::string(kSelectedPref) + "b";
190 const std::string other_name = kUnselectedPref;
191 const std::string prefix = kSelectedPref;
192
193 selected_store_->SetValue(subpref_name1, base::Value(kValue1),
194 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
195 default_store_->SetValue(subpref_name2, base::Value(kValue2),
196 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
197 default_store_->SetValue(other_name, base::Value(kValue2),
198 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
199
200 ASSERT_TRUE(selected_store_->GetValue(subpref_name1, nullptr));
201 ASSERT_TRUE(default_store_->GetValue(subpref_name2, nullptr));
202 ASSERT_TRUE(default_store_->GetValue(other_name, nullptr));
203
204 segregated_store_->RemoveValuesByPrefixSilently(kSelectedPref);
205
206 ASSERT_FALSE(selected_store_->GetValue(subpref_name1, nullptr));
207 ASSERT_FALSE(default_store_->GetValue(subpref_name2, nullptr));
208 ASSERT_TRUE(default_store_->GetValue(other_name, nullptr));
209 }
210
TEST_F(SegregatedPrefStoreTest,Observer)211 TEST_F(SegregatedPrefStoreTest, Observer) {
212 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
213 segregated_store_->ReadPrefs());
214 EXPECT_TRUE(observer_.initialized);
215 EXPECT_TRUE(observer_.initialization_success);
216 EXPECT_TRUE(observer_.changed_keys.empty());
217 segregated_store_->SetValue(kSelectedPref, base::Value(kValue1),
218 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
219 observer_.VerifyAndResetChangedKey(kSelectedPref);
220 segregated_store_->SetValue(kUnselectedPref, base::Value(kValue2),
221 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
222 observer_.VerifyAndResetChangedKey(kUnselectedPref);
223 }
224
TEST_F(SegregatedPrefStoreTest,ObserverAfterConstructionAfterSubInitialization)225 TEST_F(SegregatedPrefStoreTest,
226 ObserverAfterConstructionAfterSubInitialization) {
227 // Ensure that underlying PrefStores are initialized first.
228 default_store_->ReadPrefs();
229 selected_store_->ReadPrefs();
230 EXPECT_TRUE(default_store_->IsInitializationComplete());
231 EXPECT_TRUE(selected_store_->IsInitializationComplete());
232
233 // Create a new SegregatedPrefStore based on the initialized PrefStores.
234 segregated_store_->RemoveObserver(&observer_);
235 segregated_store_ = base::MakeRefCounted<SegregatedPrefStore>(
236 default_store_, selected_store_, selected_pref_names_);
237 segregated_store_->AddObserver(&observer_);
238 EXPECT_TRUE(segregated_store_->IsInitializationComplete());
239
240 // The Observer should receive notifications from the SegregatedPrefStore.
241 EXPECT_TRUE(observer_.changed_keys.empty());
242 segregated_store_->SetValue(kSelectedPref, base::Value(kValue1),
243 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
244 observer_.VerifyAndResetChangedKey(kSelectedPref);
245 segregated_store_->SetValue(kUnselectedPref, base::Value(kValue2),
246 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
247 observer_.VerifyAndResetChangedKey(kUnselectedPref);
248 }
249
TEST_F(SegregatedPrefStoreTest,SelectedPrefReadNoFileError)250 TEST_F(SegregatedPrefStoreTest, SelectedPrefReadNoFileError) {
251 // PREF_READ_ERROR_NO_FILE for the selected prefs file is silently converted
252 // to PREF_READ_ERROR_NONE.
253 selected_store_->set_read_error(PersistentPrefStore::PREF_READ_ERROR_NO_FILE);
254 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
255 segregated_store_->ReadPrefs());
256 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
257 segregated_store_->GetReadError());
258 }
259
TEST_F(SegregatedPrefStoreTest,SelectedPrefReadError)260 TEST_F(SegregatedPrefStoreTest, SelectedPrefReadError) {
261 selected_store_->set_read_error(
262 PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED);
263 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED,
264 segregated_store_->ReadPrefs());
265 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED,
266 segregated_store_->GetReadError());
267 }
268
TEST_F(SegregatedPrefStoreTest,SelectedPrefReadNoFileErrorAsync)269 TEST_F(SegregatedPrefStoreTest, SelectedPrefReadNoFileErrorAsync) {
270 // PREF_READ_ERROR_NO_FILE for the selected prefs file is silently converted
271 // to PREF_READ_ERROR_NONE.
272 selected_store_->set_read_error(PersistentPrefStore::PREF_READ_ERROR_NO_FILE);
273
274 default_store_->SetBlockAsyncRead(true);
275
276 EXPECT_FALSE(read_error_delegate_data_.invoked);
277
278 segregated_store_->ReadPrefsAsync(GetReadErrorDelegate().release());
279
280 EXPECT_FALSE(read_error_delegate_data_.invoked);
281
282 default_store_->SetBlockAsyncRead(false);
283
284 // ReadErrorDelegate is not invoked for ERROR_NONE.
285 EXPECT_FALSE(read_error_delegate_data_.invoked);
286 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
287 segregated_store_->GetReadError());
288 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
289 segregated_store_->GetReadError());
290 }
291
TEST_F(SegregatedPrefStoreTest,UnselectedPrefReadNoFileError)292 TEST_F(SegregatedPrefStoreTest, UnselectedPrefReadNoFileError) {
293 default_store_->set_read_error(PersistentPrefStore::PREF_READ_ERROR_NO_FILE);
294 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
295 segregated_store_->ReadPrefs());
296 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
297 segregated_store_->GetReadError());
298 }
299
TEST_F(SegregatedPrefStoreTest,UnselectedPrefReadError)300 TEST_F(SegregatedPrefStoreTest, UnselectedPrefReadError) {
301 default_store_->set_read_error(
302 PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED);
303 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED,
304 segregated_store_->ReadPrefs());
305 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED,
306 segregated_store_->GetReadError());
307 }
308
TEST_F(SegregatedPrefStoreTest,BothPrefReadError)309 TEST_F(SegregatedPrefStoreTest, BothPrefReadError) {
310 default_store_->set_read_error(PersistentPrefStore::PREF_READ_ERROR_NO_FILE);
311 selected_store_->set_read_error(
312 PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED);
313 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
314 segregated_store_->ReadPrefs());
315 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
316 segregated_store_->GetReadError());
317 }
318
TEST_F(SegregatedPrefStoreTest,BothPrefReadErrorAsync)319 TEST_F(SegregatedPrefStoreTest, BothPrefReadErrorAsync) {
320 default_store_->set_read_error(PersistentPrefStore::PREF_READ_ERROR_NO_FILE);
321 selected_store_->set_read_error(
322 PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED);
323
324 selected_store_->SetBlockAsyncRead(true);
325
326 EXPECT_FALSE(read_error_delegate_data_.invoked);
327
328 segregated_store_->ReadPrefsAsync(GetReadErrorDelegate().release());
329
330 EXPECT_FALSE(read_error_delegate_data_.invoked);
331
332 selected_store_->SetBlockAsyncRead(false);
333
334 EXPECT_TRUE(read_error_delegate_data_.invoked);
335 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
336 segregated_store_->GetReadError());
337 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
338 segregated_store_->GetReadError());
339 }
340
TEST_F(SegregatedPrefStoreTest,IsInitializationComplete)341 TEST_F(SegregatedPrefStoreTest, IsInitializationComplete) {
342 EXPECT_FALSE(segregated_store_->IsInitializationComplete());
343 segregated_store_->ReadPrefs();
344 EXPECT_TRUE(segregated_store_->IsInitializationComplete());
345 }
346
TEST_F(SegregatedPrefStoreTest,IsInitializationCompleteAsync)347 TEST_F(SegregatedPrefStoreTest, IsInitializationCompleteAsync) {
348 selected_store_->SetBlockAsyncRead(true);
349 default_store_->SetBlockAsyncRead(true);
350 EXPECT_FALSE(segregated_store_->IsInitializationComplete());
351 segregated_store_->ReadPrefsAsync(NULL);
352 EXPECT_FALSE(segregated_store_->IsInitializationComplete());
353 selected_store_->SetBlockAsyncRead(false);
354 EXPECT_FALSE(segregated_store_->IsInitializationComplete());
355 default_store_->SetBlockAsyncRead(false);
356 EXPECT_TRUE(segregated_store_->IsInitializationComplete());
357 }
358
TEST_F(SegregatedPrefStoreTest,GetValues)359 TEST_F(SegregatedPrefStoreTest, GetValues) {
360 // To check merge behavior, create selected and default stores so each has a
361 // key the other doesn't have and they have one key in common.
362 selected_store_->SetValue(kSelectedPref, base::Value(kValue1),
363 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
364 default_store_->SetValue(kUnselectedPref, base::Value(kValue2),
365 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
366 selected_store_->SetValue(kSharedPref, base::Value(kValue1),
367 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
368
369 auto values = segregated_store_->GetValues();
370 // Check that a selected preference is returned.
371 const base::Value* value = values.Find(kSelectedPref);
372 ASSERT_TRUE(value);
373 EXPECT_EQ(base::Value(kValue1), *value);
374
375 // Check that a a default preference is returned.
376 value = values.Find(kUnselectedPref);
377 ASSERT_TRUE(value);
378 EXPECT_EQ(base::Value(kValue2), *value);
379
380 // Check that the selected preference is preferred.
381 value = values.Find(kSharedPref);
382 ASSERT_TRUE(value);
383 EXPECT_EQ(base::Value(kValue1), *value);
384 }
385
386 INSTANTIATE_TEST_SUITE_P(
387 WithoutCallback,
388 SegregatedPrefStoreTest,
389 ::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK));
390 INSTANTIATE_TEST_SUITE_P(
391 WithCallback,
392 SegregatedPrefStoreTest,
393 ::testing::Values(CommitPendingWriteMode::WITH_CALLBACK));
394 INSTANTIATE_TEST_SUITE_P(
395 WithSynchronousCallback,
396 SegregatedPrefStoreTest,
397 ::testing::Values(CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
398