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