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.
4
5#include "chrome/browser/prefs/profile_pref_store_manager.h"
6
7#include <vector>
8
9#include "base/compiler_specific.h"
10#include "base/files/file_enumerator.h"
11#include "base/files/file_util.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/mock_validation_delegate.h"
26#include "chrome/browser/prefs/pref_hash_filter.h"
27#include "chrome/browser/prefs/tracked/pref_service_hash_store_contents.h"
28#include "chrome/common/pref_names.h"
29#include "components/pref_registry/pref_registry_syncable.h"
30#include "testing/gtest/include/gtest/gtest.h"
31
32namespace {
33
34class FirstEqualsPredicate {
35 public:
36  explicit FirstEqualsPredicate(const std::string& expected)
37      : expected_(expected) {}
38  bool operator()(const std::pair<std::string, base::Value*>& pair) {
39    return pair.first == expected_;
40  }
41
42 private:
43  const std::string expected_;
44};
45
46// Observes changes to the PrefStore and verifies that only registered prefs are
47// written.
48class RegistryVerifier : public PrefStore::Observer {
49 public:
50  explicit RegistryVerifier(PrefRegistry* pref_registry)
51      : pref_registry_(pref_registry) {}
52
53  // PrefStore::Observer implementation
54  virtual void OnPrefValueChanged(const std::string& key) OVERRIDE {
55    EXPECT_TRUE(pref_registry_->end() !=
56                std::find_if(pref_registry_->begin(),
57                             pref_registry_->end(),
58                             FirstEqualsPredicate(key)))
59        << "Unregistered key " << key << " was changed.";
60  }
61
62  virtual void OnInitializationCompleted(bool succeeded) OVERRIDE {}
63
64 private:
65  scoped_refptr<PrefRegistry> pref_registry_;
66};
67
68const char kUnprotectedPref[] = "unprotected_pref";
69const char kTrackedAtomic[] = "tracked_atomic";
70const char kProtectedAtomic[] = "protected_atomic";
71
72const char kFoobar[] = "FOOBAR";
73const char kBarfoo[] = "BARFOO";
74const char kHelloWorld[] = "HELLOWORLD";
75const char kGoodbyeWorld[] = "GOODBYEWORLD";
76
77const PrefHashFilter::TrackedPreferenceMetadata kConfiguration[] = {
78    {0u, kTrackedAtomic, PrefHashFilter::NO_ENFORCEMENT,
79     PrefHashFilter::TRACKING_STRATEGY_ATOMIC},
80    {1u, kProtectedAtomic, PrefHashFilter::ENFORCE_ON_LOAD,
81     PrefHashFilter::TRACKING_STRATEGY_ATOMIC}};
82
83const size_t kExtraReportingId = 2u;
84const size_t kReportingIdCount = 3u;
85
86}  // namespace
87
88class ProfilePrefStoreManagerTest : public testing::Test {
89 public:
90  ProfilePrefStoreManagerTest()
91      : configuration_(kConfiguration,
92                       kConfiguration + arraysize(kConfiguration)),
93        profile_pref_registry_(new user_prefs::PrefRegistrySyncable),
94        registry_verifier_(profile_pref_registry_.get()),
95        seed_("seed"),
96        reset_recorded_(false) {}
97
98  virtual void SetUp() OVERRIDE {
99    ProfilePrefStoreManager::RegisterPrefs(local_state_.registry());
100    ProfilePrefStoreManager::RegisterProfilePrefs(profile_pref_registry_.get());
101    for (const PrefHashFilter::TrackedPreferenceMetadata* it = kConfiguration;
102         it != kConfiguration + arraysize(kConfiguration);
103         ++it) {
104      if (it->strategy == PrefHashFilter::TRACKING_STRATEGY_ATOMIC) {
105        profile_pref_registry_->RegisterStringPref(
106            it->name,
107            std::string(),
108            user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
109      } else {
110        profile_pref_registry_->RegisterDictionaryPref(
111            it->name, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
112      }
113    }
114    profile_pref_registry_->RegisterStringPref(
115        kUnprotectedPref,
116        std::string(),
117        user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
118
119    // As in chrome_pref_service_factory.cc, kPreferencesResetTime needs to be
120    // declared as protected in order to be read from the proper store by the
121    // SegregatedPrefStore. Only declare it after configured prefs have been
122    // registered above for this test as kPreferenceResetTime is already
123    // registered in ProfilePrefStoreManager::RegisterProfilePrefs.
124    PrefHashFilter::TrackedPreferenceMetadata pref_reset_time_config =
125        {configuration_.rbegin()->reporting_id + 1, prefs::kPreferenceResetTime,
126         PrefHashFilter::ENFORCE_ON_LOAD,
127         PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
128    configuration_.push_back(pref_reset_time_config);
129
130    ASSERT_TRUE(profile_dir_.CreateUniqueTempDir());
131    ReloadConfiguration();
132  }
133
134  void ReloadConfiguration() {
135    manager_.reset(new ProfilePrefStoreManager(profile_dir_.path(),
136                                               configuration_,
137                                               kReportingIdCount,
138                                               seed_,
139                                               "device_id",
140                                               &local_state_));
141  }
142
143  virtual void TearDown() OVERRIDE { DestroyPrefStore(); }
144
145 protected:
146  // Verifies whether a reset was reported via the RecordReset() hook. Also
147  // verifies that GetResetTime() was set (or not) accordingly.
148  void VerifyResetRecorded(bool reset_expected) {
149    EXPECT_EQ(reset_expected, reset_recorded_);
150
151    base::PrefServiceFactory pref_service_factory;
152    pref_service_factory.set_user_prefs(pref_store_);
153
154    scoped_ptr<PrefService> pref_service(
155        pref_service_factory.Create(profile_pref_registry_.get()));
156
157    EXPECT_EQ(
158        reset_expected,
159        !ProfilePrefStoreManager::GetResetTime(pref_service.get()).is_null());
160  }
161
162  void ClearResetRecorded() {
163    reset_recorded_ = false;
164
165    base::PrefServiceFactory pref_service_factory;
166    pref_service_factory.set_user_prefs(pref_store_);
167
168    scoped_ptr<PrefService> pref_service(
169        pref_service_factory.Create(profile_pref_registry_.get()));
170
171    ProfilePrefStoreManager::ClearResetTime(pref_service.get());
172  }
173
174  void InitializePrefs() {
175    // According to the implementation of ProfilePrefStoreManager, this is
176    // actually a SegregatedPrefStore backed by two underlying pref stores.
177    scoped_refptr<PersistentPrefStore> pref_store =
178        manager_->CreateProfilePrefStore(
179            main_message_loop_.message_loop_proxy(),
180            base::Bind(&ProfilePrefStoreManagerTest::RecordReset,
181                       base::Unretained(this)),
182            &mock_validation_delegate_);
183    InitializePrefStore(pref_store.get());
184    pref_store = NULL;
185    base::RunLoop().RunUntilIdle();
186  }
187
188  void DestroyPrefStore() {
189    if (pref_store_.get()) {
190      ClearResetRecorded();
191      // Force everything to be written to disk, triggering the PrefHashFilter
192      // while our RegistryVerifier is watching.
193      pref_store_->CommitPendingWrite();
194      base::RunLoop().RunUntilIdle();
195
196      pref_store_->RemoveObserver(&registry_verifier_);
197      pref_store_ = NULL;
198      // Nothing should have to happen on the background threads, but just in
199      // case...
200      base::RunLoop().RunUntilIdle();
201    }
202  }
203
204  void InitializeDeprecatedCombinedProfilePrefStore() {
205    scoped_refptr<PersistentPrefStore> pref_store =
206        manager_->CreateDeprecatedCombinedProfilePrefStore(
207            main_message_loop_.message_loop_proxy());
208    InitializePrefStore(pref_store.get());
209    pref_store = NULL;
210    base::RunLoop().RunUntilIdle();
211  }
212
213  void InitializePrefStore(PersistentPrefStore* pref_store) {
214    pref_store->AddObserver(&registry_verifier_);
215    PersistentPrefStore::PrefReadError error = pref_store->ReadPrefs();
216    EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, error);
217    pref_store->SetValue(kTrackedAtomic, new base::StringValue(kFoobar));
218    pref_store->SetValue(kProtectedAtomic, new base::StringValue(kHelloWorld));
219    pref_store->SetValue(kUnprotectedPref, new base::StringValue(kFoobar));
220    pref_store->RemoveObserver(&registry_verifier_);
221    pref_store->CommitPendingWrite();
222    base::RunLoop().RunUntilIdle();
223  }
224
225  void LoadExistingPrefs() {
226    DestroyPrefStore();
227    pref_store_ = manager_->CreateProfilePrefStore(
228        main_message_loop_.message_loop_proxy(),
229        base::Bind(&ProfilePrefStoreManagerTest::RecordReset,
230                   base::Unretained(this)),
231        NULL);
232    pref_store_->AddObserver(&registry_verifier_);
233    pref_store_->ReadPrefs();
234  }
235
236  void ReplaceStringInPrefs(const std::string& find,
237                            const std::string& replace) {
238    base::FileEnumerator file_enum(
239        profile_dir_.path(), true, base::FileEnumerator::FILES);
240
241    for (base::FilePath path = file_enum.Next(); !path.empty();
242         path = file_enum.Next()) {
243      // Tamper with the file's contents
244      std::string contents;
245      EXPECT_TRUE(base::ReadFileToString(path, &contents));
246      ReplaceSubstringsAfterOffset(&contents, 0u, find, replace);
247      EXPECT_EQ(static_cast<int>(contents.length()),
248                base::WriteFile(path, contents.c_str(), contents.length()));
249    }
250  }
251
252  void ExpectStringValueEquals(const std::string& name,
253                               const std::string& expected) {
254    const base::Value* value = NULL;
255    std::string as_string;
256    if (!pref_store_->GetValue(name, &value)) {
257      ADD_FAILURE() << name << " is not a defined value.";
258    } else if (!value->GetAsString(&as_string)) {
259      ADD_FAILURE() << name << " could not be coerced to a string.";
260    } else {
261      EXPECT_EQ(expected, as_string);
262    }
263  }
264
265  void ExpectValidationObserved(const std::string& pref_path) {
266    // No validations are expected for platforms that do not support tracking.
267    if (!ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking)
268      return;
269    if (!mock_validation_delegate_.GetEventForPath(pref_path))
270      ADD_FAILURE() << "No validation observed for preference: " << pref_path;
271  }
272
273  base::MessageLoop main_message_loop_;
274  std::vector<PrefHashFilter::TrackedPreferenceMetadata> configuration_;
275  base::ScopedTempDir profile_dir_;
276  TestingPrefServiceSimple local_state_;
277  scoped_refptr<user_prefs::PrefRegistrySyncable> profile_pref_registry_;
278  RegistryVerifier registry_verifier_;
279  MockValidationDelegate mock_validation_delegate_;
280  scoped_ptr<ProfilePrefStoreManager> manager_;
281  scoped_refptr<PersistentPrefStore> pref_store_;
282
283  std::string seed_;
284
285 private:
286  void RecordReset() {
287    // As-is |reset_recorded_| is only designed to remember a single reset, make
288    // sure none was previously recorded (or that ClearResetRecorded() was
289    // called).
290    EXPECT_FALSE(reset_recorded_);
291    reset_recorded_ = true;
292  }
293
294  bool reset_recorded_;
295};
296
297TEST_F(ProfilePrefStoreManagerTest, StoreValues) {
298  InitializePrefs();
299
300  LoadExistingPrefs();
301
302  ExpectStringValueEquals(kTrackedAtomic, kFoobar);
303  ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
304  VerifyResetRecorded(false);
305  ExpectValidationObserved(kTrackedAtomic);
306  ExpectValidationObserved(kProtectedAtomic);
307}
308
309TEST_F(ProfilePrefStoreManagerTest, GetPrefFilePathFromProfilePath) {
310  base::FilePath pref_file_path =
311      ProfilePrefStoreManager::GetPrefFilePathFromProfilePath(
312          profile_dir_.path());
313
314  EXPECT_FALSE(base::PathExists(pref_file_path));
315
316  InitializePrefs();
317
318  EXPECT_TRUE(base::PathExists(pref_file_path));
319}
320
321TEST_F(ProfilePrefStoreManagerTest, ProtectValues) {
322  InitializePrefs();
323
324  ReplaceStringInPrefs(kFoobar, kBarfoo);
325  ReplaceStringInPrefs(kHelloWorld, kGoodbyeWorld);
326
327  LoadExistingPrefs();
328
329  // kTrackedAtomic is unprotected and thus will be loaded as it appears on
330  // disk.
331  ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
332
333  // If preference tracking is supported, the tampered value of kProtectedAtomic
334  // will be discarded at load time, leaving this preference undefined.
335  EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
336            pref_store_->GetValue(kProtectedAtomic, NULL));
337  VerifyResetRecorded(
338      ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
339
340  ExpectValidationObserved(kTrackedAtomic);
341  ExpectValidationObserved(kProtectedAtomic);
342}
343
344TEST_F(ProfilePrefStoreManagerTest, MigrateFromOneFile) {
345  InitializeDeprecatedCombinedProfilePrefStore();
346
347  // The deprecated model stores hashes in local state (on supported
348  // platforms)..
349  ASSERT_EQ(
350      ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
351      local_state_.GetUserPrefValue(
352          PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
353
354  LoadExistingPrefs();
355
356  // After a first migration, the hashes were copied to the two user preference
357  // files but were not cleaned.
358  ASSERT_EQ(
359      ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
360      local_state_.GetUserPrefValue(
361          PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
362
363  ExpectStringValueEquals(kTrackedAtomic, kFoobar);
364  ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
365  VerifyResetRecorded(false);
366
367  LoadExistingPrefs();
368
369  // In a subsequent launch, the local state hash store should be reset.
370  ASSERT_FALSE(local_state_.GetUserPrefValue(
371      PrefServiceHashStoreContents::kProfilePreferenceHashes));
372
373  ExpectStringValueEquals(kTrackedAtomic, kFoobar);
374  ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
375  VerifyResetRecorded(false);
376}
377
378TEST_F(ProfilePrefStoreManagerTest, MigrateWithTampering) {
379  InitializeDeprecatedCombinedProfilePrefStore();
380
381  ReplaceStringInPrefs(kFoobar, kBarfoo);
382  ReplaceStringInPrefs(kHelloWorld, kGoodbyeWorld);
383
384  // The deprecated model stores hashes in local state (on supported
385  // platforms)..
386  ASSERT_EQ(
387      ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
388      local_state_.GetUserPrefValue(
389          PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
390
391  LoadExistingPrefs();
392
393  // After a first migration, the hashes were copied to the two user preference
394  // files but were not cleaned.
395  ASSERT_EQ(
396      ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
397      local_state_.GetUserPrefValue(
398          PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
399
400  // kTrackedAtomic is unprotected and thus will be loaded as it appears on
401  // disk.
402  ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
403
404  // If preference tracking is supported, the tampered value of kProtectedAtomic
405  // will be discarded at load time, leaving this preference undefined.
406  EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
407            pref_store_->GetValue(kProtectedAtomic, NULL));
408  VerifyResetRecorded(
409      ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
410
411  LoadExistingPrefs();
412
413  // In a subsequent launch, the local state hash store would be reset.
414  ASSERT_FALSE(local_state_.GetUserPrefValue(
415      PrefServiceHashStoreContents::kProfilePreferenceHashes));
416
417  ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
418  VerifyResetRecorded(false);
419}
420
421TEST_F(ProfilePrefStoreManagerTest, InitializePrefsFromMasterPrefs) {
422  base::DictionaryValue master_prefs;
423  master_prefs.Set(kTrackedAtomic, new base::StringValue(kFoobar));
424  master_prefs.Set(kProtectedAtomic, new base::StringValue(kHelloWorld));
425  EXPECT_TRUE(manager_->InitializePrefsFromMasterPrefs(master_prefs));
426
427  LoadExistingPrefs();
428
429  // Verify that InitializePrefsFromMasterPrefs correctly applied the MACs
430  // necessary to authenticate these values.
431  ExpectStringValueEquals(kTrackedAtomic, kFoobar);
432  ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
433  VerifyResetRecorded(false);
434}
435
436TEST_F(ProfilePrefStoreManagerTest, UnprotectedToProtected) {
437  InitializePrefs();
438
439  ExpectValidationObserved(kTrackedAtomic);
440  ExpectValidationObserved(kProtectedAtomic);
441
442  LoadExistingPrefs();
443  ExpectStringValueEquals(kUnprotectedPref, kFoobar);
444
445  // Ensure everything is written out to disk.
446  DestroyPrefStore();
447
448  ReplaceStringInPrefs(kFoobar, kBarfoo);
449
450  // It's unprotected, so we can load the modified value.
451  LoadExistingPrefs();
452  ExpectStringValueEquals(kUnprotectedPref, kBarfoo);
453
454  // Now update the configuration to protect it.
455  PrefHashFilter::TrackedPreferenceMetadata new_protected = {
456      kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
457      PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
458  configuration_.push_back(new_protected);
459  ReloadConfiguration();
460
461  // And try loading with the new configuration.
462  LoadExistingPrefs();
463
464  // Since there was a valid super MAC we were able to extend the existing trust
465  // to the newly protected preference.
466  ExpectStringValueEquals(kUnprotectedPref, kBarfoo);
467  VerifyResetRecorded(false);
468
469  // Ensure everything is written out to disk.
470  DestroyPrefStore();
471
472  // It's protected now, so (if the platform supports it) any tampering should
473  // lead to a reset.
474  ReplaceStringInPrefs(kBarfoo, kFoobar);
475  LoadExistingPrefs();
476  EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
477            pref_store_->GetValue(kUnprotectedPref, NULL));
478  VerifyResetRecorded(
479      ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
480}
481
482TEST_F(ProfilePrefStoreManagerTest, NewPrefWhenFirstProtecting) {
483  std::vector<PrefHashFilter::TrackedPreferenceMetadata>
484      original_configuration = configuration_;
485  for (std::vector<PrefHashFilter::TrackedPreferenceMetadata>::iterator it =
486           configuration_.begin();
487       it != configuration_.end();
488       ++it) {
489    it->enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
490  }
491  ReloadConfiguration();
492
493  InitializePrefs();
494
495  ExpectValidationObserved(kTrackedAtomic);
496  ExpectValidationObserved(kProtectedAtomic);
497
498  LoadExistingPrefs();
499  ExpectStringValueEquals(kUnprotectedPref, kFoobar);
500
501  // Ensure everything is written out to disk.
502  DestroyPrefStore();
503
504  // Now introduce protection, including the never-before tracked "new_pref".
505  configuration_ = original_configuration;
506  PrefHashFilter::TrackedPreferenceMetadata new_protected = {
507      kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
508      PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
509  configuration_.push_back(new_protected);
510  ReloadConfiguration();
511
512  // And try loading with the new configuration.
513  LoadExistingPrefs();
514
515  // Since there was a valid super MAC we were able to extend the existing trust
516  // to the newly tracked & protected preference.
517  ExpectStringValueEquals(kUnprotectedPref, kFoobar);
518  VerifyResetRecorded(false);
519}
520
521TEST_F(ProfilePrefStoreManagerTest, UnprotectedToProtectedWithoutTrust) {
522  InitializePrefs();
523
524  ExpectValidationObserved(kTrackedAtomic);
525  ExpectValidationObserved(kProtectedAtomic);
526
527  // Now update the configuration to protect it.
528  PrefHashFilter::TrackedPreferenceMetadata new_protected = {
529      kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
530      PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
531  configuration_.push_back(new_protected);
532  seed_ = "new-seed-to-break-trust";
533  ReloadConfiguration();
534
535  // And try loading with the new configuration.
536  LoadExistingPrefs();
537
538  // If preference tracking is supported, kUnprotectedPref will have been
539  // discarded because new values are not accepted without a valid super MAC.
540  EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
541            pref_store_->GetValue(kUnprotectedPref, NULL));
542  VerifyResetRecorded(
543      ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
544}
545
546// This test verifies that preference values are correctly maintained when a
547// preference's protection state changes from protected to unprotected.
548TEST_F(ProfilePrefStoreManagerTest, ProtectedToUnprotected) {
549  InitializePrefs();
550
551  ExpectValidationObserved(kTrackedAtomic);
552  ExpectValidationObserved(kProtectedAtomic);
553
554  DestroyPrefStore();
555
556  // Unconfigure protection for kProtectedAtomic
557  for (std::vector<PrefHashFilter::TrackedPreferenceMetadata>::iterator it =
558           configuration_.begin();
559       it != configuration_.end();
560       ++it) {
561    if (it->name == kProtectedAtomic) {
562      it->enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
563      break;
564    }
565  }
566
567  seed_ = "new-seed-to-break-trust";
568  ReloadConfiguration();
569  LoadExistingPrefs();
570
571  // Verify that the value was not reset.
572  ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
573  VerifyResetRecorded(false);
574
575  // Accessing the value of the previously protected pref didn't trigger its
576  // move to the unprotected preferences file, though the loading of the pref
577  // store should still have caused the MAC store to be recalculated.
578  LoadExistingPrefs();
579  ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
580
581  // Trigger the logic that migrates it back to the unprotected preferences
582  // file.
583  pref_store_->SetValue(kProtectedAtomic, new base::StringValue(kGoodbyeWorld));
584  LoadExistingPrefs();
585  ExpectStringValueEquals(kProtectedAtomic, kGoodbyeWorld);
586  VerifyResetRecorded(false);
587}
588