1 // Copyright (c) 2012 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/metrics/variations/variations_service.h" 6 7 #include <vector> 8 9 #include "base/base64.h" 10 #include "base/prefs/testing_pref_service.h" 11 #include "base/sha1.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_util.h" 14 #include "chrome/browser/web_resource/resource_request_allowed_notifier_test_util.h" 15 #include "chrome/common/pref_names.h" 16 #include "chrome/test/base/testing_browser_process.h" 17 #include "chrome/test/base/testing_pref_service_syncable.h" 18 #include "components/variations/proto/study.pb.h" 19 #include "components/variations/proto/variations_seed.pb.h" 20 #include "content/public/test/test_browser_thread.h" 21 #include "net/base/url_util.h" 22 #include "net/http/http_response_headers.h" 23 #include "net/http/http_status_code.h" 24 #include "net/url_request/test_url_fetcher_factory.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 27 #if defined(OS_CHROMEOS) 28 #include "chrome/browser/chromeos/settings/cros_settings.h" 29 #include "chrome/browser/chromeos/settings/device_settings_service.h" 30 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" 31 #endif 32 33 namespace chrome_variations { 34 35 namespace { 36 37 // A test class used to validate expected functionality in VariationsService. 38 class TestVariationsService : public VariationsService { 39 public: 40 TestVariationsService(TestRequestAllowedNotifier* test_notifier, 41 PrefService* local_state) 42 : VariationsService(test_notifier, local_state, NULL), 43 intercepts_fetch_(true), 44 fetch_attempted_(false), 45 seed_stored_(false) { 46 // Set this so StartRepeatedVariationsSeedFetch can be called in tests. 47 SetCreateTrialsFromSeedCalledForTesting(true); 48 } 49 50 virtual ~TestVariationsService() { 51 } 52 53 void set_intercepts_fetch(bool value) { 54 intercepts_fetch_ = value; 55 } 56 57 bool fetch_attempted() const { return fetch_attempted_; } 58 59 bool seed_stored() const { return seed_stored_; } 60 61 virtual void DoActualFetch() OVERRIDE { 62 if (intercepts_fetch_) { 63 fetch_attempted_ = true; 64 return; 65 } 66 67 VariationsService::DoActualFetch(); 68 } 69 70 protected: 71 virtual void StoreSeed(const std::string& seed_data, 72 const std::string& seed_signature, 73 const base::Time& date_fetched) OVERRIDE { 74 seed_stored_ = true; 75 } 76 77 private: 78 bool intercepts_fetch_; 79 bool fetch_attempted_; 80 bool seed_stored_; 81 82 DISALLOW_COPY_AND_ASSIGN(TestVariationsService); 83 }; 84 85 class TestVariationsServiceObserver : public VariationsService::Observer { 86 public: 87 TestVariationsServiceObserver() 88 : best_effort_changes_notified_(0), 89 crticial_changes_notified_(0) { 90 } 91 virtual ~TestVariationsServiceObserver() { 92 } 93 94 virtual void OnExperimentChangesDetected(Severity severity) OVERRIDE { 95 switch (severity) { 96 case BEST_EFFORT: 97 ++best_effort_changes_notified_; 98 break; 99 case CRITICAL: 100 ++crticial_changes_notified_; 101 break; 102 } 103 } 104 105 int best_effort_changes_notified() const { 106 return best_effort_changes_notified_; 107 } 108 109 int crticial_changes_notified() const { 110 return crticial_changes_notified_; 111 } 112 113 private: 114 // Number of notification received with BEST_EFFORT severity. 115 int best_effort_changes_notified_; 116 117 // Number of notification received with CRITICAL severity. 118 int crticial_changes_notified_; 119 120 DISALLOW_COPY_AND_ASSIGN(TestVariationsServiceObserver); 121 }; 122 123 // Populates |seed| with simple test data. The resulting seed will contain one 124 // study called "test", which contains one experiment called "abc" with 125 // probability weight 100. |seed|'s study field will be cleared before adding 126 // the new study. 127 variations::VariationsSeed CreateTestSeed() { 128 variations::VariationsSeed seed; 129 variations::Study* study = seed.add_study(); 130 study->set_name("test"); 131 study->set_default_experiment_name("abc"); 132 variations::Study_Experiment* experiment = study->add_experiment(); 133 experiment->set_name("abc"); 134 experiment->set_probability_weight(100); 135 seed.set_serial_number("123"); 136 return seed; 137 } 138 139 // Serializes |seed| to protobuf binary format. 140 std::string SerializeSeed(const variations::VariationsSeed& seed) { 141 std::string serialized_seed; 142 seed.SerializeToString(&serialized_seed); 143 return serialized_seed; 144 } 145 146 // Simulates a variations service response by setting a date header and the 147 // specified HTTP |response_code| on |fetcher|. 148 void SimulateServerResponse(int response_code, net::TestURLFetcher* fetcher) { 149 ASSERT_TRUE(fetcher); 150 scoped_refptr<net::HttpResponseHeaders> headers( 151 new net::HttpResponseHeaders("date:Wed, 13 Feb 2013 00:25:24 GMT\0\0")); 152 fetcher->set_response_headers(headers); 153 fetcher->set_response_code(response_code); 154 } 155 156 } // namespace 157 158 class VariationsServiceTest : public ::testing::Test { 159 protected: 160 VariationsServiceTest() {} 161 162 private: 163 #if defined(OS_CHROMEOS) 164 // Not used directly. Initializes CrosSettings for testing. 165 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; 166 chromeos::ScopedTestCrosSettings test_cros_settings_; 167 #endif 168 169 DISALLOW_COPY_AND_ASSIGN(VariationsServiceTest); 170 }; 171 172 #if !defined(OS_CHROMEOS) 173 TEST_F(VariationsServiceTest, VariationsURLIsValid) { 174 #if defined(OS_ANDROID) 175 // Android uses profile prefs as the PrefService to generate the URL. 176 TestingPrefServiceSyncable prefs; 177 VariationsService::RegisterProfilePrefs(prefs.registry()); 178 #else 179 TestingPrefServiceSimple prefs; 180 VariationsService::RegisterPrefs(prefs.registry()); 181 #endif 182 const std::string default_variations_url = 183 VariationsService::GetDefaultVariationsServerURLForTesting(); 184 185 std::string value; 186 GURL url = VariationsService::GetVariationsServerURL(&prefs); 187 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 188 EXPECT_FALSE(net::GetValueForKeyInQuery(url, "restrict", &value)); 189 190 prefs.SetString(prefs::kVariationsRestrictParameter, "restricted"); 191 url = VariationsService::GetVariationsServerURL(&prefs); 192 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 193 EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value)); 194 EXPECT_EQ("restricted", value); 195 } 196 #else 197 class VariationsServiceTestChromeOS : public VariationsServiceTest { 198 protected: 199 VariationsServiceTestChromeOS() {} 200 201 virtual void SetUp() OVERRIDE { 202 cros_settings_ = chromeos::CrosSettings::Get(); 203 DCHECK(cros_settings_ != NULL); 204 // Remove the real DeviceSettingsProvider and replace it with a stub that 205 // allows modifications in a test. 206 device_settings_provider_ = cros_settings_->GetProvider( 207 chromeos::kReportDeviceVersionInfo); 208 EXPECT_TRUE(device_settings_provider_ != NULL); 209 EXPECT_TRUE(cros_settings_->RemoveSettingsProvider( 210 device_settings_provider_)); 211 cros_settings_->AddSettingsProvider(&stub_settings_provider_); 212 } 213 214 virtual void TearDown() OVERRIDE { 215 // Restore the real DeviceSettingsProvider. 216 EXPECT_TRUE( 217 cros_settings_->RemoveSettingsProvider(&stub_settings_provider_)); 218 cros_settings_->AddSettingsProvider(device_settings_provider_); 219 } 220 221 void SetVariationsRestrictParameterPolicyValue(std::string value) { 222 cros_settings_->SetString(chromeos::kVariationsRestrictParameter, value); 223 } 224 225 private: 226 chromeos::CrosSettings* cros_settings_; 227 chromeos::StubCrosSettingsProvider stub_settings_provider_; 228 chromeos::CrosSettingsProvider* device_settings_provider_; 229 230 DISALLOW_COPY_AND_ASSIGN(VariationsServiceTestChromeOS); 231 }; 232 233 TEST_F(VariationsServiceTestChromeOS, VariationsURLIsValid) { 234 TestingPrefServiceSimple prefs; 235 VariationsService::RegisterPrefs(prefs.registry()); 236 const std::string default_variations_url = 237 VariationsService::GetDefaultVariationsServerURLForTesting(); 238 239 std::string value; 240 GURL url = VariationsService::GetVariationsServerURL(&prefs); 241 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 242 EXPECT_FALSE(net::GetValueForKeyInQuery(url, "restrict", &value)); 243 244 SetVariationsRestrictParameterPolicyValue("restricted"); 245 url = VariationsService::GetVariationsServerURL(&prefs); 246 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 247 EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value)); 248 EXPECT_EQ("restricted", value); 249 } 250 #endif 251 252 TEST_F(VariationsServiceTest, VariationsURLHasOSNameParam) { 253 TestingPrefServiceSimple prefs; 254 VariationsService::RegisterPrefs(prefs.registry()); 255 const GURL url = VariationsService::GetVariationsServerURL(&prefs); 256 257 std::string value; 258 EXPECT_TRUE(net::GetValueForKeyInQuery(url, "osname", &value)); 259 EXPECT_FALSE(value.empty()); 260 } 261 262 TEST_F(VariationsServiceTest, RequestsInitiallyNotAllowed) { 263 base::MessageLoopForUI message_loop; 264 content::TestBrowserThread ui_thread(content::BrowserThread::UI, 265 &message_loop); 266 TestingPrefServiceSimple prefs; 267 VariationsService::RegisterPrefs(prefs.registry()); 268 269 // Pass ownership to TestVariationsService, but keep a weak pointer to 270 // manipulate it for this test. 271 TestRequestAllowedNotifier* test_notifier = new TestRequestAllowedNotifier; 272 TestVariationsService test_service(test_notifier, &prefs); 273 274 // Force the notifier to initially disallow requests. 275 test_notifier->SetRequestsAllowedOverride(false); 276 test_service.StartRepeatedVariationsSeedFetch(); 277 EXPECT_FALSE(test_service.fetch_attempted()); 278 279 test_notifier->NotifyObserver(); 280 EXPECT_TRUE(test_service.fetch_attempted()); 281 } 282 283 TEST_F(VariationsServiceTest, RequestsInitiallyAllowed) { 284 base::MessageLoopForUI message_loop; 285 content::TestBrowserThread ui_thread(content::BrowserThread::UI, 286 &message_loop); 287 TestingPrefServiceSimple prefs; 288 VariationsService::RegisterPrefs(prefs.registry()); 289 290 // Pass ownership to TestVariationsService, but keep a weak pointer to 291 // manipulate it for this test. 292 TestRequestAllowedNotifier* test_notifier = new TestRequestAllowedNotifier; 293 TestVariationsService test_service(test_notifier, &prefs); 294 295 test_notifier->SetRequestsAllowedOverride(true); 296 test_service.StartRepeatedVariationsSeedFetch(); 297 EXPECT_TRUE(test_service.fetch_attempted()); 298 } 299 300 TEST_F(VariationsServiceTest, SeedStoredWhenOKStatus) { 301 base::MessageLoop message_loop; 302 content::TestBrowserThread io_thread(content::BrowserThread::IO, 303 &message_loop); 304 TestingPrefServiceSimple prefs; 305 VariationsService::RegisterPrefs(prefs.registry()); 306 307 TestVariationsService service(new TestRequestAllowedNotifier, &prefs); 308 service.set_intercepts_fetch(false); 309 310 net::TestURLFetcherFactory factory; 311 service.DoActualFetch(); 312 313 net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); 314 SimulateServerResponse(net::HTTP_OK, fetcher); 315 fetcher->SetResponseString(SerializeSeed(CreateTestSeed())); 316 317 EXPECT_FALSE(service.seed_stored()); 318 service.OnURLFetchComplete(fetcher); 319 EXPECT_TRUE(service.seed_stored()); 320 } 321 322 TEST_F(VariationsServiceTest, SeedNotStoredWhenNonOKStatus) { 323 const int non_ok_status_codes[] = { 324 net::HTTP_NO_CONTENT, 325 net::HTTP_NOT_MODIFIED, 326 net::HTTP_NOT_FOUND, 327 net::HTTP_INTERNAL_SERVER_ERROR, 328 net::HTTP_SERVICE_UNAVAILABLE, 329 }; 330 331 base::MessageLoop message_loop; 332 content::TestBrowserThread io_thread(content::BrowserThread::IO, 333 &message_loop); 334 TestingPrefServiceSimple prefs; 335 VariationsService::RegisterPrefs(prefs.registry()); 336 337 VariationsService service(new TestRequestAllowedNotifier, &prefs, NULL); 338 for (size_t i = 0; i < arraysize(non_ok_status_codes); ++i) { 339 net::TestURLFetcherFactory factory; 340 service.DoActualFetch(); 341 EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 342 343 net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); 344 SimulateServerResponse(non_ok_status_codes[i], fetcher); 345 service.OnURLFetchComplete(fetcher); 346 347 EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 348 } 349 } 350 351 TEST_F(VariationsServiceTest, SeedDateUpdatedOn304Status) { 352 base::MessageLoop message_loop; 353 content::TestBrowserThread io_thread(content::BrowserThread::IO, 354 &message_loop); 355 TestingPrefServiceSimple prefs; 356 VariationsService::RegisterPrefs(prefs.registry()); 357 358 VariationsService service(new TestRequestAllowedNotifier, &prefs, NULL); 359 net::TestURLFetcherFactory factory; 360 service.DoActualFetch(); 361 EXPECT_TRUE( 362 prefs.FindPreference(prefs::kVariationsSeedDate)->IsDefaultValue()); 363 364 net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); 365 SimulateServerResponse(net::HTTP_NOT_MODIFIED, fetcher); 366 service.OnURLFetchComplete(fetcher); 367 EXPECT_FALSE( 368 prefs.FindPreference(prefs::kVariationsSeedDate)->IsDefaultValue()); 369 } 370 371 TEST_F(VariationsServiceTest, Observer) { 372 TestingPrefServiceSimple prefs; 373 VariationsService::RegisterPrefs(prefs.registry()); 374 VariationsService service(new TestRequestAllowedNotifier, &prefs, NULL); 375 376 struct { 377 int normal_count; 378 int best_effort_count; 379 int critical_count; 380 int expected_best_effort_notifications; 381 int expected_crtical_notifications; 382 } cases[] = { 383 {0, 0, 0, 0, 0}, 384 {1, 0, 0, 0, 0}, 385 {10, 0, 0, 0, 0}, 386 {0, 1, 0, 1, 0}, 387 {0, 10, 0, 1, 0}, 388 {0, 0, 1, 0, 1}, 389 {0, 0, 10, 0, 1}, 390 {0, 1, 1, 0, 1}, 391 {1, 1, 1, 0, 1}, 392 {1, 1, 0, 1, 0}, 393 {1, 0, 1, 0, 1}, 394 }; 395 396 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 397 TestVariationsServiceObserver observer; 398 service.AddObserver(&observer); 399 400 variations::VariationsSeedSimulator::Result result; 401 result.normal_group_change_count = cases[i].normal_count; 402 result.kill_best_effort_group_change_count = cases[i].best_effort_count; 403 result.kill_critical_group_change_count = cases[i].critical_count; 404 service.NotifyObservers(result); 405 406 EXPECT_EQ(cases[i].expected_best_effort_notifications, 407 observer.best_effort_changes_notified()) << i; 408 EXPECT_EQ(cases[i].expected_crtical_notifications, 409 observer.crticial_changes_notified()) << i; 410 411 service.RemoveObserver(&observer); 412 } 413 } 414 415 } // namespace chrome_variations 416