1 // Copyright 2013 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 "chrome/browser/prefs/pref_hash_store_impl.h"
9 #include "base/macros.h"
10 #include "base/values.h"
11 #include "chrome/browser/prefs/pref_hash_store_impl.h"
12 #include "chrome/browser/prefs/pref_hash_store_transaction.h"
13 #include "chrome/browser/prefs/tracked/hash_store_contents.h"
14 #include "testing/gtest/include/gtest/gtest.h"
16 class MockHashStoreContents
: public HashStoreContents
{
18 // Keep the data separate from the API implementation so that it can be owned
19 // by the test and reused. The API implementation is owned by the
22 Data() : commit_performed(false) {}
24 // Returns the current value of |commit_performed| and resets it to false
26 bool GetCommitPerformedAndReset() {
27 bool current_commit_performed
= commit_performed
;
28 commit_performed
= false;
29 return current_commit_performed
;
32 scoped_ptr
<base::DictionaryValue
> contents
;
33 std::string super_mac
;
34 scoped_ptr
<int> version
;
35 bool commit_performed
;
38 explicit MockHashStoreContents(Data
* data
) : data_(data
) {}
40 // HashStoreContents implementation
41 virtual std::string
hash_store_id() const OVERRIDE
{ return "store_id"; }
43 virtual void Reset() OVERRIDE
{
44 data_
->contents
.reset();
45 data_
->super_mac
= "";
46 data_
->version
.reset();
49 virtual bool IsInitialized() const OVERRIDE
{ return data_
->contents
; }
51 virtual bool GetVersion(int* version
) const OVERRIDE
{
53 *version
= *data_
->version
;
54 return data_
->version
;
57 virtual void SetVersion(int version
) OVERRIDE
{
58 data_
->version
.reset(new int(version
));
61 virtual const base::DictionaryValue
* GetContents() const OVERRIDE
{
62 return data_
->contents
.get();
65 virtual scoped_ptr
<MutableDictionary
> GetMutableContents() OVERRIDE
{
66 return scoped_ptr
<MutableDictionary
>(new MockMutableDictionary(data_
));
69 virtual std::string
GetSuperMac() const OVERRIDE
{ return data_
->super_mac
; }
71 virtual void SetSuperMac(const std::string
& super_mac
) OVERRIDE
{
72 data_
->super_mac
= super_mac
;
75 virtual void CommitPendingWrite() OVERRIDE
{
76 EXPECT_FALSE(data_
->commit_performed
);
77 data_
->commit_performed
= true;
81 class MockMutableDictionary
: public MutableDictionary
{
83 explicit MockMutableDictionary(Data
* data
) : data_(data
) {}
85 // MutableDictionary implementation
86 virtual base::DictionaryValue
* operator->() OVERRIDE
{
88 data_
->contents
.reset(new base::DictionaryValue
);
89 return data_
->contents
.get();
94 DISALLOW_COPY_AND_ASSIGN(MockMutableDictionary
);
99 DISALLOW_COPY_AND_ASSIGN(MockHashStoreContents
);
102 class PrefHashStoreImplTest
: public testing::Test
{
104 scoped_ptr
<HashStoreContents
> CreateHashStoreContents() {
105 return scoped_ptr
<HashStoreContents
>(
106 new MockHashStoreContents(&hash_store_data_
));
109 MockHashStoreContents::Data hash_store_data_
;
112 TEST_F(PrefHashStoreImplTest
, AtomicHashStoreAndCheck
) {
113 base::StringValue
string_1("string1");
114 base::StringValue
string_2("string2");
117 // 32 NULL bytes is the seed that was used to generate the legacy hash.
118 PrefHashStoreImpl
pref_hash_store(
119 std::string(32, 0), "device_id", CreateHashStoreContents());
120 scoped_ptr
<PrefHashStoreTransaction
> transaction(
121 pref_hash_store
.BeginTransaction());
123 // Only NULL should be trusted in the absence of a hash.
124 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE
,
125 transaction
->CheckValue("path1", &string_1
));
126 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE
,
127 transaction
->CheckValue("path1", NULL
));
129 transaction
->StoreHash("path1", &string_1
);
130 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED
,
131 transaction
->CheckValue("path1", &string_1
));
132 EXPECT_EQ(PrefHashStoreTransaction::CLEARED
,
133 transaction
->CheckValue("path1", NULL
));
134 transaction
->StoreHash("path1", NULL
);
135 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED
,
136 transaction
->CheckValue("path1", NULL
));
137 EXPECT_EQ(PrefHashStoreTransaction::CHANGED
,
138 transaction
->CheckValue("path1", &string_2
));
140 base::DictionaryValue dict
;
141 dict
.Set("a", new base::StringValue("foo"));
142 dict
.Set("d", new base::StringValue("bad"));
143 dict
.Set("b", new base::StringValue("bar"));
144 dict
.Set("c", new base::StringValue("baz"));
146 // Manually shove in a legacy hash.
147 (*CreateHashStoreContents()->GetMutableContents())->SetString(
149 "C503FB7C65EEFD5C07185F616A0AA67923C069909933F362022B1F187E73E9A2");
151 EXPECT_EQ(PrefHashStoreTransaction::WEAK_LEGACY
,
152 transaction
->CheckValue("path1", &dict
));
153 transaction
->StoreHash("path1", &dict
);
154 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED
,
155 transaction
->CheckValue("path1", &dict
));
157 // Test that the |pref_hash_store| flushes its changes on request post
160 pref_hash_store
.CommitPendingWrite();
161 EXPECT_TRUE(hash_store_data_
.GetCommitPerformedAndReset());
165 // |pref_hash_store2| should trust its initial hashes dictionary and thus
166 // trust new unknown values.
167 PrefHashStoreImpl
pref_hash_store2(
168 std::string(32, 0), "device_id", CreateHashStoreContents());
169 scoped_ptr
<PrefHashStoreTransaction
> transaction(
170 pref_hash_store2
.BeginTransaction());
171 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE
,
172 transaction
->CheckValue("new_path", &string_1
));
173 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE
,
174 transaction
->CheckValue("new_path", &string_2
));
175 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE
,
176 transaction
->CheckValue("new_path", NULL
));
178 // Test that |pref_hash_store2| doesn't flush its contents to disk when it
181 pref_hash_store2
.CommitPendingWrite();
182 EXPECT_FALSE(hash_store_data_
.GetCommitPerformedAndReset());
185 // Manually corrupt the super MAC.
186 hash_store_data_
.super_mac
= std::string(64, 'A');
189 // |pref_hash_store3| should no longer trust its initial hashes dictionary
190 // and thus shouldn't trust non-NULL unknown values.
191 PrefHashStoreImpl
pref_hash_store3(
192 std::string(32, 0), "device_id", CreateHashStoreContents());
193 scoped_ptr
<PrefHashStoreTransaction
> transaction(
194 pref_hash_store3
.BeginTransaction());
195 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE
,
196 transaction
->CheckValue("new_path", &string_1
));
197 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE
,
198 transaction
->CheckValue("new_path", &string_2
));
199 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE
,
200 transaction
->CheckValue("new_path", NULL
));
202 // Test that |pref_hash_store3| doesn't flush its contents to disk when it
205 pref_hash_store3
.CommitPendingWrite();
206 EXPECT_FALSE(hash_store_data_
.GetCommitPerformedAndReset());
210 TEST_F(PrefHashStoreImplTest
, SplitHashStoreAndCheck
) {
211 base::DictionaryValue dict
;
212 dict
.Set("a", new base::StringValue("to be replaced"));
213 dict
.Set("b", new base::StringValue("same"));
214 dict
.Set("o", new base::StringValue("old"));
216 base::DictionaryValue modified_dict
;
217 modified_dict
.Set("a", new base::StringValue("replaced"));
218 modified_dict
.Set("b", new base::StringValue("same"));
219 modified_dict
.Set("c", new base::StringValue("new"));
221 base::DictionaryValue empty_dict
;
223 std::vector
<std::string
> invalid_keys
;
226 PrefHashStoreImpl
pref_hash_store(
227 std::string(32, 0), "device_id", CreateHashStoreContents());
228 scoped_ptr
<PrefHashStoreTransaction
> transaction(
229 pref_hash_store
.BeginTransaction());
231 // No hashes stored yet and hashes dictionary is empty (and thus not
233 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE
,
234 transaction
->CheckSplitValue("path1", &dict
, &invalid_keys
));
235 EXPECT_TRUE(invalid_keys
.empty());
237 transaction
->StoreSplitHash("path1", &dict
);
239 // Verify match post storage.
240 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED
,
241 transaction
->CheckSplitValue("path1", &dict
, &invalid_keys
));
242 EXPECT_TRUE(invalid_keys
.empty());
244 // Verify new path is still unknown.
245 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE
,
246 transaction
->CheckSplitValue("path2", &dict
, &invalid_keys
));
247 EXPECT_TRUE(invalid_keys
.empty());
249 // Verify NULL or empty dicts are declared as having been cleared.
250 EXPECT_EQ(PrefHashStoreTransaction::CLEARED
,
251 transaction
->CheckSplitValue("path1", NULL
, &invalid_keys
));
252 EXPECT_TRUE(invalid_keys
.empty());
254 PrefHashStoreTransaction::CLEARED
,
255 transaction
->CheckSplitValue("path1", &empty_dict
, &invalid_keys
));
256 EXPECT_TRUE(invalid_keys
.empty());
258 // Verify changes are properly detected.
260 PrefHashStoreTransaction::CHANGED
,
261 transaction
->CheckSplitValue("path1", &modified_dict
, &invalid_keys
));
262 std::vector
<std::string
> expected_invalid_keys1
;
263 expected_invalid_keys1
.push_back("a");
264 expected_invalid_keys1
.push_back("c");
265 expected_invalid_keys1
.push_back("o");
266 EXPECT_EQ(expected_invalid_keys1
, invalid_keys
);
267 invalid_keys
.clear();
269 // Verify |dict| still matches post check.
270 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED
,
271 transaction
->CheckSplitValue("path1", &dict
, &invalid_keys
));
272 EXPECT_TRUE(invalid_keys
.empty());
274 // Store hash for |modified_dict|.
275 transaction
->StoreSplitHash("path1", &modified_dict
);
277 // Verify |modified_dict| is now the one that verifies correctly.
279 PrefHashStoreTransaction::UNCHANGED
,
280 transaction
->CheckSplitValue("path1", &modified_dict
, &invalid_keys
));
281 EXPECT_TRUE(invalid_keys
.empty());
283 // Verify old dict no longer matches.
284 EXPECT_EQ(PrefHashStoreTransaction::CHANGED
,
285 transaction
->CheckSplitValue("path1", &dict
, &invalid_keys
));
286 std::vector
<std::string
> expected_invalid_keys2
;
287 expected_invalid_keys2
.push_back("a");
288 expected_invalid_keys2
.push_back("o");
289 expected_invalid_keys2
.push_back("c");
290 EXPECT_EQ(expected_invalid_keys2
, invalid_keys
);
291 invalid_keys
.clear();
293 // Test that the |pref_hash_store| flushes its changes on request.
295 pref_hash_store
.CommitPendingWrite();
296 EXPECT_TRUE(hash_store_data_
.GetCommitPerformedAndReset());
300 // |pref_hash_store2| should trust its initial hashes dictionary and thus
301 // trust new unknown values.
302 PrefHashStoreImpl
pref_hash_store2(
303 std::string(32, 0), "device_id", CreateHashStoreContents());
304 scoped_ptr
<PrefHashStoreTransaction
> transaction(
305 pref_hash_store2
.BeginTransaction());
306 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE
,
307 transaction
->CheckSplitValue("new_path", &dict
, &invalid_keys
));
308 EXPECT_TRUE(invalid_keys
.empty());
310 // Test that |pref_hash_store2| doesn't flush its contents to disk when it
313 pref_hash_store2
.CommitPendingWrite();
314 EXPECT_FALSE(hash_store_data_
.GetCommitPerformedAndReset());
317 // Manually corrupt the super MAC.
318 hash_store_data_
.super_mac
= std::string(64, 'A');
321 // |pref_hash_store3| should no longer trust its initial hashes dictionary
322 // and thus shouldn't trust unknown values.
323 PrefHashStoreImpl
pref_hash_store3(
324 std::string(32, 0), "device_id", CreateHashStoreContents());
325 scoped_ptr
<PrefHashStoreTransaction
> transaction(
326 pref_hash_store3
.BeginTransaction());
327 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE
,
328 transaction
->CheckSplitValue("new_path", &dict
, &invalid_keys
));
329 EXPECT_TRUE(invalid_keys
.empty());
331 // Test that |pref_hash_store3| doesn't flush its contents to disk when it
334 pref_hash_store3
.CommitPendingWrite();
335 EXPECT_FALSE(hash_store_data_
.GetCommitPerformedAndReset());
339 TEST_F(PrefHashStoreImplTest
, EmptyAndNULLSplitDict
) {
340 base::DictionaryValue empty_dict
;
342 std::vector
<std::string
> invalid_keys
;
345 PrefHashStoreImpl
pref_hash_store(
346 std::string(32, 0), "device_id", CreateHashStoreContents());
347 scoped_ptr
<PrefHashStoreTransaction
> transaction(
348 pref_hash_store
.BeginTransaction());
350 // Store hashes for a random dict to be overwritten below.
351 base::DictionaryValue initial_dict
;
352 initial_dict
.Set("a", new base::StringValue("foo"));
353 transaction
->StoreSplitHash("path1", &initial_dict
);
355 // Verify stored empty dictionary matches NULL and empty dictionary back.
356 transaction
->StoreSplitHash("path1", &empty_dict
);
357 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED
,
358 transaction
->CheckSplitValue("path1", NULL
, &invalid_keys
));
359 EXPECT_TRUE(invalid_keys
.empty());
361 PrefHashStoreTransaction::UNCHANGED
,
362 transaction
->CheckSplitValue("path1", &empty_dict
, &invalid_keys
));
363 EXPECT_TRUE(invalid_keys
.empty());
365 // Same when storing NULL directly.
366 transaction
->StoreSplitHash("path1", NULL
);
367 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED
,
368 transaction
->CheckSplitValue("path1", NULL
, &invalid_keys
));
369 EXPECT_TRUE(invalid_keys
.empty());
371 PrefHashStoreTransaction::UNCHANGED
,
372 transaction
->CheckSplitValue("path1", &empty_dict
, &invalid_keys
));
373 EXPECT_TRUE(invalid_keys
.empty());
377 // |pref_hash_store2| should trust its initial hashes dictionary (and thus
378 // trust new unknown values) even though the last action done was to clear
379 // the hashes for path1 by setting its value to NULL (this is a regression
380 // test ensuring that the internal action of clearing some hashes does
381 // update the stored hash of hashes).
382 PrefHashStoreImpl
pref_hash_store2(
383 std::string(32, 0), "device_id", CreateHashStoreContents());
384 scoped_ptr
<PrefHashStoreTransaction
> transaction(
385 pref_hash_store2
.BeginTransaction());
387 base::DictionaryValue tested_dict
;
388 tested_dict
.Set("a", new base::StringValue("foo"));
389 tested_dict
.Set("b", new base::StringValue("bar"));
391 PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE
,
392 transaction
->CheckSplitValue("new_path", &tested_dict
, &invalid_keys
));
393 EXPECT_TRUE(invalid_keys
.empty());
397 // Test that the PrefHashStore returns TRUSTED_UNKNOWN_VALUE when checking for
398 // a split preference even if there is an existing atomic preference's hash
399 // stored. There is no point providing a migration path for preferences
400 // switching strategies after their initial release as split preferences are
401 // turned into split preferences specifically because the atomic hash isn't
402 // considered useful.
403 TEST_F(PrefHashStoreImplTest
, TrustedUnknownSplitValueFromExistingAtomic
) {
404 base::StringValue
string("string1");
406 base::DictionaryValue dict
;
407 dict
.Set("a", new base::StringValue("foo"));
408 dict
.Set("d", new base::StringValue("bad"));
409 dict
.Set("b", new base::StringValue("bar"));
410 dict
.Set("c", new base::StringValue("baz"));
413 PrefHashStoreImpl
pref_hash_store(
414 std::string(32, 0), "device_id", CreateHashStoreContents());
415 scoped_ptr
<PrefHashStoreTransaction
> transaction(
416 pref_hash_store
.BeginTransaction());
418 transaction
->StoreHash("path1", &string
);
419 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED
,
420 transaction
->CheckValue("path1", &string
));
424 // Load a new |pref_hash_store2| in which the hashes dictionary is trusted.
425 PrefHashStoreImpl
pref_hash_store2(
426 std::string(32, 0), "device_id", CreateHashStoreContents());
427 scoped_ptr
<PrefHashStoreTransaction
> transaction(
428 pref_hash_store2
.BeginTransaction());
429 std::vector
<std::string
> invalid_keys
;
430 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE
,
431 transaction
->CheckSplitValue("path1", &dict
, &invalid_keys
));
432 EXPECT_TRUE(invalid_keys
.empty());
436 TEST_F(PrefHashStoreImplTest
, GetCurrentVersion
) {
437 COMPILE_ASSERT(PrefHashStoreImpl::VERSION_LATEST
== 2,
438 new_versions_should_be_tested_here
);
440 PrefHashStoreImpl
pref_hash_store(
441 std::string(32, 0), "device_id", CreateHashStoreContents());
443 // VERSION_UNINITIALIZED when no hashes are stored.
444 EXPECT_EQ(PrefHashStoreImpl::VERSION_UNINITIALIZED
,
445 pref_hash_store
.GetCurrentVersion());
447 scoped_ptr
<PrefHashStoreTransaction
> transaction(
448 pref_hash_store
.BeginTransaction());
449 base::StringValue
string_value("foo");
450 transaction
->StoreHash("path1", &string_value
);
452 // Test that |pref_hash_store| flushes its content to disk when it
453 // initializes its version.
455 pref_hash_store
.CommitPendingWrite();
456 EXPECT_TRUE(hash_store_data_
.GetCommitPerformedAndReset());
459 PrefHashStoreImpl
pref_hash_store(
460 std::string(32, 0), "device_id", CreateHashStoreContents());
462 // VERSION_LATEST after storing a hash.
463 EXPECT_EQ(PrefHashStoreImpl::VERSION_LATEST
,
464 pref_hash_store
.GetCurrentVersion());
466 // Test that |pref_hash_store| doesn't flush its contents to disk when it
468 pref_hash_store
.CommitPendingWrite();
469 EXPECT_FALSE(hash_store_data_
.GetCommitPerformedAndReset());
472 // Manually clear the version number.
473 hash_store_data_
.version
.reset();
476 PrefHashStoreImpl
pref_hash_store(
477 std::string(32, 0), "device_id", CreateHashStoreContents());
479 // VERSION_PRE_MIGRATION when no version is stored.
480 EXPECT_EQ(PrefHashStoreImpl::VERSION_PRE_MIGRATION
,
481 pref_hash_store
.GetCurrentVersion());
483 scoped_ptr
<PrefHashStoreTransaction
> transaction(
484 pref_hash_store
.BeginTransaction());
486 // Test that |pref_hash_store| flushes its content to disk when it
487 // re-initializes its version.
489 pref_hash_store
.CommitPendingWrite();
490 EXPECT_TRUE(hash_store_data_
.GetCommitPerformedAndReset());
493 PrefHashStoreImpl
pref_hash_store(
494 std::string(32, 0), "device_id", CreateHashStoreContents());
496 // Back to VERSION_LATEST after performing a transaction from
497 // VERSION_PRE_MIGRATION (the presence of an existing hash should be
498 // sufficient, no need for the transaction itself to perform any work).
499 EXPECT_EQ(PrefHashStoreImpl::VERSION_LATEST
,
500 pref_hash_store
.GetCurrentVersion());
502 // Test that |pref_hash_store| doesn't flush its contents to disk when it
503 // didn't change (i.e., its version was already up-to-date and the only
504 // transaction performed was empty).
505 scoped_ptr
<PrefHashStoreTransaction
> transaction(
506 pref_hash_store
.BeginTransaction());
508 pref_hash_store
.CommitPendingWrite();
509 EXPECT_FALSE(hash_store_data_
.GetCommitPerformedAndReset());