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/metrics/proto/study.pb.h" 15#include "chrome/browser/web_resource/resource_request_allowed_notifier_test_util.h" 16#include "chrome/common/pref_names.h" 17#include "chrome/test/base/testing_browser_process.h" 18#include "content/public/test/test_browser_thread.h" 19#include "net/base/url_util.h" 20#include "net/http/http_response_headers.h" 21#include "net/http/http_status_code.h" 22#include "net/url_request/test_url_fetcher_factory.h" 23#include "testing/gtest/include/gtest/gtest.h" 24 25#if defined(OS_CHROMEOS) 26#include "chrome/browser/chromeos/settings/cros_settings.h" 27#include "chrome/browser/chromeos/settings/device_settings_service.h" 28#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" 29#endif 30 31namespace chrome_variations { 32 33namespace { 34 35// A test class used to validate expected functionality in VariationsService. 36class TestVariationsService : public VariationsService { 37 public: 38 TestVariationsService(TestRequestAllowedNotifier* test_notifier, 39 PrefService* local_state) 40 : VariationsService(test_notifier, local_state), 41 fetch_attempted_(false) { 42 // Set this so StartRepeatedVariationsSeedFetch can be called in tests. 43 SetCreateTrialsFromSeedCalledForTesting(true); 44 } 45 46 virtual ~TestVariationsService() { 47 } 48 49 bool fetch_attempted() const { return fetch_attempted_; } 50 51 protected: 52 virtual void DoActualFetch() OVERRIDE { 53 fetch_attempted_ = true; 54 } 55 56 private: 57 bool fetch_attempted_; 58 59 DISALLOW_COPY_AND_ASSIGN(TestVariationsService); 60}; 61 62// Populates |seed| with simple test data. The resulting seed will contain one 63// study called "test", which contains one experiment called "abc" with 64// probability weight 100. |seed|'s study field will be cleared before adding 65// the new study. 66TrialsSeed CreateTestSeed() { 67 TrialsSeed seed; 68 Study* study = seed.add_study(); 69 study->set_name("test"); 70 study->set_default_experiment_name("abc"); 71 Study_Experiment* experiment = study->add_experiment(); 72 experiment->set_name("abc"); 73 experiment->set_probability_weight(100); 74 seed.set_serial_number("123"); 75 return seed; 76} 77 78// Serializes |seed| to protobuf binary format. 79std::string SerializeSeed(const TrialsSeed& seed) { 80 std::string serialized_seed; 81 seed.SerializeToString(&serialized_seed); 82 return serialized_seed; 83} 84 85// Serializes |seed| to base64-encoded protobuf binary format. 86std::string SerializeSeedBase64(const TrialsSeed& seed, std::string* hash) { 87 std::string serialized_seed = SerializeSeed(seed); 88 if (hash != NULL) { 89 std::string sha1 = base::SHA1HashString(serialized_seed); 90 *hash = base::HexEncode(sha1.data(), sha1.size()); 91 } 92 std::string base64_serialized_seed; 93 EXPECT_TRUE(base::Base64Encode(serialized_seed, &base64_serialized_seed)); 94 return base64_serialized_seed; 95} 96 97// Simulates a variations service response by setting a date header and the 98// specified HTTP |response_code| on |fetcher|. 99void SimulateServerResponse(int response_code, net::TestURLFetcher* fetcher) { 100 ASSERT_TRUE(fetcher); 101 scoped_refptr<net::HttpResponseHeaders> headers( 102 new net::HttpResponseHeaders("date:Wed, 13 Feb 2013 00:25:24 GMT\0\0")); 103 fetcher->set_response_headers(headers); 104 fetcher->set_response_code(response_code); 105} 106 107} // namespace 108 109class VariationsServiceTest : public ::testing::Test { 110 protected: 111 VariationsServiceTest() {} 112 113 private: 114#if defined(OS_CHROMEOS) 115 // Not used directly. Initializes CrosSettings for testing. 116 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; 117 chromeos::ScopedTestCrosSettings test_cros_settings_; 118#endif 119 120 DISALLOW_COPY_AND_ASSIGN(VariationsServiceTest); 121}; 122 123#if !defined(OS_CHROMEOS) 124TEST_F(VariationsServiceTest, VariationsURLIsValid) { 125 TestingPrefServiceSimple prefs; 126 VariationsService::RegisterPrefs(prefs.registry()); 127 const std::string default_variations_url = 128 VariationsService::GetDefaultVariationsServerURLForTesting(); 129 130 std::string value; 131 GURL url = VariationsService::GetVariationsServerURL(&prefs); 132 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 133 EXPECT_FALSE(net::GetValueForKeyInQuery(url, "restrict", &value)); 134 135 prefs.SetString(prefs::kVariationsRestrictParameter, "restricted"); 136 url = VariationsService::GetVariationsServerURL(&prefs); 137 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 138 EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value)); 139 EXPECT_EQ("restricted", value); 140} 141#else 142class VariationsServiceTestChromeOS : public VariationsServiceTest { 143 protected: 144 VariationsServiceTestChromeOS() {} 145 146 virtual void SetUp() OVERRIDE { 147 cros_settings_ = chromeos::CrosSettings::Get(); 148 DCHECK(cros_settings_ != NULL); 149 // Remove the real DeviceSettingsProvider and replace it with a stub that 150 // allows modifications in a test. 151 device_settings_provider_ = cros_settings_->GetProvider( 152 chromeos::kReportDeviceVersionInfo); 153 EXPECT_TRUE(device_settings_provider_ != NULL); 154 EXPECT_TRUE(cros_settings_->RemoveSettingsProvider( 155 device_settings_provider_)); 156 cros_settings_->AddSettingsProvider(&stub_settings_provider_); 157 } 158 159 virtual void TearDown() OVERRIDE { 160 // Restore the real DeviceSettingsProvider. 161 EXPECT_TRUE( 162 cros_settings_->RemoveSettingsProvider(&stub_settings_provider_)); 163 cros_settings_->AddSettingsProvider(device_settings_provider_); 164 } 165 166 void SetVariationsRestrictParameterPolicyValue(std::string value) { 167 cros_settings_->SetString(chromeos::kVariationsRestrictParameter, value); 168 } 169 170 private: 171 chromeos::CrosSettings* cros_settings_; 172 chromeos::StubCrosSettingsProvider stub_settings_provider_; 173 chromeos::CrosSettingsProvider* device_settings_provider_; 174 175 DISALLOW_COPY_AND_ASSIGN(VariationsServiceTestChromeOS); 176}; 177 178TEST_F(VariationsServiceTestChromeOS, VariationsURLIsValid) { 179 TestingPrefServiceSimple prefs; 180 VariationsService::RegisterPrefs(prefs.registry()); 181 const std::string default_variations_url = 182 VariationsService::GetDefaultVariationsServerURLForTesting(); 183 184 std::string value; 185 GURL url = VariationsService::GetVariationsServerURL(&prefs); 186 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 187 EXPECT_FALSE(net::GetValueForKeyInQuery(url, "restrict", &value)); 188 189 SetVariationsRestrictParameterPolicyValue("restricted"); 190 url = VariationsService::GetVariationsServerURL(&prefs); 191 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 192 EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value)); 193 EXPECT_EQ("restricted", value); 194} 195#endif 196 197TEST_F(VariationsServiceTest, VariationsURLHasOSNameParam) { 198 TestingPrefServiceSimple prefs; 199 VariationsService::RegisterPrefs(prefs.registry()); 200 const GURL url = VariationsService::GetVariationsServerURL(&prefs); 201 202 std::string value; 203 EXPECT_TRUE(net::GetValueForKeyInQuery(url, "osname", &value)); 204 EXPECT_FALSE(value.empty()); 205} 206 207TEST_F(VariationsServiceTest, LoadSeed) { 208 // Store good seed data to test if loading from prefs works. 209 const TrialsSeed seed = CreateTestSeed(); 210 std::string seed_hash; 211 const std::string base64_seed = SerializeSeedBase64(seed, &seed_hash); 212 213 TestingPrefServiceSimple prefs; 214 VariationsService::RegisterPrefs(prefs.registry()); 215 prefs.SetString(prefs::kVariationsSeed, base64_seed); 216 217 TestVariationsService variations_service(new TestRequestAllowedNotifier, 218 &prefs); 219 TrialsSeed loaded_seed; 220 // Check that loading a seed without a hash pref set works correctly. 221 EXPECT_TRUE(variations_service.LoadTrialsSeedFromPref(&loaded_seed)); 222 223 // Check that the loaded data is the same as the original. 224 EXPECT_EQ(SerializeSeed(seed), SerializeSeed(loaded_seed)); 225 // Make sure the pref hasn't been changed. 226 EXPECT_FALSE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 227 EXPECT_EQ(base64_seed, prefs.GetString(prefs::kVariationsSeed)); 228 229 // Check that loading a seed with the correct hash works. 230 prefs.SetString(prefs::kVariationsSeedHash, seed_hash); 231 loaded_seed.Clear(); 232 EXPECT_TRUE(variations_service.LoadTrialsSeedFromPref(&loaded_seed)); 233 EXPECT_EQ(SerializeSeed(seed), SerializeSeed(loaded_seed)); 234 235 // Check that false is returned and the pref is cleared when hash differs. 236 TrialsSeed different_seed = seed; 237 different_seed.mutable_study(0)->set_name("octopus"); 238 std::string different_hash; 239 prefs.SetString(prefs::kVariationsSeed, 240 SerializeSeedBase64(different_seed, &different_hash)); 241 ASSERT_NE(different_hash, prefs.GetString(prefs::kVariationsSeedHash)); 242 EXPECT_FALSE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 243 EXPECT_FALSE(variations_service.LoadTrialsSeedFromPref(&loaded_seed)); 244 EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 245 EXPECT_TRUE( 246 prefs.FindPreference(prefs::kVariationsSeedDate)->IsDefaultValue()); 247 EXPECT_TRUE( 248 prefs.FindPreference(prefs::kVariationsSeedHash)->IsDefaultValue()); 249 250 // Check that loading a bad seed returns false and clears the pref. 251 prefs.ClearPref(prefs::kVariationsSeed); 252 prefs.SetString(prefs::kVariationsSeed, "this should fail"); 253 EXPECT_FALSE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 254 EXPECT_FALSE(variations_service.LoadTrialsSeedFromPref(&loaded_seed)); 255 EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 256 EXPECT_TRUE( 257 prefs.FindPreference(prefs::kVariationsSeedDate)->IsDefaultValue()); 258 EXPECT_TRUE( 259 prefs.FindPreference(prefs::kVariationsSeedHash)->IsDefaultValue()); 260 261 // Check that having no seed in prefs results in a return value of false. 262 prefs.ClearPref(prefs::kVariationsSeed); 263 EXPECT_FALSE(variations_service.LoadTrialsSeedFromPref(&loaded_seed)); 264} 265 266TEST_F(VariationsServiceTest, StoreSeed) { 267 const base::Time now = base::Time::Now(); 268 const TrialsSeed seed = CreateTestSeed(); 269 const std::string serialized_seed = SerializeSeed(seed); 270 271 TestingPrefServiceSimple prefs; 272 VariationsService::RegisterPrefs(prefs.registry()); 273 274 TestVariationsService variations_service(new TestRequestAllowedNotifier, 275 &prefs); 276 277 EXPECT_TRUE(variations_service.StoreSeedData(serialized_seed, now)); 278 // Make sure the pref was actually set. 279 EXPECT_FALSE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 280 281 std::string loaded_serialized_seed = prefs.GetString(prefs::kVariationsSeed); 282 std::string decoded_serialized_seed; 283 ASSERT_TRUE(base::Base64Decode(loaded_serialized_seed, 284 &decoded_serialized_seed)); 285 // Make sure the stored seed from pref is the same as the seed we created. 286 EXPECT_EQ(serialized_seed, decoded_serialized_seed); 287 288 // Check if trying to store a bad seed leaves the pref unchanged. 289 prefs.ClearPref(prefs::kVariationsSeed); 290 EXPECT_FALSE(variations_service.StoreSeedData("should fail", now)); 291 EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 292} 293 294TEST_F(VariationsServiceTest, RequestsInitiallyNotAllowed) { 295 base::MessageLoopForUI message_loop; 296 content::TestBrowserThread ui_thread(content::BrowserThread::UI, 297 &message_loop); 298 TestingPrefServiceSimple prefs; 299 VariationsService::RegisterPrefs(prefs.registry()); 300 301 // Pass ownership to TestVariationsService, but keep a weak pointer to 302 // manipulate it for this test. 303 TestRequestAllowedNotifier* test_notifier = new TestRequestAllowedNotifier; 304 TestVariationsService test_service(test_notifier, &prefs); 305 306 // Force the notifier to initially disallow requests. 307 test_notifier->SetRequestsAllowedOverride(false); 308 test_service.StartRepeatedVariationsSeedFetch(); 309 EXPECT_FALSE(test_service.fetch_attempted()); 310 311 test_notifier->NotifyObserver(); 312 EXPECT_TRUE(test_service.fetch_attempted()); 313} 314 315TEST_F(VariationsServiceTest, RequestsInitiallyAllowed) { 316 base::MessageLoopForUI message_loop; 317 content::TestBrowserThread ui_thread(content::BrowserThread::UI, 318 &message_loop); 319 TestingPrefServiceSimple prefs; 320 VariationsService::RegisterPrefs(prefs.registry()); 321 322 // Pass ownership to TestVariationsService, but keep a weak pointer to 323 // manipulate it for this test. 324 TestRequestAllowedNotifier* test_notifier = new TestRequestAllowedNotifier; 325 TestVariationsService test_service(test_notifier, &prefs); 326 327 test_notifier->SetRequestsAllowedOverride(true); 328 test_service.StartRepeatedVariationsSeedFetch(); 329 EXPECT_TRUE(test_service.fetch_attempted()); 330} 331 332TEST_F(VariationsServiceTest, SeedStoredWhenOKStatus) { 333 base::MessageLoop message_loop; 334 content::TestBrowserThread io_thread(content::BrowserThread::IO, 335 &message_loop); 336 TestingPrefServiceSimple prefs; 337 VariationsService::RegisterPrefs(prefs.registry()); 338 339 VariationsService variations_service(new TestRequestAllowedNotifier, &prefs); 340 341 net::TestURLFetcherFactory factory; 342 variations_service.DoActualFetch(); 343 344 net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); 345 SimulateServerResponse(net::HTTP_OK, fetcher); 346 const TrialsSeed seed = CreateTestSeed(); 347 fetcher->SetResponseString(SerializeSeed(seed)); 348 349 EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 350 variations_service.OnURLFetchComplete(fetcher); 351 EXPECT_FALSE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 352 const std::string expected_base64 = SerializeSeedBase64(seed, NULL); 353 EXPECT_EQ(expected_base64, prefs.GetString(prefs::kVariationsSeed)); 354} 355 356TEST_F(VariationsServiceTest, SeedNotStoredWhenNonOKStatus) { 357 const int non_ok_status_codes[] = { 358 net::HTTP_NO_CONTENT, 359 net::HTTP_NOT_MODIFIED, 360 net::HTTP_NOT_FOUND, 361 net::HTTP_INTERNAL_SERVER_ERROR, 362 net::HTTP_SERVICE_UNAVAILABLE, 363 }; 364 365 base::MessageLoop message_loop; 366 content::TestBrowserThread io_thread(content::BrowserThread::IO, 367 &message_loop); 368 TestingPrefServiceSimple prefs; 369 VariationsService::RegisterPrefs(prefs.registry()); 370 371 VariationsService variations_service(new TestRequestAllowedNotifier, &prefs); 372 for (size_t i = 0; i < arraysize(non_ok_status_codes); ++i) { 373 net::TestURLFetcherFactory factory; 374 variations_service.DoActualFetch(); 375 EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 376 377 net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); 378 SimulateServerResponse(non_ok_status_codes[i], fetcher); 379 variations_service.OnURLFetchComplete(fetcher); 380 381 EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 382 } 383} 384 385} // namespace chrome_variations 386