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
32 namespace chrome_variations {
33
34 namespace {
35
36 // A test class used to validate expected functionality in VariationsService.
37 class TestVariationsService : public VariationsService {
38 public:
TestVariationsService(TestRequestAllowedNotifier * test_notifier,PrefService * local_state)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
~TestVariationsService()47 virtual ~TestVariationsService() {
48 }
49
fetch_attempted() const50 bool fetch_attempted() const { return fetch_attempted_; }
51
52 protected:
DoActualFetch()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.
CreateTestSeed()67 VariationsSeed 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.
SerializeSeed(const VariationsSeed & seed)80 std::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.
SerializeSeedBase64(const VariationsSeed & seed,std::string * hash)87 std::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|.
SimulateServerResponse(int response_code,net::TestURLFetcher * fetcher)100 void 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
110 class VariationsServiceTest : public ::testing::Test {
111 protected:
VariationsServiceTest()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)
TEST_F(VariationsServiceTest,VariationsURLIsValid)125 TEST_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
143 class VariationsServiceTestChromeOS : public VariationsServiceTest {
144 protected:
VariationsServiceTestChromeOS()145 VariationsServiceTestChromeOS() {}
146
SetUp()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
TearDown()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
SetVariationsRestrictParameterPolicyValue(std::string value)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
TEST_F(VariationsServiceTestChromeOS,VariationsURLIsValid)179 TEST_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
TEST_F(VariationsServiceTest,VariationsURLHasOSNameParam)198 TEST_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
TEST_F(VariationsServiceTest,LoadSeed)208 TEST_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
TEST_F(VariationsServiceTest,StoreSeed)267 TEST_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
TEST_F(VariationsServiceTest,RequestsInitiallyNotAllowed)295 TEST_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
TEST_F(VariationsServiceTest,RequestsInitiallyAllowed)316 TEST_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
TEST_F(VariationsServiceTest,SeedStoredWhenOKStatus)333 TEST_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
TEST_F(VariationsServiceTest,SeedNotStoredWhenNonOKStatus)357 TEST_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
TEST_F(VariationsServiceTest,SeedDateUpdatedOn304Status)386 TEST_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