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