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_filter.h"
13 #include "base/basictypes.h"
14 #include "base/bind.h"
15 #include "base/callback_forward.h"
16 #include "base/compiler_specific.h"
17 #include "base/logging.h"
18 #include "base/memory/ref_counted.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/values.h"
21 #include "chrome/browser/prefs/pref_hash_store.h"
22 #include "chrome/browser/prefs/pref_hash_store_transaction.h"
23 #include "chrome/common/pref_names.h"
24 #include "testing/gtest/include/gtest/gtest.h"
28 const char kAtomicPref[] = "atomic_pref";
29 const char kAtomicPref2[] = "atomic_pref2";
30 const char kAtomicPref3[] = "pref3";
31 const char kReportOnlyPref[] = "report_only";
32 const char kReportOnlySplitPref[] = "report_only_split_pref";
33 const char kSplitPref[] = "split_pref";
35 const PrefHashFilter::TrackedPreferenceMetadata kTestTrackedPrefs[] = {
37 0, kAtomicPref, PrefHashFilter::ENFORCE_ON_LOAD,
38 PrefHashFilter::TRACKING_STRATEGY_ATOMIC
41 1, kReportOnlyPref, PrefHashFilter::NO_ENFORCEMENT,
42 PrefHashFilter::TRACKING_STRATEGY_ATOMIC
45 2, kSplitPref, PrefHashFilter::ENFORCE_ON_LOAD,
46 PrefHashFilter::TRACKING_STRATEGY_SPLIT
49 3, kReportOnlySplitPref, PrefHashFilter::NO_ENFORCEMENT,
50 PrefHashFilter::TRACKING_STRATEGY_SPLIT
53 4, kAtomicPref2, PrefHashFilter::ENFORCE_ON_LOAD,
54 PrefHashFilter::TRACKING_STRATEGY_ATOMIC
57 5, kAtomicPref3, PrefHashFilter::ENFORCE_ON_LOAD,
58 PrefHashFilter::TRACKING_STRATEGY_ATOMIC
64 // A PrefHashStore that allows simulation of CheckValue results and captures
65 // checked and stored values.
66 class MockPrefHashStore : public PrefHashStore {
68 typedef std::pair<const void*, PrefHashFilter::PrefTrackingStrategy>
71 MockPrefHashStore() : transactions_expected_(1),
72 transactions_performed_(0),
73 transaction_active_(false) {}
75 virtual ~MockPrefHashStore() {
76 EXPECT_EQ(transactions_expected_, transactions_performed_);
77 EXPECT_FALSE(transaction_active_);
80 // Set the result that will be returned when |path| is passed to
81 // |CheckValue/CheckSplitValue|.
82 void SetCheckResult(const std::string& path,
83 PrefHashStoreTransaction::ValueState result);
85 // Set the invalid_keys that will be returned when |path| is passed to
86 // |CheckSplitValue|. SetCheckResult should already have been called for
87 // |path| with |result == CHANGED| for this to make any sense.
88 void SetInvalidKeysResult(
89 const std::string& path,
90 const std::vector<std::string>& invalid_keys_result);
92 void set_transactions_expected(size_t transactions_expected) {
93 transactions_expected_ = transactions_expected;
96 // Returns the number of paths checked.
97 size_t checked_paths_count() const {
98 return checked_values_.size();
101 // Returns the number of paths stored.
102 size_t stored_paths_count() const {
103 return stored_values_.size();
106 // Returns the pointer value and strategy that was passed to
107 // |CheckHash/CheckSplitHash| for |path|. The returned pointer could since
108 // have been freed and is thus not safe to dereference.
109 ValuePtrStrategyPair checked_value(const std::string& path) const {
110 std::map<std::string, ValuePtrStrategyPair>::const_iterator value =
111 checked_values_.find(path);
112 if (value != checked_values_.end())
113 return value->second;
114 return std::make_pair(
115 reinterpret_cast<void*>(0xBAD),
116 static_cast<PrefHashFilter::PrefTrackingStrategy>(-1));
119 // Returns the pointer value that was passed to |StoreHash/StoreSplitHash| for
120 // |path|. The returned pointer could since have been freed and is thus not
121 // safe to dereference.
122 ValuePtrStrategyPair stored_value(const std::string& path) const {
123 std::map<std::string, ValuePtrStrategyPair>::const_iterator value =
124 stored_values_.find(path);
125 if (value != stored_values_.end())
126 return value->second;
127 return std::make_pair(
128 reinterpret_cast<void*>(0xBAD),
129 static_cast<PrefHashFilter::PrefTrackingStrategy>(-1));
132 // PrefHashStore implementation.
133 virtual scoped_ptr<PrefHashStoreTransaction> BeginTransaction() OVERRIDE;
136 // A MockPrefHashStoreTransaction is handed to the caller on
137 // MockPrefHashStore::BeginTransaction(). It then stores state in its
138 // underlying MockPrefHashStore about calls it receives from that same caller
139 // which can later be verified in tests.
140 class MockPrefHashStoreTransaction : public PrefHashStoreTransaction {
142 explicit MockPrefHashStoreTransaction(MockPrefHashStore* outer)
145 virtual ~MockPrefHashStoreTransaction() {
146 outer_->transaction_active_ = false;
147 ++outer_->transactions_performed_;
150 // PrefHashStoreTransaction implementation.
151 virtual PrefHashStoreTransaction::ValueState CheckValue(
152 const std::string& path, const base::Value* value) const OVERRIDE;
153 virtual void StoreHash(const std::string& path,
154 const base::Value* new_value) OVERRIDE;
155 virtual PrefHashStoreTransaction::ValueState CheckSplitValue(
156 const std::string& path,
157 const base::DictionaryValue* initial_split_value,
158 std::vector<std::string>* invalid_keys) const OVERRIDE;
159 virtual void StoreSplitHash(
160 const std::string& path,
161 const base::DictionaryValue* split_value) OVERRIDE;
164 MockPrefHashStore* outer_;
166 DISALLOW_COPY_AND_ASSIGN(MockPrefHashStoreTransaction);
169 // Records a call to this mock's CheckValue/CheckSplitValue methods.
170 PrefHashStoreTransaction::ValueState RecordCheckValue(
171 const std::string& path,
172 const base::Value* value,
173 PrefHashFilter::PrefTrackingStrategy strategy);
175 // Records a call to this mock's StoreHash/StoreSplitHash methods.
176 void RecordStoreHash(const std::string& path,
177 const base::Value* new_value,
178 PrefHashFilter::PrefTrackingStrategy strategy);
180 std::map<std::string, PrefHashStoreTransaction::ValueState> check_results_;
181 std::map<std::string, std::vector<std::string> > invalid_keys_results_;
182 std::map<std::string, ValuePtrStrategyPair> checked_values_;
183 std::map<std::string, ValuePtrStrategyPair> stored_values_;
185 // Number of transactions that are expected to be performed in the scope of
186 // this test (defaults to 1).
187 size_t transactions_expected_;
189 // Number of transactions that were performed via this MockPrefHashStore.
190 // Verified to match |transactions_expected_| when this MockPrefHashStore is
192 size_t transactions_performed_;
194 // Whether a transaction is currently active (only one transaction should be
195 // active at a time).
196 bool transaction_active_;
198 DISALLOW_COPY_AND_ASSIGN(MockPrefHashStore);
201 void MockPrefHashStore::SetCheckResult(
202 const std::string& path, PrefHashStoreTransaction::ValueState result) {
203 check_results_.insert(std::make_pair(path, result));
206 void MockPrefHashStore::SetInvalidKeysResult(
207 const std::string& path,
208 const std::vector<std::string>& invalid_keys_result) {
209 // Ensure |check_results_| has a CHANGED entry for |path|.
210 std::map<std::string,
211 PrefHashStoreTransaction::ValueState>::const_iterator result =
212 check_results_.find(path);
213 ASSERT_TRUE(result != check_results_.end());
214 ASSERT_EQ(PrefHashStoreTransaction::CHANGED, result->second);
216 invalid_keys_results_.insert(std::make_pair(path, invalid_keys_result));
219 scoped_ptr<PrefHashStoreTransaction> MockPrefHashStore::BeginTransaction() {
220 EXPECT_FALSE(transaction_active_);
221 return scoped_ptr<PrefHashStoreTransaction>(
222 new MockPrefHashStoreTransaction(this));
225 PrefHashStoreTransaction::ValueState MockPrefHashStore::RecordCheckValue(
226 const std::string& path,
227 const base::Value* value,
228 PrefHashFilter::PrefTrackingStrategy strategy) {
229 // Record that |path| was checked and validate that it wasn't previously
231 EXPECT_TRUE(checked_values_.insert(
232 std::make_pair(path, std::make_pair(value, strategy))).second);
233 std::map<std::string,
234 PrefHashStoreTransaction::ValueState>::const_iterator result =
235 check_results_.find(path);
236 if (result != check_results_.end())
237 return result->second;
238 return PrefHashStoreTransaction::UNCHANGED;
241 void MockPrefHashStore::RecordStoreHash(
242 const std::string& path,
243 const base::Value* new_value,
244 PrefHashFilter::PrefTrackingStrategy strategy) {
245 EXPECT_TRUE(stored_values_.insert(
246 std::make_pair(path, std::make_pair(new_value, strategy))).second);
249 PrefHashStoreTransaction::ValueState
250 MockPrefHashStore::MockPrefHashStoreTransaction::CheckValue(
251 const std::string& path, const base::Value* value) const {
252 return outer_->RecordCheckValue(path, value,
253 PrefHashFilter::TRACKING_STRATEGY_ATOMIC);
256 void MockPrefHashStore::MockPrefHashStoreTransaction::StoreHash(
257 const std::string& path,
258 const base::Value* new_value) {
259 outer_->RecordStoreHash(path, new_value,
260 PrefHashFilter::TRACKING_STRATEGY_ATOMIC);
263 PrefHashStoreTransaction::ValueState
264 MockPrefHashStore::MockPrefHashStoreTransaction::CheckSplitValue(
265 const std::string& path,
266 const base::DictionaryValue* initial_split_value,
267 std::vector<std::string>* invalid_keys) const {
268 EXPECT_TRUE(invalid_keys && invalid_keys->empty());
270 std::map<std::string, std::vector<std::string> >::const_iterator
271 invalid_keys_result = outer_->invalid_keys_results_.find(path);
272 if (invalid_keys_result != outer_->invalid_keys_results_.end()) {
273 invalid_keys->insert(invalid_keys->begin(),
274 invalid_keys_result->second.begin(),
275 invalid_keys_result->second.end());
278 return outer_->RecordCheckValue(path, initial_split_value,
279 PrefHashFilter::TRACKING_STRATEGY_SPLIT);
282 void MockPrefHashStore::MockPrefHashStoreTransaction::StoreSplitHash(
283 const std::string& path,
284 const base::DictionaryValue* new_value) {
285 outer_->RecordStoreHash(path, new_value,
286 PrefHashFilter::TRACKING_STRATEGY_SPLIT);
289 // Creates a PrefHashFilter that uses a MockPrefHashStore. The
290 // MockPrefHashStore (owned by the PrefHashFilter) is returned in
291 // |mock_pref_hash_store|.
292 scoped_ptr<PrefHashFilter> CreatePrefHashFilter(
293 PrefHashFilter::EnforcementLevel max_enforcement_level,
294 MockPrefHashStore** mock_pref_hash_store) {
295 scoped_ptr<MockPrefHashStore> temp_mock_pref_hash_store(
296 new MockPrefHashStore);
297 if (mock_pref_hash_store)
298 *mock_pref_hash_store = temp_mock_pref_hash_store.get();
299 std::vector<PrefHashFilter::TrackedPreferenceMetadata> configuration(
300 kTestTrackedPrefs, kTestTrackedPrefs + arraysize(kTestTrackedPrefs));
301 for (std::vector<PrefHashFilter::TrackedPreferenceMetadata>::iterator it =
302 configuration.begin();
303 it != configuration.end();
305 if (it->enforcement_level > max_enforcement_level)
306 it->enforcement_level = max_enforcement_level;
308 return scoped_ptr<PrefHashFilter>(
309 new PrefHashFilter(temp_mock_pref_hash_store.PassAs<PrefHashStore>(),
311 arraysize(kTestTrackedPrefs)));
314 class PrefHashFilterTest
315 : public testing::TestWithParam<PrefHashFilter::EnforcementLevel> {
317 PrefHashFilterTest() : mock_pref_hash_store_(NULL) {}
319 virtual void SetUp() OVERRIDE {
320 // Construct a PrefHashFilter and MockPrefHashStore for the test.
322 CreatePrefHashFilter(GetParam(), &mock_pref_hash_store_);
326 bool RecordedReset() {
327 return pref_store_contents_.Get(prefs::kPreferenceResetTime, NULL);
330 MockPrefHashStore* mock_pref_hash_store_;
331 base::DictionaryValue pref_store_contents_;
332 scoped_ptr<PrefHashFilter> pref_hash_filter_;
334 DISALLOW_COPY_AND_ASSIGN(PrefHashFilterTest);
337 TEST_P(PrefHashFilterTest, EmptyAndUnchanged) {
338 pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
339 // All paths checked.
340 ASSERT_EQ(arraysize(kTestTrackedPrefs),
341 mock_pref_hash_store_->checked_paths_count());
342 // No paths stored, since they all return |UNCHANGED|.
343 ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths_count());
344 // Since there was nothing in |pref_store_contents_| the checked value should
345 // have been NULL for all tracked preferences.
346 for (size_t i = 0; i < arraysize(kTestTrackedPrefs); ++i) {
347 ASSERT_EQ(NULL, mock_pref_hash_store_->checked_value(
348 kTestTrackedPrefs[i].name).first);
350 ASSERT_FALSE(RecordedReset());
353 TEST_P(PrefHashFilterTest, FilterTrackedPrefUpdate) {
354 base::DictionaryValue root_dict;
355 // Ownership of |string_value| is transfered to |root_dict|.
356 base::Value* string_value = base::Value::CreateStringValue("string value");
357 root_dict.Set(kAtomicPref, string_value);
359 // No path should be stored on FilterUpdate.
360 pref_hash_filter_->FilterUpdate(kAtomicPref);
361 ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths_count());
363 // One path should be stored on FilterSerializeData.
364 pref_hash_filter_->FilterSerializeData(&root_dict);
365 ASSERT_EQ(1u, mock_pref_hash_store_->stored_paths_count());
366 MockPrefHashStore::ValuePtrStrategyPair stored_value =
367 mock_pref_hash_store_->stored_value(kAtomicPref);
368 ASSERT_EQ(string_value, stored_value.first);
369 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC, stored_value.second);
371 ASSERT_FALSE(RecordedReset());
374 TEST_P(PrefHashFilterTest, FilterSplitPrefUpdate) {
375 base::DictionaryValue root_dict;
376 // Ownership of |dict_value| is transfered to |root_dict|.
377 base::DictionaryValue* dict_value = new base::DictionaryValue;
378 dict_value->SetString("a", "foo");
379 dict_value->SetInteger("b", 1234);
380 root_dict.Set(kSplitPref, dict_value);
382 // No path should be stored on FilterUpdate.
383 pref_hash_filter_->FilterUpdate(kSplitPref);
384 ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths_count());
386 // One path should be stored on FilterSerializeData.
387 pref_hash_filter_->FilterSerializeData(&root_dict);
388 ASSERT_EQ(1u, mock_pref_hash_store_->stored_paths_count());
389 MockPrefHashStore::ValuePtrStrategyPair stored_value =
390 mock_pref_hash_store_->stored_value(kSplitPref);
391 ASSERT_EQ(dict_value, stored_value.first);
392 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT, stored_value.second);
394 ASSERT_FALSE(RecordedReset());
397 TEST_P(PrefHashFilterTest, FilterUntrackedPrefUpdate) {
398 // No transaction should even be started on FilterSerializeData() if there are
399 // no updates to perform.
400 mock_pref_hash_store_->set_transactions_expected(0);
402 base::DictionaryValue root_dict;
403 root_dict.Set("untracked", base::Value::CreateStringValue("some value"));
404 pref_hash_filter_->FilterUpdate("untracked");
406 // No paths should be stored on FilterUpdate.
407 ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths_count());
409 // Nor on FilterSerializeData.
410 pref_hash_filter_->FilterSerializeData(&root_dict);
411 ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths_count());
414 TEST_P(PrefHashFilterTest, MultiplePrefsFilterSerializeData) {
415 base::DictionaryValue root_dict;
416 // Ownership of the following values is transfered to |root_dict|.
417 base::Value* int_value1 = base::Value::CreateIntegerValue(1);
418 base::Value* int_value2 = base::Value::CreateIntegerValue(2);
419 base::Value* int_value3 = base::Value::CreateIntegerValue(3);
420 base::Value* int_value4 = base::Value::CreateIntegerValue(4);
421 base::DictionaryValue* dict_value = new base::DictionaryValue;
422 dict_value->Set("a", base::Value::CreateBooleanValue(true));
423 root_dict.Set(kAtomicPref, int_value1);
424 root_dict.Set(kAtomicPref2, int_value2);
425 root_dict.Set(kAtomicPref3, int_value3);
426 root_dict.Set("untracked", int_value4);
427 root_dict.Set(kSplitPref, dict_value);
429 // Only update kAtomicPref, kAtomicPref3, and kSplitPref.
430 pref_hash_filter_->FilterUpdate(kAtomicPref);
431 pref_hash_filter_->FilterUpdate(kAtomicPref3);
432 pref_hash_filter_->FilterUpdate(kSplitPref);
433 ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths_count());
435 // Update kAtomicPref3 again, nothing should be stored still.
436 base::Value* int_value5 = base::Value::CreateIntegerValue(5);
437 root_dict.Set(kAtomicPref3, int_value5);
438 ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths_count());
440 // On FilterSerializeData, only kAtomicPref, kAtomicPref3, and kSplitPref
441 // should get a new hash.
442 pref_hash_filter_->FilterSerializeData(&root_dict);
443 ASSERT_EQ(3u, mock_pref_hash_store_->stored_paths_count());
444 MockPrefHashStore::ValuePtrStrategyPair stored_value_atomic1 =
445 mock_pref_hash_store_->stored_value(kAtomicPref);
446 ASSERT_EQ(int_value1, stored_value_atomic1.first);
447 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
448 stored_value_atomic1.second);
450 MockPrefHashStore::ValuePtrStrategyPair stored_value_atomic3 =
451 mock_pref_hash_store_->stored_value(kAtomicPref3);
452 ASSERT_EQ(int_value5, stored_value_atomic3.first);
453 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
454 stored_value_atomic3.second);
456 MockPrefHashStore::ValuePtrStrategyPair stored_value_split =
457 mock_pref_hash_store_->stored_value(kSplitPref);
458 ASSERT_EQ(dict_value, stored_value_split.first);
459 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT, stored_value_split.second);
462 TEST_P(PrefHashFilterTest, EmptyAndUnknown) {
463 ASSERT_FALSE(pref_store_contents_.Get(kAtomicPref, NULL));
464 ASSERT_FALSE(pref_store_contents_.Get(kSplitPref, NULL));
465 // NULL values are always trusted by the PrefHashStore.
466 mock_pref_hash_store_->SetCheckResult(
467 kAtomicPref, PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE);
468 mock_pref_hash_store_->SetCheckResult(
469 kSplitPref, PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE);
470 pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
471 ASSERT_EQ(arraysize(kTestTrackedPrefs),
472 mock_pref_hash_store_->checked_paths_count());
473 ASSERT_EQ(2u, mock_pref_hash_store_->stored_paths_count());
475 MockPrefHashStore::ValuePtrStrategyPair stored_atomic_value =
476 mock_pref_hash_store_->stored_value(kAtomicPref);
477 ASSERT_EQ(NULL, stored_atomic_value.first);
478 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
479 stored_atomic_value.second);
481 MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
482 mock_pref_hash_store_->stored_value(kSplitPref);
483 ASSERT_EQ(NULL, stored_split_value.first);
484 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT,
485 stored_split_value.second);
488 TEST_P(PrefHashFilterTest, InitialValueUnknown) {
489 // Ownership of these values is transfered to |pref_store_contents_|.
490 base::StringValue* string_value = new base::StringValue("string value");
491 pref_store_contents_.Set(kAtomicPref, string_value);
493 base::DictionaryValue* dict_value = new base::DictionaryValue;
494 dict_value->SetString("a", "foo");
495 dict_value->SetInteger("b", 1234);
496 pref_store_contents_.Set(kSplitPref, dict_value);
498 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, NULL));
499 ASSERT_TRUE(pref_store_contents_.Get(kSplitPref, NULL));
501 mock_pref_hash_store_->SetCheckResult(
502 kAtomicPref, PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE);
503 mock_pref_hash_store_->SetCheckResult(
504 kSplitPref, PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE);
505 pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
506 ASSERT_EQ(arraysize(kTestTrackedPrefs),
507 mock_pref_hash_store_->checked_paths_count());
508 ASSERT_EQ(2u, mock_pref_hash_store_->stored_paths_count());
510 MockPrefHashStore::ValuePtrStrategyPair stored_atomic_value =
511 mock_pref_hash_store_->stored_value(kAtomicPref);
512 MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
513 mock_pref_hash_store_->stored_value(kSplitPref);
514 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
515 stored_atomic_value.second);
516 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT,
517 stored_split_value.second);
518 if (GetParam() == PrefHashFilter::ENFORCE_ON_LOAD) {
519 // Ensure the prefs were cleared and the hashes for NULL were restored if
520 // the current enforcement level denies seeding.
521 ASSERT_FALSE(pref_store_contents_.Get(kAtomicPref, NULL));
522 ASSERT_EQ(NULL, stored_atomic_value.first);
524 ASSERT_FALSE(pref_store_contents_.Get(kSplitPref, NULL));
525 ASSERT_EQ(NULL, stored_split_value.first);
527 ASSERT_TRUE(RecordedReset());
529 // Otherwise the values should have remained intact and the hashes should
530 // have been updated to match them.
531 const base::Value* atomic_value_in_store;
532 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, &atomic_value_in_store));
533 ASSERT_EQ(string_value, atomic_value_in_store);
534 ASSERT_EQ(string_value, stored_atomic_value.first);
536 const base::Value* split_value_in_store;
537 ASSERT_TRUE(pref_store_contents_.Get(kSplitPref, &split_value_in_store));
538 ASSERT_EQ(dict_value, split_value_in_store);
539 ASSERT_EQ(dict_value, stored_split_value.first);
541 ASSERT_FALSE(RecordedReset());
545 TEST_P(PrefHashFilterTest, InitialValueTrustedUnknown) {
546 // Ownership of this value is transfered to |pref_store_contents_|.
547 base::Value* string_value = base::Value::CreateStringValue("test");
548 pref_store_contents_.Set(kAtomicPref, string_value);
550 base::DictionaryValue* dict_value = new base::DictionaryValue;
551 dict_value->SetString("a", "foo");
552 dict_value->SetInteger("b", 1234);
553 pref_store_contents_.Set(kSplitPref, dict_value);
555 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, NULL));
556 ASSERT_TRUE(pref_store_contents_.Get(kSplitPref, NULL));
558 mock_pref_hash_store_->SetCheckResult(
559 kAtomicPref, PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE);
560 mock_pref_hash_store_->SetCheckResult(
561 kSplitPref, PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE);
562 pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
563 ASSERT_EQ(arraysize(kTestTrackedPrefs),
564 mock_pref_hash_store_->checked_paths_count());
565 ASSERT_EQ(2u, mock_pref_hash_store_->stored_paths_count());
567 // Seeding is always allowed for trusted unknown values.
568 const base::Value* atomic_value_in_store;
569 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, &atomic_value_in_store));
570 ASSERT_EQ(string_value, atomic_value_in_store);
571 MockPrefHashStore::ValuePtrStrategyPair stored_atomic_value =
572 mock_pref_hash_store_->stored_value(kAtomicPref);
573 ASSERT_EQ(string_value, stored_atomic_value.first);
574 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
575 stored_atomic_value.second);
577 const base::Value* split_value_in_store;
578 ASSERT_TRUE(pref_store_contents_.Get(kSplitPref, &split_value_in_store));
579 ASSERT_EQ(dict_value, split_value_in_store);
580 MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
581 mock_pref_hash_store_->stored_value(kSplitPref);
582 ASSERT_EQ(dict_value, stored_split_value.first);
583 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT,
584 stored_split_value.second);
587 TEST_P(PrefHashFilterTest, InitialValueChanged) {
588 // Ownership of this value is transfered to |pref_store_contents_|.
589 base::Value* int_value = base::Value::CreateIntegerValue(1234);
590 pref_store_contents_.Set(kAtomicPref, int_value);
592 base::DictionaryValue* dict_value = new base::DictionaryValue;
593 dict_value->SetString("a", "foo");
594 dict_value->SetInteger("b", 1234);
595 dict_value->SetInteger("c", 56);
596 dict_value->SetBoolean("d", false);
597 pref_store_contents_.Set(kSplitPref, dict_value);
599 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, NULL));
600 ASSERT_TRUE(pref_store_contents_.Get(kSplitPref, NULL));
602 mock_pref_hash_store_->SetCheckResult(kAtomicPref,
603 PrefHashStoreTransaction::CHANGED);
604 mock_pref_hash_store_->SetCheckResult(kSplitPref,
605 PrefHashStoreTransaction::CHANGED);
607 std::vector<std::string> mock_invalid_keys;
608 mock_invalid_keys.push_back("a");
609 mock_invalid_keys.push_back("c");
610 mock_pref_hash_store_->SetInvalidKeysResult(kSplitPref, mock_invalid_keys);
612 pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
613 ASSERT_EQ(arraysize(kTestTrackedPrefs),
614 mock_pref_hash_store_->checked_paths_count());
615 ASSERT_EQ(2u, mock_pref_hash_store_->stored_paths_count());
617 MockPrefHashStore::ValuePtrStrategyPair stored_atomic_value =
618 mock_pref_hash_store_->stored_value(kAtomicPref);
619 MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
620 mock_pref_hash_store_->stored_value(kSplitPref);
621 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
622 stored_atomic_value.second);
623 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT,
624 stored_split_value.second);
625 if (GetParam() == PrefHashFilter::ENFORCE_ON_LOAD) {
626 // Ensure the atomic pref was cleared and the hash for NULL was restored if
627 // the current enforcement level prevents changes.
628 ASSERT_FALSE(pref_store_contents_.Get(kAtomicPref, NULL));
629 ASSERT_EQ(NULL, stored_atomic_value.first);
631 // The split pref on the other hand should only have been stripped of its
633 const base::Value* split_value_in_store;
634 ASSERT_TRUE(pref_store_contents_.Get(kSplitPref, &split_value_in_store));
635 ASSERT_EQ(2U, dict_value->size());
636 ASSERT_FALSE(dict_value->HasKey("a"));
637 ASSERT_TRUE(dict_value->HasKey("b"));
638 ASSERT_FALSE(dict_value->HasKey("c"));
639 ASSERT_TRUE(dict_value->HasKey("d"));
640 ASSERT_EQ(dict_value, stored_split_value.first);
642 ASSERT_TRUE(RecordedReset());
644 // Otherwise the value should have remained intact and the hash should have
645 // been updated to match it.
646 const base::Value* atomic_value_in_store;
647 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, &atomic_value_in_store));
648 ASSERT_EQ(int_value, atomic_value_in_store);
649 ASSERT_EQ(int_value, stored_atomic_value.first);
651 const base::Value* split_value_in_store;
652 ASSERT_TRUE(pref_store_contents_.Get(kSplitPref, &split_value_in_store));
653 ASSERT_EQ(dict_value, split_value_in_store);
654 ASSERT_EQ(4U, dict_value->size());
655 ASSERT_TRUE(dict_value->HasKey("a"));
656 ASSERT_TRUE(dict_value->HasKey("b"));
657 ASSERT_TRUE(dict_value->HasKey("c"));
658 ASSERT_TRUE(dict_value->HasKey("d"));
659 ASSERT_EQ(dict_value, stored_split_value.first);
661 ASSERT_FALSE(RecordedReset());
665 TEST_P(PrefHashFilterTest, EmptyCleared) {
666 ASSERT_FALSE(pref_store_contents_.Get(kAtomicPref, NULL));
667 ASSERT_FALSE(pref_store_contents_.Get(kSplitPref, NULL));
668 mock_pref_hash_store_->SetCheckResult(kAtomicPref,
669 PrefHashStoreTransaction::CLEARED);
670 mock_pref_hash_store_->SetCheckResult(kSplitPref,
671 PrefHashStoreTransaction::CLEARED);
672 pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
673 ASSERT_EQ(arraysize(kTestTrackedPrefs),
674 mock_pref_hash_store_->checked_paths_count());
675 ASSERT_EQ(2u, mock_pref_hash_store_->stored_paths_count());
677 // Regardless of the enforcement level, the only thing that should be done is
678 // to restore the hash for NULL. The value itself should still be NULL.
679 ASSERT_FALSE(pref_store_contents_.Get(kAtomicPref, NULL));
680 MockPrefHashStore::ValuePtrStrategyPair stored_atomic_value =
681 mock_pref_hash_store_->stored_value(kAtomicPref);
682 ASSERT_EQ(NULL, stored_atomic_value.first);
683 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
684 stored_atomic_value.second);
686 ASSERT_FALSE(pref_store_contents_.Get(kSplitPref, NULL));
687 MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
688 mock_pref_hash_store_->stored_value(kSplitPref);
689 ASSERT_EQ(NULL, stored_split_value.first);
690 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT,
691 stored_split_value.second);
694 TEST_P(PrefHashFilterTest, InitialValueMigrated) {
695 // Only test atomic prefs, split prefs were introduce after the migration.
697 // Ownership of this value is transfered to |pref_store_contents_|.
698 base::ListValue* list_value = new base::ListValue;
699 list_value->Append(base::Value::CreateStringValue("test"));
700 pref_store_contents_.Set(kAtomicPref, list_value);
702 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, NULL));
704 mock_pref_hash_store_->SetCheckResult(kAtomicPref,
705 PrefHashStoreTransaction::WEAK_LEGACY);
706 pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
707 ASSERT_EQ(arraysize(kTestTrackedPrefs),
708 mock_pref_hash_store_->checked_paths_count());
709 ASSERT_EQ(1u, mock_pref_hash_store_->stored_paths_count());
711 MockPrefHashStore::ValuePtrStrategyPair stored_atomic_value =
712 mock_pref_hash_store_->stored_value(kAtomicPref);
713 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
714 stored_atomic_value.second);
715 if (GetParam() == PrefHashFilter::ENFORCE_ON_LOAD) {
716 // Ensure the pref was cleared and the hash for NULL was restored if the
717 // current enforcement level prevents migration.
718 ASSERT_FALSE(pref_store_contents_.Get(kAtomicPref, NULL));
719 ASSERT_EQ(NULL, stored_atomic_value.first);
721 ASSERT_TRUE(RecordedReset());
723 // Otherwise the value should have remained intact and the hash should have
724 // been updated to match it.
725 const base::Value* atomic_value_in_store;
726 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, &atomic_value_in_store));
727 ASSERT_EQ(list_value, atomic_value_in_store);
728 ASSERT_EQ(list_value, stored_atomic_value.first);
730 ASSERT_FALSE(RecordedReset());
734 TEST_P(PrefHashFilterTest, InitialValueUnchangedLegacyId) {
735 // Ownership of these values is transfered to |pref_store_contents_|.
736 base::StringValue* string_value = new base::StringValue("string value");
737 pref_store_contents_.Set(kAtomicPref, string_value);
739 base::DictionaryValue* dict_value = new base::DictionaryValue;
740 dict_value->SetString("a", "foo");
741 dict_value->SetInteger("b", 1234);
742 pref_store_contents_.Set(kSplitPref, dict_value);
744 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, NULL));
745 ASSERT_TRUE(pref_store_contents_.Get(kSplitPref, NULL));
747 mock_pref_hash_store_->SetCheckResult(
748 kAtomicPref, PrefHashStoreTransaction::SECURE_LEGACY);
749 mock_pref_hash_store_->SetCheckResult(
750 kSplitPref, PrefHashStoreTransaction::SECURE_LEGACY);
751 pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
752 ASSERT_EQ(arraysize(kTestTrackedPrefs),
753 mock_pref_hash_store_->checked_paths_count());
755 // Ensure that both the atomic and split hashes were restored.
756 ASSERT_EQ(2u, mock_pref_hash_store_->stored_paths_count());
758 // In all cases, the values should have remained intact and the hashes should
759 // have been updated to match them.
761 MockPrefHashStore::ValuePtrStrategyPair stored_atomic_value =
762 mock_pref_hash_store_->stored_value(kAtomicPref);
763 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_ATOMIC,
764 stored_atomic_value.second);
765 const base::Value* atomic_value_in_store;
766 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, &atomic_value_in_store));
767 ASSERT_EQ(string_value, atomic_value_in_store);
768 ASSERT_EQ(string_value, stored_atomic_value.first);
770 MockPrefHashStore::ValuePtrStrategyPair stored_split_value =
771 mock_pref_hash_store_->stored_value(kSplitPref);
772 ASSERT_EQ(PrefHashFilter::TRACKING_STRATEGY_SPLIT,
773 stored_split_value.second);
774 const base::Value* split_value_in_store;
775 ASSERT_TRUE(pref_store_contents_.Get(kSplitPref, &split_value_in_store));
776 ASSERT_EQ(dict_value, split_value_in_store);
777 ASSERT_EQ(dict_value, stored_split_value.first);
779 ASSERT_FALSE(RecordedReset());
782 TEST_P(PrefHashFilterTest, DontResetReportOnly) {
783 // Ownership of these values is transfered to |pref_store_contents_|.
784 base::Value* int_value1 = base::Value::CreateIntegerValue(1);
785 base::Value* int_value2 = base::Value::CreateIntegerValue(2);
786 base::Value* report_only_val = base::Value::CreateIntegerValue(3);
787 base::DictionaryValue* report_only_split_val = new base::DictionaryValue;
788 report_only_split_val->SetInteger("a", 1234);
789 pref_store_contents_.Set(kAtomicPref, int_value1);
790 pref_store_contents_.Set(kAtomicPref2, int_value2);
791 pref_store_contents_.Set(kReportOnlyPref, report_only_val);
792 pref_store_contents_.Set(kReportOnlySplitPref, report_only_split_val);
794 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, NULL));
795 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref2, NULL));
796 ASSERT_TRUE(pref_store_contents_.Get(kReportOnlyPref, NULL));
797 ASSERT_TRUE(pref_store_contents_.Get(kReportOnlySplitPref, NULL));
799 mock_pref_hash_store_->SetCheckResult(kAtomicPref,
800 PrefHashStoreTransaction::CHANGED);
801 mock_pref_hash_store_->SetCheckResult(kAtomicPref2,
802 PrefHashStoreTransaction::CHANGED);
803 mock_pref_hash_store_->SetCheckResult(kReportOnlyPref,
804 PrefHashStoreTransaction::CHANGED);
805 mock_pref_hash_store_->SetCheckResult(kReportOnlySplitPref,
806 PrefHashStoreTransaction::CHANGED);
807 pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
808 // All prefs should be checked and a new hash should be stored for each tested
810 ASSERT_EQ(arraysize(kTestTrackedPrefs),
811 mock_pref_hash_store_->checked_paths_count());
812 ASSERT_EQ(4u, mock_pref_hash_store_->stored_paths_count());
814 // No matter what the enforcement level is, the report only pref should never
816 ASSERT_TRUE(pref_store_contents_.Get(kReportOnlyPref, NULL));
817 ASSERT_TRUE(pref_store_contents_.Get(kReportOnlySplitPref, NULL));
818 ASSERT_EQ(report_only_val,
819 mock_pref_hash_store_->stored_value(kReportOnlyPref).first);
820 ASSERT_EQ(report_only_split_val,
821 mock_pref_hash_store_->stored_value(kReportOnlySplitPref).first);
823 // All other prefs should have been reset if the enforcement level allows it.
824 if (GetParam() == PrefHashFilter::ENFORCE_ON_LOAD) {
825 ASSERT_FALSE(pref_store_contents_.Get(kAtomicPref, NULL));
826 ASSERT_FALSE(pref_store_contents_.Get(kAtomicPref2, NULL));
827 ASSERT_EQ(NULL, mock_pref_hash_store_->stored_value(kAtomicPref).first);
828 ASSERT_EQ(NULL, mock_pref_hash_store_->stored_value(kAtomicPref2).first);
830 ASSERT_TRUE(RecordedReset());
832 const base::Value* value_in_store;
833 const base::Value* value_in_store2;
834 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref, &value_in_store));
835 ASSERT_TRUE(pref_store_contents_.Get(kAtomicPref2, &value_in_store2));
836 ASSERT_EQ(int_value1, value_in_store);
837 ASSERT_EQ(int_value1,
838 mock_pref_hash_store_->stored_value(kAtomicPref).first);
839 ASSERT_EQ(int_value2, value_in_store2);
840 ASSERT_EQ(int_value2,
841 mock_pref_hash_store_->stored_value(kAtomicPref2).first);
843 ASSERT_FALSE(RecordedReset());
847 INSTANTIATE_TEST_CASE_P(
848 PrefHashFilterTestInstance, PrefHashFilterTest,
849 testing::Values(PrefHashFilter::NO_ENFORCEMENT,
850 PrefHashFilter::ENFORCE_ON_LOAD));