Revert 271602 "Implementation of leveldb-backed PrefStore."
[chromium-blink-merge.git] / chrome / browser / prefs / profile_pref_store_manager_unittest.cc
blob6f4c3756d4f26aab1e2eda83c6031432ab058919
1 // Copyright 2014 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/profile_pref_store_manager.h"
7 #include <vector>
9 #include "base/compiler_specific.h"
10 #include "base/file_util.h"
11 #include "base/files/file_enumerator.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/prefs/json_pref_store.h"
17 #include "base/prefs/persistent_pref_store.h"
18 #include "base/prefs/pref_service.h"
19 #include "base/prefs/pref_service_factory.h"
20 #include "base/prefs/pref_store.h"
21 #include "base/prefs/testing_pref_service.h"
22 #include "base/run_loop.h"
23 #include "base/strings/string_util.h"
24 #include "base/values.h"
25 #include "chrome/browser/prefs/pref_hash_filter.h"
26 #include "chrome/common/pref_names.h"
27 #include "components/pref_registry/pref_registry_syncable.h"
28 #include "testing/gtest/include/gtest/gtest.h"
30 namespace {
32 class FirstEqualsPredicate {
33 public:
34 explicit FirstEqualsPredicate(const std::string& expected)
35 : expected_(expected) {}
36 bool operator()(const std::pair<std::string, base::Value*>& pair) {
37 return pair.first == expected_;
40 private:
41 const std::string expected_;
44 // Observes changes to the PrefStore and verifies that only registered prefs are
45 // written.
46 class RegistryVerifier : public PrefStore::Observer {
47 public:
48 explicit RegistryVerifier(PrefRegistry* pref_registry)
49 : pref_registry_(pref_registry) {}
51 // PrefStore::Observer implementation
52 virtual void OnPrefValueChanged(const std::string& key) OVERRIDE {
53 EXPECT_TRUE(pref_registry_->end() !=
54 std::find_if(pref_registry_->begin(),
55 pref_registry_->end(),
56 FirstEqualsPredicate(key)))
57 << "Unregistered key " << key << " was changed.";
60 virtual void OnInitializationCompleted(bool succeeded) OVERRIDE {}
62 private:
63 scoped_refptr<PrefRegistry> pref_registry_;
66 const char kUnprotectedPref[] = "unprotected_pref";
67 const char kTrackedAtomic[] = "tracked_atomic";
68 const char kProtectedAtomic[] = "protected_atomic";
70 const char kFoobar[] = "FOOBAR";
71 const char kBarfoo[] = "BARFOO";
72 const char kHelloWorld[] = "HELLOWORLD";
73 const char kGoodbyeWorld[] = "GOODBYEWORLD";
75 const PrefHashFilter::TrackedPreferenceMetadata kConfiguration[] = {
76 {0u, kTrackedAtomic, PrefHashFilter::NO_ENFORCEMENT,
77 PrefHashFilter::TRACKING_STRATEGY_ATOMIC},
78 {1u, kProtectedAtomic, PrefHashFilter::ENFORCE_ON_LOAD,
79 PrefHashFilter::TRACKING_STRATEGY_ATOMIC}};
81 const size_t kExtraReportingId = 2u;
82 const size_t kReportingIdCount = 3u;
84 } // namespace
86 class ProfilePrefStoreManagerTest : public testing::Test {
87 public:
88 ProfilePrefStoreManagerTest()
89 : configuration_(kConfiguration,
90 kConfiguration + arraysize(kConfiguration)),
91 profile_pref_registry_(new user_prefs::PrefRegistrySyncable),
92 registry_verifier_(profile_pref_registry_) {}
94 virtual void SetUp() OVERRIDE {
95 ProfilePrefStoreManager::RegisterPrefs(local_state_.registry());
96 ProfilePrefStoreManager::RegisterProfilePrefs(profile_pref_registry_);
97 for (const PrefHashFilter::TrackedPreferenceMetadata* it = kConfiguration;
98 it != kConfiguration + arraysize(kConfiguration);
99 ++it) {
100 if (it->strategy == PrefHashFilter::TRACKING_STRATEGY_ATOMIC) {
101 profile_pref_registry_->RegisterStringPref(
102 it->name,
103 std::string(),
104 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
105 } else {
106 profile_pref_registry_->RegisterDictionaryPref(
107 it->name, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
110 profile_pref_registry_->RegisterStringPref(
111 kUnprotectedPref,
112 std::string(),
113 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
115 // As in chrome_pref_service_factory.cc, kPreferencesResetTime needs to be
116 // declared as protected in order to be read from the proper store by the
117 // SegregatedPrefStore. Only declare it after configured prefs have been
118 // registered above for this test as kPreferenceResetTime is already
119 // registered in ProfilePrefStoreManager::RegisterProfilePrefs.
120 PrefHashFilter::TrackedPreferenceMetadata pref_reset_time_config =
121 {configuration_.rbegin()->reporting_id + 1, prefs::kPreferenceResetTime,
122 PrefHashFilter::ENFORCE_ON_LOAD,
123 PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
124 configuration_.push_back(pref_reset_time_config);
126 ASSERT_TRUE(profile_dir_.CreateUniqueTempDir());
127 ReloadConfiguration();
130 void ReloadConfiguration() {
131 manager_.reset(new ProfilePrefStoreManager(profile_dir_.path(),
132 configuration_,
133 kReportingIdCount,
134 "seed",
135 "device_id",
136 &local_state_));
139 virtual void TearDown() OVERRIDE { DestroyPrefStore(); }
141 protected:
142 bool WasResetRecorded() {
143 base::PrefServiceFactory pref_service_factory;
144 pref_service_factory.set_user_prefs(pref_store_);
146 scoped_ptr<PrefService> pref_service(
147 pref_service_factory.Create(profile_pref_registry_));
149 return !ProfilePrefStoreManager::GetResetTime(pref_service.get()).is_null();
152 void InitializePrefs() {
153 // According to the implementation of ProfilePrefStoreManager, this is
154 // actually a SegregatedPrefStore backed by two underlying pref stores.
155 scoped_refptr<PersistentPrefStore> pref_store =
156 manager_->CreateProfilePrefStore(
157 main_message_loop_.message_loop_proxy());
158 InitializePrefStore(pref_store);
159 pref_store = NULL;
160 base::RunLoop().RunUntilIdle();
163 void DestroyPrefStore() {
164 if (pref_store_) {
165 // Force everything to be written to disk, triggering the PrefHashFilter
166 // while our RegistryVerifier is watching.
167 pref_store_->CommitPendingWrite();
168 base::RunLoop().RunUntilIdle();
170 pref_store_->RemoveObserver(&registry_verifier_);
171 pref_store_ = NULL;
172 // Nothing should have to happen on the background threads, but just in
173 // case...
174 base::RunLoop().RunUntilIdle();
178 void InitializeDeprecatedCombinedProfilePrefStore() {
179 scoped_refptr<PersistentPrefStore> pref_store =
180 manager_->CreateDeprecatedCombinedProfilePrefStore(
181 main_message_loop_.message_loop_proxy());
182 InitializePrefStore(pref_store);
183 pref_store = NULL;
184 base::RunLoop().RunUntilIdle();
187 void InitializePrefStore(PersistentPrefStore* pref_store) {
188 pref_store->AddObserver(&registry_verifier_);
189 PersistentPrefStore::PrefReadError error = pref_store->ReadPrefs();
190 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, error);
191 pref_store->SetValue(kTrackedAtomic, new base::StringValue(kFoobar));
192 pref_store->SetValue(kProtectedAtomic, new base::StringValue(kHelloWorld));
193 pref_store->SetValue(kUnprotectedPref, new base::StringValue(kFoobar));
194 pref_store->RemoveObserver(&registry_verifier_);
195 pref_store->CommitPendingWrite();
196 base::RunLoop().RunUntilIdle();
199 void LoadExistingPrefs() {
200 DestroyPrefStore();
201 pref_store_ = manager_->CreateProfilePrefStore(
202 main_message_loop_.message_loop_proxy());
203 pref_store_->AddObserver(&registry_verifier_);
204 pref_store_->ReadPrefs();
207 void ReplaceStringInPrefs(const std::string& find,
208 const std::string& replace) {
209 base::FileEnumerator file_enum(
210 profile_dir_.path(), true, base::FileEnumerator::FILES);
212 for (base::FilePath path = file_enum.Next(); !path.empty();
213 path = file_enum.Next()) {
214 // Tamper with the file's contents
215 std::string contents;
216 EXPECT_TRUE(base::ReadFileToString(path, &contents));
217 ReplaceSubstringsAfterOffset(&contents, 0u, find, replace);
218 EXPECT_EQ(static_cast<int>(contents.length()),
219 base::WriteFile(path, contents.c_str(), contents.length()));
223 void ExpectStringValueEquals(const std::string& name,
224 const std::string& expected) {
225 const base::Value* value = NULL;
226 std::string as_string;
227 if (!pref_store_->GetValue(name, &value)) {
228 ADD_FAILURE() << name << " is not a defined value.";
229 } else if (!value->GetAsString(&as_string)) {
230 ADD_FAILURE() << name << " could not be coerced to a string.";
231 } else {
232 EXPECT_EQ(expected, as_string);
236 base::MessageLoop main_message_loop_;
237 std::vector<PrefHashFilter::TrackedPreferenceMetadata> configuration_;
238 base::ScopedTempDir profile_dir_;
239 TestingPrefServiceSimple local_state_;
240 scoped_refptr<user_prefs::PrefRegistrySyncable> profile_pref_registry_;
241 RegistryVerifier registry_verifier_;
242 scoped_ptr<ProfilePrefStoreManager> manager_;
243 scoped_refptr<PersistentPrefStore> pref_store_;
246 TEST_F(ProfilePrefStoreManagerTest, StoreValues) {
247 InitializePrefs();
249 LoadExistingPrefs();
251 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
252 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
253 EXPECT_FALSE(WasResetRecorded());
256 TEST_F(ProfilePrefStoreManagerTest, GetPrefFilePathFromProfilePath) {
257 base::FilePath pref_file_path =
258 ProfilePrefStoreManager::GetPrefFilePathFromProfilePath(
259 profile_dir_.path());
261 EXPECT_FALSE(base::PathExists(pref_file_path));
263 InitializePrefs();
265 EXPECT_TRUE(base::PathExists(pref_file_path));
268 TEST_F(ProfilePrefStoreManagerTest, ProtectValues) {
269 InitializePrefs();
271 ReplaceStringInPrefs(kFoobar, kBarfoo);
272 ReplaceStringInPrefs(kHelloWorld, kGoodbyeWorld);
274 LoadExistingPrefs();
276 // kTrackedAtomic is unprotected and thus will be loaded as it appears on
277 // disk.
278 ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
280 // If preference tracking is supported, the tampered value of kProtectedAtomic
281 // will be discarded at load time, leaving this preference undefined.
282 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
283 pref_store_->GetValue(kProtectedAtomic, NULL));
284 EXPECT_EQ(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
285 WasResetRecorded());
288 TEST_F(ProfilePrefStoreManagerTest, ResetPrefHashStore) {
289 InitializePrefs();
291 manager_->ResetPrefHashStore();
293 LoadExistingPrefs();
295 // kTrackedAtomic is loaded as it appears on disk.
296 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
297 // If preference tracking is supported, kProtectedAtomic will be undefined
298 // because the value was discarded due to loss of the hash store contents.
299 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
300 pref_store_->GetValue(kProtectedAtomic, NULL));
301 EXPECT_EQ(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
302 WasResetRecorded());
305 TEST_F(ProfilePrefStoreManagerTest, ResetAllPrefHashStores) {
306 InitializePrefs();
308 ProfilePrefStoreManager::ResetAllPrefHashStores(&local_state_);
310 LoadExistingPrefs();
312 // kTrackedAtomic is loaded as it appears on disk.
313 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
314 // If preference tracking is supported, kProtectedAtomic will be undefined
315 // because the value was discarded due to loss of the hash store contents.
316 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
317 pref_store_->GetValue(kProtectedAtomic, NULL));
318 EXPECT_EQ(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
319 WasResetRecorded());
322 TEST_F(ProfilePrefStoreManagerTest, MigrateFromOneFile) {
323 InitializeDeprecatedCombinedProfilePrefStore();
325 LoadExistingPrefs();
327 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
328 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
329 EXPECT_FALSE(WasResetRecorded());
332 TEST_F(ProfilePrefStoreManagerTest, UpdateProfileHashStoreIfRequired) {
333 scoped_refptr<JsonPrefStore> legacy_prefs(
334 new JsonPrefStore(ProfilePrefStoreManager::GetPrefFilePathFromProfilePath(
335 profile_dir_.path()),
336 main_message_loop_.message_loop_proxy(),
337 scoped_ptr<PrefFilter>()));
338 legacy_prefs->SetValue(kTrackedAtomic, new base::StringValue(kFoobar));
339 legacy_prefs->SetValue(kProtectedAtomic, new base::StringValue(kHelloWorld));
340 legacy_prefs = NULL;
341 base::RunLoop().RunUntilIdle();
343 // This is a no-op if !kPlatformSupportsPreferenceTracking.
344 manager_->UpdateProfileHashStoreIfRequired(
345 main_message_loop_.message_loop_proxy());
346 base::RunLoop().RunUntilIdle();
348 // At the moment, UpdateProfileHashStoreIfRequired will accept existing
349 // values.
350 LoadExistingPrefs();
352 // These expectations hold whether or not tracking is supported.
353 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
354 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
355 EXPECT_FALSE(WasResetRecorded());
358 TEST_F(ProfilePrefStoreManagerTest, InitializePrefsFromMasterPrefs) {
359 base::DictionaryValue master_prefs;
360 master_prefs.Set(kTrackedAtomic, new base::StringValue(kFoobar));
361 master_prefs.Set(kProtectedAtomic, new base::StringValue(kHelloWorld));
362 EXPECT_TRUE(manager_->InitializePrefsFromMasterPrefs(master_prefs));
364 LoadExistingPrefs();
366 // Verify that InitializePrefsFromMasterPrefs correctly applied the MACs
367 // necessary to authenticate these values.
368 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
369 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
370 EXPECT_FALSE(WasResetRecorded());
373 TEST_F(ProfilePrefStoreManagerTest, UnprotectedToProtected) {
374 InitializePrefs();
375 LoadExistingPrefs();
376 ExpectStringValueEquals(kUnprotectedPref, kFoobar);
378 // Ensure everything is written out to disk.
379 DestroyPrefStore();
381 ReplaceStringInPrefs(kFoobar, kBarfoo);
383 // It's unprotected, so we can load the modified value.
384 LoadExistingPrefs();
385 ExpectStringValueEquals(kUnprotectedPref, kBarfoo);
387 // Now update the configuration to protect it.
388 PrefHashFilter::TrackedPreferenceMetadata new_protected = {
389 kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
390 PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
391 configuration_.push_back(new_protected);
392 ReloadConfiguration();
394 // And try loading with the new configuration.
395 LoadExistingPrefs();
397 // Since there was a valid super MAC we were able to extend the existing trust
398 // to the newly proteted preference.
399 ExpectStringValueEquals(kUnprotectedPref, kBarfoo);
400 EXPECT_FALSE(WasResetRecorded());
402 // Ensure everything is written out to disk.
403 DestroyPrefStore();
405 // It's protected now, so (if the platform supports it) any tampering should
406 // lead to a reset.
407 ReplaceStringInPrefs(kBarfoo, kFoobar);
408 LoadExistingPrefs();
409 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
410 pref_store_->GetValue(kUnprotectedPref, NULL));
411 EXPECT_EQ(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
412 WasResetRecorded());
415 TEST_F(ProfilePrefStoreManagerTest, UnprotectedToProtectedWithoutTrust) {
416 InitializePrefs();
418 // Now update the configuration to protect it.
419 PrefHashFilter::TrackedPreferenceMetadata new_protected = {
420 kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
421 PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
422 configuration_.push_back(new_protected);
423 ReloadConfiguration();
424 ProfilePrefStoreManager::ResetAllPrefHashStores(&local_state_);
426 // And try loading with the new configuration.
427 LoadExistingPrefs();
429 // If preference tracking is supported, kUnprotectedPref will have been
430 // discarded because new values are not accepted without a valid super MAC.
431 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
432 pref_store_->GetValue(kUnprotectedPref, NULL));
433 EXPECT_EQ(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
434 WasResetRecorded());
437 // This test verifies that preference values are correctly maintained when a
438 // preference's protection state changes from protected to unprotected.
439 TEST_F(ProfilePrefStoreManagerTest, ProtectedToUnprotected) {
440 InitializePrefs();
441 DestroyPrefStore();
443 // Unconfigure protection for kProtectedAtomic
444 for (std::vector<PrefHashFilter::TrackedPreferenceMetadata>::iterator it =
445 configuration_.begin();
446 it != configuration_.end();
447 ++it) {
448 if (it->name == kProtectedAtomic) {
449 it->enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
450 break;
453 ReloadConfiguration();
455 // Reset the hash stores and then try loading the prefs.
456 ProfilePrefStoreManager::ResetAllPrefHashStores(&local_state_);
457 LoadExistingPrefs();
459 // Verify that the value was not reset.
460 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
461 EXPECT_FALSE(WasResetRecorded());
463 // Accessing the value of the previously protected pref didn't trigger its
464 // move to the unprotected preferences file, though the loading of the pref
465 // store should still have caused the MAC store to be recalculated.
466 LoadExistingPrefs();
467 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
469 // Trigger the logic that migrates it back to the unprotected preferences
470 // file.
471 pref_store_->SetValue(kProtectedAtomic, new base::StringValue(kGoodbyeWorld));
472 LoadExistingPrefs();
473 ExpectStringValueEquals(kProtectedAtomic, kGoodbyeWorld);
474 EXPECT_FALSE(WasResetRecorded());