Home | History | Annotate | Download | only in prefs
      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.
      4 
      5 #include "base/memory/scoped_ptr.h"
      6 #include "base/metrics/histogram.h"
      7 #include "base/metrics/statistics_recorder.h"
      8 #include "base/prefs/scoped_user_pref_update.h"
      9 #include "base/prefs/testing_pref_service.h"
     10 #include "base/values.h"
     11 #include "chrome/browser/prefs/pref_metrics_service.h"
     12 #include "chrome/common/pref_names.h"
     13 #include "chrome/test/base/testing_browser_process.h"
     14 #include "chrome/test/base/testing_pref_service_syncable.h"
     15 #include "chrome/test/base/testing_profile.h"
     16 #include "chrome/test/base/testing_profile_manager.h"
     17 #include "components/user_prefs/pref_registry_syncable.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 namespace {
     21 
     22 // TestingProfile may register some real preferences; to avoid interference,
     23 // define fake preferences for testing.
     24 const char* kTrackedPrefs[] = {
     25   "pref_metrics_service_test.pref1",
     26   "pref_metrics_service_test.pref2",
     27 };
     28 
     29 const int kTrackedPrefCount = arraysize(kTrackedPrefs);
     30 
     31 const char kTestDeviceId[] = "test_device_id1";
     32 const char kOtherTestDeviceId[] = "test_device_id2";
     33 
     34 }  // namespace
     35 
     36 class PrefMetricsServiceTest : public testing::Test {
     37  protected:
     38   virtual void SetUp() {
     39     pref1_changed_ = 0;
     40     pref2_changed_ = 0;
     41     pref1_cleared_ = 0;
     42     pref2_cleared_ = 0;
     43     pref1_initialized_ = 0;
     44     pref2_initialized_ = 0;
     45     pref1_migrated_ = 0;
     46     pref2_migrated_ = 0;
     47     pref1_unchanged_ = 0;
     48     pref2_unchanged_ = 0;
     49 
     50     base::StatisticsRecorder::Initialize();
     51 
     52     // Reset and set up the profile manager.
     53     profile_manager_.reset(new TestingProfileManager(
     54         TestingBrowserProcess::GetGlobal()));
     55     ASSERT_TRUE(profile_manager_->SetUp());
     56 
     57     // Check that PrefMetricsService behaves with a '.' in the profile name.
     58     profile_ = profile_manager_->CreateTestingProfile("test (at) example.com");
     59 
     60     profile_name_ = profile_->GetPath().AsUTF8Unsafe();
     61 
     62     prefs_ = profile_->GetTestingPrefService();
     63 
     64     // Register our test-only tracked prefs as string values.
     65     for (int i = 0; i < kTrackedPrefCount; ++i) {
     66       prefs_->registry()->RegisterStringPref(
     67           kTrackedPrefs[i],
     68           "test_default_value",
     69           user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
     70     }
     71 
     72     // Initialize pref in local state that holds hashed values.
     73     PrefMetricsService::RegisterPrefs(local_state_.registry());
     74 
     75     // Update global counts in case another test left stray samples.
     76     UpdateHistogramSamples();
     77   }
     78 
     79   scoped_ptr<PrefMetricsService> CreatePrefMetricsService(
     80       const std::string& device_id) {
     81     return scoped_ptr<PrefMetricsService>(
     82         new PrefMetricsService(profile_,
     83                                &local_state_,
     84                                device_id,
     85                                kTrackedPrefs,
     86                                kTrackedPrefCount));
     87   }
     88 
     89   std::string GetHashedPrefValue(PrefMetricsService* service,
     90                                  const char* path,
     91                                  const base::Value* value) {
     92     return service->GetHashedPrefValue(
     93         path, value, PrefMetricsService::HASHED_PREF_STYLE_NEW);
     94   }
     95 
     96   std::string GetOldStyleHashedPrefValue(PrefMetricsService* service,
     97                                          const char* path,
     98                                          const base::Value* value) {
     99     return service->GetHashedPrefValue(
    100         path, value, PrefMetricsService::HASHED_PREF_STYLE_DEPRECATED);
    101   }
    102 
    103   void GetSamples(const char* histogram_name, int* bucket1, int* bucket2) {
    104     base::HistogramBase* histogram =
    105         base::StatisticsRecorder::FindHistogram(histogram_name);
    106     if (!histogram) {
    107       *bucket1 = 0;
    108       *bucket2 = 0;
    109     } else {
    110       scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples());
    111       *bucket1 = samples->GetCount(0);
    112       *bucket2 = samples->GetCount(1);
    113     }
    114   }
    115 
    116   void UpdateHistogramSamples() {
    117     int changed1, changed2;
    118     GetSamples("Settings.TrackedPreferenceChanged", &changed1, &changed2);
    119     pref1_changed_ = changed1 - pref1_changed_total;
    120     pref2_changed_ = changed2 - pref2_changed_total;
    121     pref1_changed_total = changed1;
    122     pref2_changed_total = changed2;
    123 
    124     int cleared1, cleared2;
    125     GetSamples("Settings.TrackedPreferenceCleared", &cleared1, &cleared2);
    126     pref1_cleared_ = cleared1 - pref1_cleared_total;
    127     pref2_cleared_ = cleared2 - pref2_cleared_total;
    128     pref1_cleared_total = cleared1;
    129     pref2_cleared_total = cleared2;
    130 
    131     int inited1, inited2;
    132     GetSamples("Settings.TrackedPreferenceInitialized", &inited1, &inited2);
    133     pref1_initialized_ = inited1 - pref1_initialized_total;
    134     pref2_initialized_ = inited2 - pref2_initialized_total;
    135     pref1_initialized_total = inited1;
    136     pref2_initialized_total = inited2;
    137 
    138     int migrated1, migrated2;
    139     GetSamples("Settings.TrackedPreferenceMigrated", &migrated1, &migrated2);
    140     pref1_migrated_ = migrated1 - pref1_migrated_total;
    141     pref2_migrated_ = migrated2 - pref2_migrated_total;
    142     pref1_migrated_total = migrated1;
    143     pref2_migrated_total = migrated2;
    144 
    145     int unchanged1, unchanged2;
    146     GetSamples("Settings.TrackedPreferenceUnchanged", &unchanged1, &unchanged2);
    147     pref1_unchanged_ = unchanged1 - pref1_unchanged_total;
    148     pref2_unchanged_ = unchanged2 - pref2_unchanged_total;
    149     pref1_unchanged_total = unchanged1;
    150     pref2_unchanged_total = unchanged2;
    151   }
    152 
    153   TestingProfile* profile_;
    154   std::string profile_name_;
    155   scoped_ptr<TestingProfileManager> profile_manager_;
    156   TestingPrefServiceSyncable* prefs_;
    157   TestingPrefServiceSimple local_state_;
    158 
    159   // Since histogram samples are recorded by a global StatisticsRecorder, we
    160   // need to maintain total counts so we can compute deltas for individual
    161   // tests.
    162   static int pref1_changed_total;
    163   static int pref2_changed_total;
    164   static int pref1_cleared_total;
    165   static int pref2_cleared_total;
    166   static int pref1_initialized_total;
    167   static int pref2_initialized_total;
    168   static int pref1_migrated_total;
    169   static int pref2_migrated_total;
    170   static int pref1_unchanged_total;
    171   static int pref2_unchanged_total;
    172 
    173   // Counts of samples recorded since UpdateHistogramSamples was last called.
    174   int pref1_changed_;
    175   int pref2_changed_;
    176   int pref1_cleared_;
    177   int pref2_cleared_;
    178   int pref1_initialized_;
    179   int pref2_initialized_;
    180   int pref1_migrated_;
    181   int pref2_migrated_;
    182   int pref1_unchanged_;
    183   int pref2_unchanged_;
    184 };
    185 
    186 int PrefMetricsServiceTest::pref1_changed_total;
    187 int PrefMetricsServiceTest::pref2_changed_total;
    188 int PrefMetricsServiceTest::pref1_cleared_total;
    189 int PrefMetricsServiceTest::pref2_cleared_total;
    190 int PrefMetricsServiceTest::pref1_initialized_total;
    191 int PrefMetricsServiceTest::pref2_initialized_total;
    192 int PrefMetricsServiceTest::pref1_migrated_total;
    193 int PrefMetricsServiceTest::pref2_migrated_total;
    194 int PrefMetricsServiceTest::pref1_unchanged_total;
    195 int PrefMetricsServiceTest::pref2_unchanged_total;
    196 
    197 TEST_F(PrefMetricsServiceTest, StartupNoUserPref) {
    198   // Local state is empty and no user prefs are set. We should still have
    199   // initialized all preferences once.
    200   scoped_ptr<PrefMetricsService> service =
    201       CreatePrefMetricsService(kTestDeviceId);
    202   UpdateHistogramSamples();
    203   EXPECT_EQ(0, pref1_changed_);
    204   EXPECT_EQ(0, pref2_changed_);
    205   EXPECT_EQ(0, pref1_cleared_);
    206   EXPECT_EQ(0, pref2_cleared_);
    207   EXPECT_EQ(1, pref1_initialized_);
    208   EXPECT_EQ(1, pref2_initialized_);
    209   EXPECT_EQ(0, pref1_migrated_);
    210   EXPECT_EQ(0, pref2_migrated_);
    211   EXPECT_EQ(0, pref1_unchanged_);
    212   EXPECT_EQ(0, pref2_unchanged_);
    213 
    214   // Ensure that each pref got a hash even though their value is NULL (i.e.,
    215   // empty).
    216   const DictionaryValue* root_dictionary =
    217       local_state_.GetDictionary(prefs::kProfilePreferenceHashes);
    218   ASSERT_TRUE(root_dictionary != NULL);
    219 
    220   const DictionaryValue* child_dictionary = NULL;
    221   ASSERT_TRUE(root_dictionary->GetDictionaryWithoutPathExpansion(
    222       profile_name_, &child_dictionary));
    223 
    224   std::string pref1_hash;
    225   std::string pref2_hash;
    226   ASSERT_TRUE(child_dictionary->GetString(kTrackedPrefs[0], &pref1_hash));
    227   ASSERT_TRUE(child_dictionary->GetString(kTrackedPrefs[1], &pref2_hash));
    228 
    229   // These two hashes are expected to be different as the paths on which they're
    230   // based differ.
    231   EXPECT_EQ("2A38C5000E1EDC2D5FA3B6A8E1D3B54068E32D329324D0D8C1AADA65BBDB20B3",
    232             pref1_hash);
    233   EXPECT_EQ("C4FEB38BDADD16CC642815B9798FEA70BF92C6CA9250BACD6993701196D72067",
    234             pref2_hash);
    235 }
    236 
    237 TEST_F(PrefMetricsServiceTest, StartupUserPref) {
    238   // Local state is empty. Set a value for one tracked pref. We should record
    239   // that we checked preferences once and initialized a hash for the pref.
    240   prefs_->SetString(kTrackedPrefs[0], "foo");
    241   {
    242     scoped_ptr<PrefMetricsService> service =
    243         CreatePrefMetricsService(kTestDeviceId);
    244     UpdateHistogramSamples();
    245     EXPECT_EQ(0, pref1_changed_);
    246     EXPECT_EQ(0, pref2_changed_);
    247     EXPECT_EQ(0, pref1_cleared_);
    248     EXPECT_EQ(0, pref2_cleared_);
    249     EXPECT_EQ(1, pref1_initialized_);
    250     EXPECT_EQ(1, pref2_initialized_);
    251     EXPECT_EQ(0, pref1_migrated_);
    252     EXPECT_EQ(0, pref2_migrated_);
    253     EXPECT_EQ(0, pref1_unchanged_);
    254     EXPECT_EQ(0, pref2_unchanged_);
    255 
    256     // Change the pref. This should be observed by the PrefMetricsService, which
    257     // will update the hash in local_state_ to stay in sync.
    258     prefs_->SetString(kTrackedPrefs[0], "bar");
    259   }
    260   // The next startup should record no changes.
    261   {
    262     scoped_ptr<PrefMetricsService> service =
    263         CreatePrefMetricsService(kTestDeviceId);
    264     UpdateHistogramSamples();
    265     EXPECT_EQ(0, pref1_changed_);
    266     EXPECT_EQ(0, pref2_changed_);
    267     EXPECT_EQ(0, pref1_cleared_);
    268     EXPECT_EQ(0, pref2_cleared_);
    269     EXPECT_EQ(0, pref1_initialized_);
    270     EXPECT_EQ(0, pref2_initialized_);
    271     EXPECT_EQ(0, pref1_migrated_);
    272     EXPECT_EQ(0, pref2_migrated_);
    273     EXPECT_EQ(1, pref1_unchanged_);
    274     EXPECT_EQ(1, pref2_unchanged_);
    275   }
    276 }
    277 
    278 TEST_F(PrefMetricsServiceTest, ChangedUserPref) {
    279   // Local state is empty. Set a value for the tracked pref. We should record
    280   // that we checked preferences once and initialized a hash for the pref.
    281   prefs_->SetString(kTrackedPrefs[0], "foo");
    282   {
    283     scoped_ptr<PrefMetricsService> service =
    284         CreatePrefMetricsService(kTestDeviceId);
    285     UpdateHistogramSamples();
    286     EXPECT_EQ(0, pref1_changed_);
    287     EXPECT_EQ(0, pref2_changed_);
    288     EXPECT_EQ(0, pref1_cleared_);
    289     EXPECT_EQ(0, pref2_cleared_);
    290     EXPECT_EQ(1, pref1_initialized_);
    291     EXPECT_EQ(1, pref2_initialized_);
    292     EXPECT_EQ(0, pref1_migrated_);
    293     EXPECT_EQ(0, pref2_migrated_);
    294     EXPECT_EQ(0, pref1_unchanged_);
    295     EXPECT_EQ(0, pref2_unchanged_);
    296     // Hashed prefs should now be stored in local state.
    297   }
    298   // Change the value of the tracked pref while there is no PrefMetricsService
    299   // to update the hash. We should observe a pref value change.
    300   prefs_->SetString(kTrackedPrefs[0], "bar");
    301   {
    302     scoped_ptr<PrefMetricsService> service =
    303         CreatePrefMetricsService(kTestDeviceId);
    304     UpdateHistogramSamples();
    305     EXPECT_EQ(1, pref1_changed_);
    306     EXPECT_EQ(0, pref2_changed_);
    307     EXPECT_EQ(0, pref1_cleared_);
    308     EXPECT_EQ(0, pref2_cleared_);
    309     EXPECT_EQ(0, pref1_initialized_);
    310     EXPECT_EQ(0, pref2_initialized_);
    311     EXPECT_EQ(0, pref1_migrated_);
    312     EXPECT_EQ(0, pref2_migrated_);
    313     EXPECT_EQ(0, pref1_unchanged_);
    314     EXPECT_EQ(1, pref2_unchanged_);
    315   }
    316   // Clear the value of the tracked pref while there is no PrefMetricsService
    317   // to update the hash. We should observe a pref value removal.
    318   prefs_->ClearPref(kTrackedPrefs[0]);
    319   {
    320     scoped_ptr<PrefMetricsService> service =
    321         CreatePrefMetricsService(kTestDeviceId);
    322     UpdateHistogramSamples();
    323     EXPECT_EQ(0, pref1_changed_);
    324     EXPECT_EQ(0, pref2_changed_);
    325     EXPECT_EQ(1, pref1_cleared_);
    326     EXPECT_EQ(0, pref2_cleared_);
    327     EXPECT_EQ(0, pref1_initialized_);
    328     EXPECT_EQ(0, pref2_initialized_);
    329     EXPECT_EQ(0, pref1_migrated_);
    330     EXPECT_EQ(0, pref2_migrated_);
    331     EXPECT_EQ(0, pref1_unchanged_);
    332     EXPECT_EQ(1, pref2_unchanged_);
    333   }
    334 }
    335 
    336 TEST_F(PrefMetricsServiceTest, MigratedUserPref) {
    337   // Initialize both preferences and get the old style hash for the first pref
    338   // from the PrefMetricsService before shutting it down.
    339   prefs_->SetString(kTrackedPrefs[0], "foo");
    340   prefs_->SetString(kTrackedPrefs[1], "bar");
    341   std::string old_style_hash;
    342   {
    343     scoped_ptr<PrefMetricsService> service =
    344         CreatePrefMetricsService(kTestDeviceId);
    345     UpdateHistogramSamples();
    346     EXPECT_EQ(0, pref1_changed_);
    347     EXPECT_EQ(0, pref2_changed_);
    348     EXPECT_EQ(0, pref1_cleared_);
    349     EXPECT_EQ(0, pref2_cleared_);
    350     EXPECT_EQ(1, pref1_initialized_);
    351     EXPECT_EQ(1, pref2_initialized_);
    352     EXPECT_EQ(0, pref1_migrated_);
    353     EXPECT_EQ(0, pref2_migrated_);
    354     EXPECT_EQ(0, pref1_unchanged_);
    355     EXPECT_EQ(0, pref2_unchanged_);
    356 
    357     old_style_hash =
    358         GetOldStyleHashedPrefValue(service.get(), kTrackedPrefs[0],
    359                                    prefs_->GetUserPrefValue(kTrackedPrefs[0]));
    360   }
    361 
    362   // Update the pref's hash to use the old style while the PrefMetricsService
    363   // isn't running.
    364   {
    365     DictionaryPrefUpdate update(&local_state_, prefs::kProfilePreferenceHashes);
    366     DictionaryValue* child_dictionary = NULL;
    367     // Get the dictionary corresponding to the profile name,
    368     // which may have a '.'
    369     ASSERT_TRUE(update->GetDictionaryWithoutPathExpansion(profile_name_,
    370                                                           &child_dictionary));
    371     child_dictionary->SetString(kTrackedPrefs[0], old_style_hash);
    372   }
    373 
    374   // Relaunch the service and make sure the first preference got migrated.
    375   {
    376     scoped_ptr<PrefMetricsService> service =
    377         CreatePrefMetricsService(kTestDeviceId);
    378     UpdateHistogramSamples();
    379     EXPECT_EQ(0, pref1_changed_);
    380     EXPECT_EQ(0, pref2_changed_);
    381     EXPECT_EQ(0, pref1_cleared_);
    382     EXPECT_EQ(0, pref2_cleared_);
    383     EXPECT_EQ(0, pref1_initialized_);
    384     EXPECT_EQ(0, pref2_initialized_);
    385     EXPECT_EQ(1, pref1_migrated_);
    386     EXPECT_EQ(0, pref2_migrated_);
    387     EXPECT_EQ(0, pref1_unchanged_);
    388     EXPECT_EQ(1, pref2_unchanged_);
    389   }
    390   // Make sure the migration happens only once.
    391   {
    392     scoped_ptr<PrefMetricsService> service =
    393         CreatePrefMetricsService(kTestDeviceId);
    394     UpdateHistogramSamples();
    395     EXPECT_EQ(0, pref1_changed_);
    396     EXPECT_EQ(0, pref2_changed_);
    397     EXPECT_EQ(0, pref1_cleared_);
    398     EXPECT_EQ(0, pref2_cleared_);
    399     EXPECT_EQ(0, pref1_initialized_);
    400     EXPECT_EQ(0, pref2_initialized_);
    401     EXPECT_EQ(0, pref1_migrated_);
    402     EXPECT_EQ(0, pref2_migrated_);
    403     EXPECT_EQ(1, pref1_unchanged_);
    404     EXPECT_EQ(1, pref2_unchanged_);
    405   }
    406 }
    407 
    408 // Make sure that the new algorithm is still able to generate old style hashes
    409 // as they were before this change.
    410 TEST_F(PrefMetricsServiceTest, OldStyleHashAsExpected) {
    411   scoped_ptr<PrefMetricsService> service =
    412       CreatePrefMetricsService(kTestDeviceId);
    413 
    414   // Verify the hashes match the values previously used in the
    415   // "PrefHashStability" test below.
    416   DictionaryValue dict;
    417   dict.Set("a", new StringValue("foo"));
    418   dict.Set("d", new StringValue("bad"));
    419   dict.Set("b", new StringValue("bar"));
    420   dict.Set("c", new StringValue("baz"));
    421   EXPECT_EQ("C503FB7C65EEFD5C07185F616A0AA67923C069909933F362022B1F187E73E9A2",
    422             GetOldStyleHashedPrefValue(service.get(), "pref.path1", &dict));
    423   ListValue list;
    424   list.Set(0, new base::FundamentalValue(true));
    425   list.Set(1, new base::FundamentalValue(100));
    426   list.Set(2, new base::FundamentalValue(1.0));
    427   EXPECT_EQ("3163EC3C96263143AF83EA5C9860DFB960EE2263413C7D7D8A9973FCC00E7692",
    428             GetOldStyleHashedPrefValue(service.get(), "pref.path2", &list));
    429 }
    430 
    431 // Tests that serialization of dictionary values is stable. If the order of
    432 // the entries or any whitespace changes, it would cause a spike in pref change
    433 // UMA events as every hash would change.
    434 TEST_F(PrefMetricsServiceTest, PrefHashStability) {
    435   scoped_ptr<PrefMetricsService> service =
    436       CreatePrefMetricsService(kTestDeviceId);
    437 
    438   DictionaryValue dict;
    439   dict.Set("a", new StringValue("foo"));
    440   dict.Set("d", new StringValue("bad"));
    441   dict.Set("b", new StringValue("bar"));
    442   dict.Set("c", new StringValue("baz"));
    443   EXPECT_EQ("A50FE7EB31BFBC32B8A27E71730AF15421178A9B5815644ACE174B18966735B9",
    444             GetHashedPrefValue(service.get(), "pref.path1", &dict));
    445 
    446   ListValue list;
    447   list.Set(0, new base::FundamentalValue(true));
    448   list.Set(1, new base::FundamentalValue(100));
    449   list.Set(2, new base::FundamentalValue(1.0));
    450   EXPECT_EQ("5CE37D7EBCBC9BE510F0F5E7C326CA92C1673713C3717839610AEA1A217D8BB8",
    451             GetHashedPrefValue(service.get(), "pref.path2", &list));
    452 }
    453 
    454 // Tests that different hashes are generated for different device IDs.
    455 TEST_F(PrefMetricsServiceTest, HashIsBasedOnDeviceId) {
    456   scoped_ptr<PrefMetricsService> service =
    457       CreatePrefMetricsService(kTestDeviceId);
    458   scoped_ptr<PrefMetricsService> other_service =
    459       CreatePrefMetricsService(kOtherTestDeviceId);
    460 
    461   StringValue test_value("test value");
    462   EXPECT_EQ("49CA276F9F2AEDCF6BFA1CD9FC4747476E1315BBBBC27DD33548B23CD36E2EEE",
    463             GetHashedPrefValue(service.get(), "pref.path", &test_value));
    464   EXPECT_EQ("13EEDA99C38777ADA8B87C23A3C5CD1FD31ADE1491823E255D3520E5B56C4BC7",
    465             GetHashedPrefValue(other_service.get(), "pref.path", &test_value));
    466 }
    467 
    468 // Tests that different hashes are generated for different paths.
    469 TEST_F(PrefMetricsServiceTest, HashIsBasedOnPath) {
    470   scoped_ptr<PrefMetricsService> service =
    471       CreatePrefMetricsService(kTestDeviceId);
    472 
    473   StringValue test_value("test value");
    474   EXPECT_EQ("2A5DCB1294F212DB26DF9C08C46F11C272D80136AAD3B4AAE5B7D008DF5F3F22",
    475             GetHashedPrefValue(service.get(), "pref.path1", &test_value));
    476   EXPECT_EQ("455EC2A7E192E9F1C06294BBB3B66BBD81B8D1A8550D518EA5D5C8F70FCF6EF3",
    477             GetHashedPrefValue(service.get(), "pref.path2", &test_value));
    478 }
    479