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 // Populates |seed| with simple test data. The resulting seed will contain one 86 // study called "test", which contains one experiment called "abc" with 87 // probability weight 100. |seed|'s study field will be cleared before adding 88 // the new study. 89 VariationsSeed CreateTestSeed() { 90 VariationsSeed seed; 91 Study* study = seed.add_study(); 92 study->set_name("test"); 93 study->set_default_experiment_name("abc"); 94 Study_Experiment* experiment = study->add_experiment(); 95 experiment->set_name("abc"); 96 experiment->set_probability_weight(100); 97 seed.set_serial_number("123"); 98 return seed; 99 } 100 101 // Serializes |seed| to protobuf binary format. 102 std::string SerializeSeed(const VariationsSeed& seed) { 103 std::string serialized_seed; 104 seed.SerializeToString(&serialized_seed); 105 return serialized_seed; 106 } 107 108 // Simulates a variations service response by setting a date header and the 109 // specified HTTP |response_code| on |fetcher|. 110 void SimulateServerResponse(int response_code, net::TestURLFetcher* fetcher) { 111 ASSERT_TRUE(fetcher); 112 scoped_refptr<net::HttpResponseHeaders> headers( 113 new net::HttpResponseHeaders("date:Wed, 13 Feb 2013 00:25:24 GMT\0\0")); 114 fetcher->set_response_headers(headers); 115 fetcher->set_response_code(response_code); 116 } 117 118 } // namespace 119 120 class VariationsServiceTest : public ::testing::Test { 121 protected: 122 VariationsServiceTest() {} 123 124 private: 125 #if defined(OS_CHROMEOS) 126 // Not used directly. Initializes CrosSettings for testing. 127 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; 128 chromeos::ScopedTestCrosSettings test_cros_settings_; 129 #endif 130 131 DISALLOW_COPY_AND_ASSIGN(VariationsServiceTest); 132 }; 133 134 #if !defined(OS_CHROMEOS) 135 TEST_F(VariationsServiceTest, VariationsURLIsValid) { 136 #if defined(OS_ANDROID) 137 // Android uses profile prefs as the PrefService to generate the URL. 138 TestingPrefServiceSyncable prefs; 139 VariationsService::RegisterProfilePrefs(prefs.registry()); 140 #else 141 TestingPrefServiceSimple prefs; 142 VariationsService::RegisterPrefs(prefs.registry()); 143 #endif 144 const std::string default_variations_url = 145 VariationsService::GetDefaultVariationsServerURLForTesting(); 146 147 std::string value; 148 GURL url = VariationsService::GetVariationsServerURL(&prefs); 149 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 150 EXPECT_FALSE(net::GetValueForKeyInQuery(url, "restrict", &value)); 151 152 prefs.SetString(prefs::kVariationsRestrictParameter, "restricted"); 153 url = VariationsService::GetVariationsServerURL(&prefs); 154 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 155 EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value)); 156 EXPECT_EQ("restricted", value); 157 } 158 #else 159 class VariationsServiceTestChromeOS : public VariationsServiceTest { 160 protected: 161 VariationsServiceTestChromeOS() {} 162 163 virtual void SetUp() OVERRIDE { 164 cros_settings_ = chromeos::CrosSettings::Get(); 165 DCHECK(cros_settings_ != NULL); 166 // Remove the real DeviceSettingsProvider and replace it with a stub that 167 // allows modifications in a test. 168 device_settings_provider_ = cros_settings_->GetProvider( 169 chromeos::kReportDeviceVersionInfo); 170 EXPECT_TRUE(device_settings_provider_ != NULL); 171 EXPECT_TRUE(cros_settings_->RemoveSettingsProvider( 172 device_settings_provider_)); 173 cros_settings_->AddSettingsProvider(&stub_settings_provider_); 174 } 175 176 virtual void TearDown() OVERRIDE { 177 // Restore the real DeviceSettingsProvider. 178 EXPECT_TRUE( 179 cros_settings_->RemoveSettingsProvider(&stub_settings_provider_)); 180 cros_settings_->AddSettingsProvider(device_settings_provider_); 181 } 182 183 void SetVariationsRestrictParameterPolicyValue(std::string value) { 184 cros_settings_->SetString(chromeos::kVariationsRestrictParameter, value); 185 } 186 187 private: 188 chromeos::CrosSettings* cros_settings_; 189 chromeos::StubCrosSettingsProvider stub_settings_provider_; 190 chromeos::CrosSettingsProvider* device_settings_provider_; 191 192 DISALLOW_COPY_AND_ASSIGN(VariationsServiceTestChromeOS); 193 }; 194 195 TEST_F(VariationsServiceTestChromeOS, VariationsURLIsValid) { 196 TestingPrefServiceSimple prefs; 197 VariationsService::RegisterPrefs(prefs.registry()); 198 const std::string default_variations_url = 199 VariationsService::GetDefaultVariationsServerURLForTesting(); 200 201 std::string value; 202 GURL url = VariationsService::GetVariationsServerURL(&prefs); 203 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 204 EXPECT_FALSE(net::GetValueForKeyInQuery(url, "restrict", &value)); 205 206 SetVariationsRestrictParameterPolicyValue("restricted"); 207 url = VariationsService::GetVariationsServerURL(&prefs); 208 EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true)); 209 EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value)); 210 EXPECT_EQ("restricted", value); 211 } 212 #endif 213 214 TEST_F(VariationsServiceTest, VariationsURLHasOSNameParam) { 215 TestingPrefServiceSimple prefs; 216 VariationsService::RegisterPrefs(prefs.registry()); 217 const GURL url = VariationsService::GetVariationsServerURL(&prefs); 218 219 std::string value; 220 EXPECT_TRUE(net::GetValueForKeyInQuery(url, "osname", &value)); 221 EXPECT_FALSE(value.empty()); 222 } 223 224 TEST_F(VariationsServiceTest, RequestsInitiallyNotAllowed) { 225 base::MessageLoopForUI message_loop; 226 content::TestBrowserThread ui_thread(content::BrowserThread::UI, 227 &message_loop); 228 TestingPrefServiceSimple prefs; 229 VariationsService::RegisterPrefs(prefs.registry()); 230 231 // Pass ownership to TestVariationsService, but keep a weak pointer to 232 // manipulate it for this test. 233 TestRequestAllowedNotifier* test_notifier = new TestRequestAllowedNotifier; 234 TestVariationsService test_service(test_notifier, &prefs); 235 236 // Force the notifier to initially disallow requests. 237 test_notifier->SetRequestsAllowedOverride(false); 238 test_service.StartRepeatedVariationsSeedFetch(); 239 EXPECT_FALSE(test_service.fetch_attempted()); 240 241 test_notifier->NotifyObserver(); 242 EXPECT_TRUE(test_service.fetch_attempted()); 243 } 244 245 TEST_F(VariationsServiceTest, RequestsInitiallyAllowed) { 246 base::MessageLoopForUI message_loop; 247 content::TestBrowserThread ui_thread(content::BrowserThread::UI, 248 &message_loop); 249 TestingPrefServiceSimple prefs; 250 VariationsService::RegisterPrefs(prefs.registry()); 251 252 // Pass ownership to TestVariationsService, but keep a weak pointer to 253 // manipulate it for this test. 254 TestRequestAllowedNotifier* test_notifier = new TestRequestAllowedNotifier; 255 TestVariationsService test_service(test_notifier, &prefs); 256 257 test_notifier->SetRequestsAllowedOverride(true); 258 test_service.StartRepeatedVariationsSeedFetch(); 259 EXPECT_TRUE(test_service.fetch_attempted()); 260 } 261 262 TEST_F(VariationsServiceTest, SeedStoredWhenOKStatus) { 263 base::MessageLoop message_loop; 264 content::TestBrowserThread io_thread(content::BrowserThread::IO, 265 &message_loop); 266 TestingPrefServiceSimple prefs; 267 VariationsService::RegisterPrefs(prefs.registry()); 268 269 TestVariationsService service(new TestRequestAllowedNotifier, &prefs); 270 service.set_intercepts_fetch(false); 271 272 net::TestURLFetcherFactory factory; 273 service.DoActualFetch(); 274 275 net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); 276 SimulateServerResponse(net::HTTP_OK, fetcher); 277 fetcher->SetResponseString(SerializeSeed(CreateTestSeed())); 278 279 EXPECT_FALSE(service.seed_stored()); 280 service.OnURLFetchComplete(fetcher); 281 EXPECT_TRUE(service.seed_stored()); 282 } 283 284 TEST_F(VariationsServiceTest, SeedNotStoredWhenNonOKStatus) { 285 const int non_ok_status_codes[] = { 286 net::HTTP_NO_CONTENT, 287 net::HTTP_NOT_MODIFIED, 288 net::HTTP_NOT_FOUND, 289 net::HTTP_INTERNAL_SERVER_ERROR, 290 net::HTTP_SERVICE_UNAVAILABLE, 291 }; 292 293 base::MessageLoop message_loop; 294 content::TestBrowserThread io_thread(content::BrowserThread::IO, 295 &message_loop); 296 TestingPrefServiceSimple prefs; 297 VariationsService::RegisterPrefs(prefs.registry()); 298 299 VariationsService service(new TestRequestAllowedNotifier, &prefs, NULL); 300 for (size_t i = 0; i < arraysize(non_ok_status_codes); ++i) { 301 net::TestURLFetcherFactory factory; 302 service.DoActualFetch(); 303 EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 304 305 net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); 306 SimulateServerResponse(non_ok_status_codes[i], fetcher); 307 service.OnURLFetchComplete(fetcher); 308 309 EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); 310 } 311 } 312 313 TEST_F(VariationsServiceTest, SeedDateUpdatedOn304Status) { 314 base::MessageLoop message_loop; 315 content::TestBrowserThread io_thread(content::BrowserThread::IO, 316 &message_loop); 317 TestingPrefServiceSimple prefs; 318 VariationsService::RegisterPrefs(prefs.registry()); 319 320 VariationsService service(new TestRequestAllowedNotifier, &prefs, NULL); 321 net::TestURLFetcherFactory factory; 322 service.DoActualFetch(); 323 EXPECT_TRUE( 324 prefs.FindPreference(prefs::kVariationsSeedDate)->IsDefaultValue()); 325 326 net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); 327 SimulateServerResponse(net::HTTP_NOT_MODIFIED, fetcher); 328 service.OnURLFetchComplete(fetcher); 329 EXPECT_FALSE( 330 prefs.FindPreference(prefs::kVariationsSeedDate)->IsDefaultValue()); 331 } 332 333 } // namespace chrome_variations 334