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 33namespace chrome_variations { 34 35namespace { 36 37// A test class used to validate expected functionality in VariationsService. 38class 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 85class 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. 127variations::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. 140std::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|. 148void 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 158class 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) 173TEST_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 197class 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 233TEST_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 252TEST_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 262TEST_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 283TEST_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 300TEST_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 322TEST_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 351TEST_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 371TEST_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