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