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.
5 #include "chrome/browser/metrics/variations/variations_service.h"
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/common/pref_names.h"
15 #include "chrome/test/base/testing_browser_process.h"
16 #include "chrome/test/base/testing_pref_service_syncable.h"
17 #include "components/variations/proto/study.pb.h"
18 #include "components/variations/proto/variations_seed.pb.h"
19 #include "components/web_resource/resource_request_allowed_notifier_test_util.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"
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"
33 namespace chrome_variations
{
37 // A test class used to validate expected functionality in VariationsService.
38 class TestVariationsService
: public VariationsService
{
40 TestVariationsService(web_resource::TestRequestAllowedNotifier
* test_notifier
,
41 PrefService
* local_state
)
42 : VariationsService(test_notifier
, local_state
, NULL
),
43 intercepts_fetch_(true),
44 fetch_attempted_(false),
46 // Set this so StartRepeatedVariationsSeedFetch can be called in tests.
47 SetCreateTrialsFromSeedCalledForTesting(true);
50 ~TestVariationsService() override
{}
52 void set_intercepts_fetch(bool value
) {
53 intercepts_fetch_
= value
;
56 bool fetch_attempted() const { return fetch_attempted_
; }
58 bool seed_stored() const { return seed_stored_
; }
60 void DoActualFetch() override
{
61 if (intercepts_fetch_
) {
62 fetch_attempted_
= true;
66 VariationsService::DoActualFetch();
70 void StoreSeed(const std::string
& seed_data
,
71 const std::string
& seed_signature
,
72 const base::Time
& date_fetched
) override
{
77 bool intercepts_fetch_
;
78 bool fetch_attempted_
;
81 DISALLOW_COPY_AND_ASSIGN(TestVariationsService
);
84 class TestVariationsServiceObserver
: public VariationsService::Observer
{
86 TestVariationsServiceObserver()
87 : best_effort_changes_notified_(0),
88 crticial_changes_notified_(0) {
90 ~TestVariationsServiceObserver() override
{}
92 void OnExperimentChangesDetected(Severity severity
) override
{
95 ++best_effort_changes_notified_
;
98 ++crticial_changes_notified_
;
103 int best_effort_changes_notified() const {
104 return best_effort_changes_notified_
;
107 int crticial_changes_notified() const {
108 return crticial_changes_notified_
;
112 // Number of notification received with BEST_EFFORT severity.
113 int best_effort_changes_notified_
;
115 // Number of notification received with CRITICAL severity.
116 int crticial_changes_notified_
;
118 DISALLOW_COPY_AND_ASSIGN(TestVariationsServiceObserver
);
121 // Populates |seed| with simple test data. The resulting seed will contain one
122 // study called "test", which contains one experiment called "abc" with
123 // probability weight 100. |seed|'s study field will be cleared before adding
125 variations::VariationsSeed
CreateTestSeed() {
126 variations::VariationsSeed seed
;
127 variations::Study
* study
= seed
.add_study();
128 study
->set_name("test");
129 study
->set_default_experiment_name("abc");
130 variations::Study_Experiment
* experiment
= study
->add_experiment();
131 experiment
->set_name("abc");
132 experiment
->set_probability_weight(100);
133 seed
.set_serial_number("123");
137 // Serializes |seed| to protobuf binary format.
138 std::string
SerializeSeed(const variations::VariationsSeed
& seed
) {
139 std::string serialized_seed
;
140 seed
.SerializeToString(&serialized_seed
);
141 return serialized_seed
;
144 // Simulates a variations service response by setting a date header and the
145 // specified HTTP |response_code| on |fetcher|.
146 void SimulateServerResponse(int response_code
, net::TestURLFetcher
* fetcher
) {
147 ASSERT_TRUE(fetcher
);
148 scoped_refptr
<net::HttpResponseHeaders
> headers(
149 new net::HttpResponseHeaders("date:Wed, 13 Feb 2013 00:25:24 GMT\0\0"));
150 fetcher
->set_response_headers(headers
);
151 fetcher
->set_response_code(response_code
);
154 // Helper class that abstracts away platform-specific details relating to the
155 // pref store used for the "restrict" param policy.
156 class TestVariationsPrefsStore
{
158 TestVariationsPrefsStore() {
159 #if defined(OS_ANDROID)
160 // Android uses profile prefs as the PrefService to generate the URL.
161 VariationsService::RegisterProfilePrefs(prefs_
.registry());
163 VariationsService::RegisterPrefs(prefs_
.registry());
166 #if defined(OS_CHROMEOS)
167 cros_settings_
= chromeos::CrosSettings::Get();
168 DCHECK(cros_settings_
!= NULL
);
169 // Remove the real DeviceSettingsProvider and replace it with a stub that
170 // allows modifications in a test.
171 // TODO(asvitkine): Make a scoped helper class for this operation.
172 device_settings_provider_
= cros_settings_
->GetProvider(
173 chromeos::kReportDeviceVersionInfo
);
174 EXPECT_TRUE(device_settings_provider_
!= NULL
);
175 EXPECT_TRUE(cros_settings_
->RemoveSettingsProvider(
176 device_settings_provider_
));
177 cros_settings_
->AddSettingsProvider(&stub_settings_provider_
);
181 ~TestVariationsPrefsStore() {
182 #if defined(OS_CHROMEOS)
183 // Restore the real DeviceSettingsProvider.
185 cros_settings_
->RemoveSettingsProvider(&stub_settings_provider_
));
186 cros_settings_
->AddSettingsProvider(device_settings_provider_
);
190 void SetVariationsRestrictParameterPolicyValue(const std::string
& value
) {
191 #if defined(OS_CHROMEOS)
192 cros_settings_
->SetString(chromeos::kVariationsRestrictParameter
, value
);
194 prefs_
.SetString(prefs::kVariationsRestrictParameter
, value
);
198 PrefService
* prefs() { return &prefs_
; }
201 #if defined(OS_ANDROID)
202 // Android uses profile prefs as the PrefService to generate the URL.
203 TestingPrefServiceSyncable prefs_
;
205 TestingPrefServiceSimple prefs_
;
208 #if defined(OS_CHROMEOS)
209 chromeos::CrosSettings
* cros_settings_
;
210 chromeos::StubCrosSettingsProvider stub_settings_provider_
;
211 chromeos::CrosSettingsProvider
* device_settings_provider_
;
214 DISALLOW_COPY_AND_ASSIGN(TestVariationsPrefsStore
);
219 class VariationsServiceTest
: public ::testing::Test
{
221 VariationsServiceTest() {}
224 #if defined(OS_CHROMEOS)
225 // Not used directly. Initializes CrosSettings for testing.
226 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_
;
227 chromeos::ScopedTestCrosSettings test_cros_settings_
;
230 DISALLOW_COPY_AND_ASSIGN(VariationsServiceTest
);
233 TEST_F(VariationsServiceTest
, GetVariationsServerURL
) {
234 TestVariationsPrefsStore prefs_store
;
235 PrefService
* prefs
= prefs_store
.prefs();
236 const std::string default_variations_url
=
237 VariationsService::GetDefaultVariationsServerURLForTesting();
240 GURL url
= VariationsService::GetVariationsServerURL(prefs
, std::string());
241 EXPECT_TRUE(StartsWithASCII(url
.spec(), default_variations_url
, true));
242 EXPECT_FALSE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
244 prefs_store
.SetVariationsRestrictParameterPolicyValue("restricted");
245 url
= VariationsService::GetVariationsServerURL(prefs
, std::string());
246 EXPECT_TRUE(StartsWithASCII(url
.spec(), default_variations_url
, true));
247 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
248 EXPECT_EQ("restricted", value
);
250 // The override value should take precedence over what's in prefs.
251 url
= VariationsService::GetVariationsServerURL(prefs
, "override");
252 EXPECT_TRUE(StartsWithASCII(url
.spec(), default_variations_url
, true));
253 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
254 EXPECT_EQ("override", value
);
257 TEST_F(VariationsServiceTest
, VariationsURLHasOSNameParam
) {
258 TestingPrefServiceSimple prefs
;
259 VariationsService::RegisterPrefs(prefs
.registry());
261 VariationsService::GetVariationsServerURL(&prefs
, std::string());
264 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "osname", &value
));
265 EXPECT_FALSE(value
.empty());
268 TEST_F(VariationsServiceTest
, RequestsInitiallyNotAllowed
) {
269 base::MessageLoopForUI message_loop
;
270 content::TestBrowserThread
ui_thread(content::BrowserThread::UI
,
272 TestingPrefServiceSimple prefs
;
273 VariationsService::RegisterPrefs(prefs
.registry());
275 // Pass ownership to TestVariationsService, but keep a weak pointer to
276 // manipulate it for this test.
277 web_resource::TestRequestAllowedNotifier
* test_notifier
=
278 new web_resource::TestRequestAllowedNotifier(&prefs
);
279 TestVariationsService
test_service(test_notifier
, &prefs
);
281 // Force the notifier to initially disallow requests.
282 test_notifier
->SetRequestsAllowedOverride(false);
283 test_service
.StartRepeatedVariationsSeedFetch();
284 EXPECT_FALSE(test_service
.fetch_attempted());
286 test_notifier
->NotifyObserver();
287 EXPECT_TRUE(test_service
.fetch_attempted());
290 TEST_F(VariationsServiceTest
, RequestsInitiallyAllowed
) {
291 base::MessageLoopForUI message_loop
;
292 content::TestBrowserThread
ui_thread(content::BrowserThread::UI
,
294 TestingPrefServiceSimple prefs
;
295 VariationsService::RegisterPrefs(prefs
.registry());
297 // Pass ownership to TestVariationsService, but keep a weak pointer to
298 // manipulate it for this test.
299 web_resource::TestRequestAllowedNotifier
* test_notifier
=
300 new web_resource::TestRequestAllowedNotifier(&prefs
);
301 TestVariationsService
test_service(test_notifier
, &prefs
);
303 test_notifier
->SetRequestsAllowedOverride(true);
304 test_service
.StartRepeatedVariationsSeedFetch();
305 EXPECT_TRUE(test_service
.fetch_attempted());
308 TEST_F(VariationsServiceTest
, SeedStoredWhenOKStatus
) {
309 base::MessageLoop message_loop
;
310 content::TestBrowserThread
io_thread(content::BrowserThread::IO
,
312 TestingPrefServiceSimple prefs
;
313 VariationsService::RegisterPrefs(prefs
.registry());
315 TestVariationsService
service(
316 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
);
317 service
.variations_server_url_
=
318 VariationsService::GetVariationsServerURL(&prefs
, std::string());
319 service
.set_intercepts_fetch(false);
321 net::TestURLFetcherFactory factory
;
322 service
.DoActualFetch();
324 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
325 SimulateServerResponse(net::HTTP_OK
, fetcher
);
326 fetcher
->SetResponseString(SerializeSeed(CreateTestSeed()));
328 EXPECT_FALSE(service
.seed_stored());
329 service
.OnURLFetchComplete(fetcher
);
330 EXPECT_TRUE(service
.seed_stored());
333 TEST_F(VariationsServiceTest
, SeedNotStoredWhenNonOKStatus
) {
334 const int non_ok_status_codes
[] = {
335 net::HTTP_NO_CONTENT
,
336 net::HTTP_NOT_MODIFIED
,
338 net::HTTP_INTERNAL_SERVER_ERROR
,
339 net::HTTP_SERVICE_UNAVAILABLE
,
342 base::MessageLoop message_loop
;
343 content::TestBrowserThread
io_thread(content::BrowserThread::IO
,
345 TestingPrefServiceSimple prefs
;
346 VariationsService::RegisterPrefs(prefs
.registry());
348 VariationsService
service(
349 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
, NULL
);
350 service
.variations_server_url_
=
351 VariationsService::GetVariationsServerURL(&prefs
, std::string());
352 for (size_t i
= 0; i
< arraysize(non_ok_status_codes
); ++i
) {
353 net::TestURLFetcherFactory factory
;
354 service
.DoActualFetch();
355 EXPECT_TRUE(prefs
.FindPreference(prefs::kVariationsSeed
)->IsDefaultValue());
357 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
358 SimulateServerResponse(non_ok_status_codes
[i
], fetcher
);
359 service
.OnURLFetchComplete(fetcher
);
361 EXPECT_TRUE(prefs
.FindPreference(prefs::kVariationsSeed
)->IsDefaultValue());
365 TEST_F(VariationsServiceTest
, SeedDateUpdatedOn304Status
) {
366 base::MessageLoop message_loop
;
367 content::TestBrowserThread
io_thread(content::BrowserThread::IO
,
369 TestingPrefServiceSimple prefs
;
370 VariationsService::RegisterPrefs(prefs
.registry());
372 net::TestURLFetcherFactory factory
;
373 VariationsService
service(
374 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
, NULL
);
375 service
.variations_server_url_
=
376 VariationsService::GetVariationsServerURL(&prefs
, std::string());
377 service
.DoActualFetch();
379 prefs
.FindPreference(prefs::kVariationsSeedDate
)->IsDefaultValue());
381 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
382 SimulateServerResponse(net::HTTP_NOT_MODIFIED
, fetcher
);
383 service
.OnURLFetchComplete(fetcher
);
385 prefs
.FindPreference(prefs::kVariationsSeedDate
)->IsDefaultValue());
388 TEST_F(VariationsServiceTest
, Observer
) {
389 TestingPrefServiceSimple prefs
;
390 VariationsService::RegisterPrefs(prefs
.registry());
391 VariationsService
service(
392 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
, NULL
);
396 int best_effort_count
;
398 int expected_best_effort_notifications
;
399 int expected_crtical_notifications
;
414 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
415 TestVariationsServiceObserver observer
;
416 service
.AddObserver(&observer
);
418 variations::VariationsSeedSimulator::Result result
;
419 result
.normal_group_change_count
= cases
[i
].normal_count
;
420 result
.kill_best_effort_group_change_count
= cases
[i
].best_effort_count
;
421 result
.kill_critical_group_change_count
= cases
[i
].critical_count
;
422 service
.NotifyObservers(result
);
424 EXPECT_EQ(cases
[i
].expected_best_effort_notifications
,
425 observer
.best_effort_changes_notified()) << i
;
426 EXPECT_EQ(cases
[i
].expected_crtical_notifications
,
427 observer
.crticial_changes_notified()) << i
;
429 service
.RemoveObserver(&observer
);
433 } // namespace chrome_variations