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 20namespace { 21 22// TestingProfile may register some real preferences; to avoid interference, 23// define fake preferences for testing. 24const char* kTrackedPrefs[] = { 25 "pref_metrics_service_test.pref1", 26 "pref_metrics_service_test.pref2", 27}; 28 29const int kTrackedPrefCount = arraysize(kTrackedPrefs); 30 31const char kTestDeviceId[] = "test_device_id1"; 32const char kOtherTestDeviceId[] = "test_device_id2"; 33 34} // namespace 35 36class 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@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 186int PrefMetricsServiceTest::pref1_changed_total; 187int PrefMetricsServiceTest::pref2_changed_total; 188int PrefMetricsServiceTest::pref1_cleared_total; 189int PrefMetricsServiceTest::pref2_cleared_total; 190int PrefMetricsServiceTest::pref1_initialized_total; 191int PrefMetricsServiceTest::pref2_initialized_total; 192int PrefMetricsServiceTest::pref1_migrated_total; 193int PrefMetricsServiceTest::pref2_migrated_total; 194int PrefMetricsServiceTest::pref1_unchanged_total; 195int PrefMetricsServiceTest::pref2_unchanged_total; 196 197TEST_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 237TEST_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 278TEST_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 336TEST_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. 410TEST_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. 434TEST_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. 455TEST_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. 469TEST_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