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/json/json_string_value_serializer.h"
11 #include "base/prefs/testing_pref_service.h"
12 #include "base/sha1.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/test/histogram_tester.h"
17 #include "base/version.h"
18 #include "chrome/browser/metrics/variations/chrome_variations_service_client.h"
19 #include "chrome/test/base/testing_browser_process.h"
20 #include "chrome/test/base/testing_pref_service_syncable.h"
21 #include "components/variations/pref_names.h"
22 #include "components/variations/proto/study.pb.h"
23 #include "components/variations/proto/variations_seed.pb.h"
24 #include "components/web_resource/resource_request_allowed_notifier_test_util.h"
25 #include "content/public/test/test_browser_thread.h"
26 #include "content/public/test/test_browser_thread_bundle.h"
27 #include "net/base/url_util.h"
28 #include "net/http/http_response_headers.h"
29 #include "net/http/http_status_code.h"
30 #include "net/url_request/test_url_fetcher_factory.h"
31 #include "testing/gtest/include/gtest/gtest.h"
33 #if defined(OS_CHROMEOS)
34 #include "chrome/browser/chromeos/settings/cros_settings.h"
35 #include "chrome/browser/chromeos/settings/device_settings_service.h"
36 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
39 namespace chrome_variations
{
43 // A test class used to validate expected functionality in VariationsService.
44 class TestVariationsService
: public VariationsService
{
46 TestVariationsService(web_resource::TestRequestAllowedNotifier
* test_notifier
,
47 PrefService
* local_state
)
48 : VariationsService(make_scoped_ptr(new ChromeVariationsServiceClient()),
52 intercepts_fetch_(true),
53 fetch_attempted_(false),
55 // Set this so StartRepeatedVariationsSeedFetch can be called in tests.
56 SetCreateTrialsFromSeedCalledForTesting(true);
59 ~TestVariationsService() override
{}
61 void set_intercepts_fetch(bool value
) {
62 intercepts_fetch_
= value
;
65 bool fetch_attempted() const { return fetch_attempted_
; }
66 bool seed_stored() const { return seed_stored_
; }
67 const std::string
& stored_country() const { return stored_country_
; }
69 void DoActualFetch() override
{
70 if (intercepts_fetch_
) {
71 fetch_attempted_
= true;
75 VariationsService::DoActualFetch();
79 bool StoreSeed(const std::string
& seed_data
,
80 const std::string
& seed_signature
,
81 const std::string
& country_code
,
82 const base::Time
& date_fetched
,
83 bool is_delta_compressed
) override
{
85 stored_country_
= country_code
;
90 bool intercepts_fetch_
;
91 bool fetch_attempted_
;
93 std::string stored_country_
;
95 DISALLOW_COPY_AND_ASSIGN(TestVariationsService
);
98 class TestVariationsServiceObserver
: public VariationsService::Observer
{
100 TestVariationsServiceObserver()
101 : best_effort_changes_notified_(0),
102 crticial_changes_notified_(0) {
104 ~TestVariationsServiceObserver() override
{}
106 void OnExperimentChangesDetected(Severity severity
) override
{
109 ++best_effort_changes_notified_
;
112 ++crticial_changes_notified_
;
117 int best_effort_changes_notified() const {
118 return best_effort_changes_notified_
;
121 int crticial_changes_notified() const {
122 return crticial_changes_notified_
;
126 // Number of notification received with BEST_EFFORT severity.
127 int best_effort_changes_notified_
;
129 // Number of notification received with CRITICAL severity.
130 int crticial_changes_notified_
;
132 DISALLOW_COPY_AND_ASSIGN(TestVariationsServiceObserver
);
135 // Populates |seed| with simple test data. The resulting seed will contain one
136 // study called "test", which contains one experiment called "abc" with
137 // probability weight 100. |seed|'s study field will be cleared before adding
139 variations::VariationsSeed
CreateTestSeed() {
140 variations::VariationsSeed seed
;
141 variations::Study
* study
= seed
.add_study();
142 study
->set_name("test");
143 study
->set_default_experiment_name("abc");
144 variations::Study_Experiment
* experiment
= study
->add_experiment();
145 experiment
->set_name("abc");
146 experiment
->set_probability_weight(100);
147 seed
.set_serial_number("123");
151 // Serializes |seed| to protobuf binary format.
152 std::string
SerializeSeed(const variations::VariationsSeed
& seed
) {
153 std::string serialized_seed
;
154 seed
.SerializeToString(&serialized_seed
);
155 return serialized_seed
;
158 // Simulates a variations service response by setting a date header and the
159 // specified HTTP |response_code| on |fetcher|.
160 scoped_refptr
<net::HttpResponseHeaders
> SimulateServerResponse(
162 net::TestURLFetcher
* fetcher
) {
163 EXPECT_TRUE(fetcher
);
164 scoped_refptr
<net::HttpResponseHeaders
> headers(
165 new net::HttpResponseHeaders("date:Wed, 13 Feb 2013 00:25:24 GMT\0\0"));
166 fetcher
->set_response_headers(headers
);
167 fetcher
->set_response_code(response_code
);
171 // Helper class that abstracts away platform-specific details relating to the
172 // pref store used for the "restrict" param policy.
173 class TestVariationsPrefsStore
{
175 TestVariationsPrefsStore() {
176 #if defined(OS_ANDROID)
177 // Android uses profile prefs as the PrefService to generate the URL.
178 VariationsService::RegisterProfilePrefs(prefs_
.registry());
180 VariationsService::RegisterPrefs(prefs_
.registry());
183 #if defined(OS_CHROMEOS)
184 cros_settings_
= chromeos::CrosSettings::Get();
185 DCHECK(cros_settings_
!= NULL
);
186 // Remove the real DeviceSettingsProvider and replace it with a stub that
187 // allows modifications in a test.
188 // TODO(asvitkine): Make a scoped helper class for this operation.
189 device_settings_provider_
= cros_settings_
->GetProvider(
190 chromeos::kReportDeviceVersionInfo
);
191 EXPECT_TRUE(device_settings_provider_
!= NULL
);
192 EXPECT_TRUE(cros_settings_
->RemoveSettingsProvider(
193 device_settings_provider_
));
194 cros_settings_
->AddSettingsProvider(&stub_settings_provider_
);
198 ~TestVariationsPrefsStore() {
199 #if defined(OS_CHROMEOS)
200 // Restore the real DeviceSettingsProvider.
202 cros_settings_
->RemoveSettingsProvider(&stub_settings_provider_
));
203 cros_settings_
->AddSettingsProvider(device_settings_provider_
);
207 void SetVariationsRestrictParameterPolicyValue(const std::string
& value
) {
208 #if defined(OS_CHROMEOS)
209 cros_settings_
->SetString(chromeos::kVariationsRestrictParameter
, value
);
211 prefs_
.SetString(prefs::kVariationsRestrictParameter
, value
);
215 PrefService
* prefs() { return &prefs_
; }
218 #if defined(OS_ANDROID)
219 // Android uses profile prefs as the PrefService to generate the URL.
220 TestingPrefServiceSyncable prefs_
;
222 TestingPrefServiceSimple prefs_
;
225 #if defined(OS_CHROMEOS)
226 chromeos::CrosSettings
* cros_settings_
;
227 chromeos::StubCrosSettingsProvider stub_settings_provider_
;
228 chromeos::CrosSettingsProvider
* device_settings_provider_
;
231 DISALLOW_COPY_AND_ASSIGN(TestVariationsPrefsStore
);
234 // Converts |list_value| to a string, to make it easier for debugging.
235 std::string
ListValueToString(const base::ListValue
& list_value
) {
237 JSONStringValueSerializer
serializer(&json
);
238 serializer
.set_pretty_print(true);
239 serializer
.Serialize(list_value
);
245 class VariationsServiceTest
: public ::testing::Test
{
247 VariationsServiceTest() {}
250 content::TestBrowserThreadBundle thread_bundle_
;
251 #if defined(OS_CHROMEOS)
252 // Not used directly. Initializes CrosSettings for testing.
253 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_
;
254 chromeos::ScopedTestCrosSettings test_cros_settings_
;
257 DISALLOW_COPY_AND_ASSIGN(VariationsServiceTest
);
260 TEST_F(VariationsServiceTest
, GetVariationsServerURL
) {
261 TestVariationsPrefsStore prefs_store
;
262 PrefService
* prefs
= prefs_store
.prefs();
263 const std::string default_variations_url
=
264 VariationsService::GetDefaultVariationsServerURLForTesting();
267 GURL url
= VariationsService::GetVariationsServerURL(prefs
, std::string());
268 EXPECT_TRUE(base::StartsWith(url
.spec(), default_variations_url
,
269 base::CompareCase::SENSITIVE
));
270 EXPECT_FALSE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
272 prefs_store
.SetVariationsRestrictParameterPolicyValue("restricted");
273 url
= VariationsService::GetVariationsServerURL(prefs
, std::string());
274 EXPECT_TRUE(base::StartsWith(url
.spec(), default_variations_url
,
275 base::CompareCase::SENSITIVE
));
276 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
277 EXPECT_EQ("restricted", value
);
279 // The override value should take precedence over what's in prefs.
280 url
= VariationsService::GetVariationsServerURL(prefs
, "override");
281 EXPECT_TRUE(base::StartsWith(url
.spec(), default_variations_url
,
282 base::CompareCase::SENSITIVE
));
283 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
284 EXPECT_EQ("override", value
);
287 TEST_F(VariationsServiceTest
, VariationsURLHasOSNameParam
) {
288 TestingPrefServiceSimple prefs
;
289 VariationsService::RegisterPrefs(prefs
.registry());
291 VariationsService::GetVariationsServerURL(&prefs
, std::string());
294 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "osname", &value
));
295 EXPECT_FALSE(value
.empty());
298 TEST_F(VariationsServiceTest
, RequestsInitiallyNotAllowed
) {
299 TestingPrefServiceSimple prefs
;
300 VariationsService::RegisterPrefs(prefs
.registry());
302 // Pass ownership to TestVariationsService, but keep a weak pointer to
303 // manipulate it for this test.
304 web_resource::TestRequestAllowedNotifier
* test_notifier
=
305 new web_resource::TestRequestAllowedNotifier(&prefs
);
306 TestVariationsService
test_service(test_notifier
, &prefs
);
308 // Force the notifier to initially disallow requests.
309 test_notifier
->SetRequestsAllowedOverride(false);
310 test_service
.StartRepeatedVariationsSeedFetch();
311 EXPECT_FALSE(test_service
.fetch_attempted());
313 test_notifier
->NotifyObserver();
314 EXPECT_TRUE(test_service
.fetch_attempted());
317 TEST_F(VariationsServiceTest
, RequestsInitiallyAllowed
) {
318 TestingPrefServiceSimple prefs
;
319 VariationsService::RegisterPrefs(prefs
.registry());
321 // Pass ownership to TestVariationsService, but keep a weak pointer to
322 // manipulate it for this test.
323 web_resource::TestRequestAllowedNotifier
* test_notifier
=
324 new web_resource::TestRequestAllowedNotifier(&prefs
);
325 TestVariationsService
test_service(test_notifier
, &prefs
);
327 test_notifier
->SetRequestsAllowedOverride(true);
328 test_service
.StartRepeatedVariationsSeedFetch();
329 EXPECT_TRUE(test_service
.fetch_attempted());
332 TEST_F(VariationsServiceTest
, SeedStoredWhenOKStatus
) {
333 TestingPrefServiceSimple prefs
;
334 VariationsService::RegisterPrefs(prefs
.registry());
336 TestVariationsService
service(
337 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
);
338 service
.variations_server_url_
=
339 VariationsService::GetVariationsServerURL(&prefs
, std::string());
340 service
.set_intercepts_fetch(false);
342 net::TestURLFetcherFactory factory
;
343 service
.DoActualFetch();
345 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
346 SimulateServerResponse(net::HTTP_OK
, fetcher
);
347 fetcher
->SetResponseString(SerializeSeed(CreateTestSeed()));
349 EXPECT_FALSE(service
.seed_stored());
350 service
.OnURLFetchComplete(fetcher
);
351 EXPECT_TRUE(service
.seed_stored());
354 TEST_F(VariationsServiceTest
, SeedNotStoredWhenNonOKStatus
) {
355 const int non_ok_status_codes
[] = {
356 net::HTTP_NO_CONTENT
,
357 net::HTTP_NOT_MODIFIED
,
359 net::HTTP_INTERNAL_SERVER_ERROR
,
360 net::HTTP_SERVICE_UNAVAILABLE
,
363 TestingPrefServiceSimple prefs
;
364 VariationsService::RegisterPrefs(prefs
.registry());
366 VariationsService
service(
367 make_scoped_ptr(new ChromeVariationsServiceClient()),
368 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
, NULL
);
369 service
.variations_server_url_
=
370 VariationsService::GetVariationsServerURL(&prefs
, std::string());
371 for (size_t i
= 0; i
< arraysize(non_ok_status_codes
); ++i
) {
372 net::TestURLFetcherFactory factory
;
373 service
.DoActualFetch();
374 EXPECT_TRUE(prefs
.FindPreference(prefs::kVariationsSeed
)->IsDefaultValue());
376 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
377 SimulateServerResponse(non_ok_status_codes
[i
], fetcher
);
378 service
.OnURLFetchComplete(fetcher
);
380 EXPECT_TRUE(prefs
.FindPreference(prefs::kVariationsSeed
)->IsDefaultValue());
384 TEST_F(VariationsServiceTest
, CountryHeader
) {
385 TestingPrefServiceSimple prefs
;
386 VariationsService::RegisterPrefs(prefs
.registry());
388 TestVariationsService
service(
389 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
);
390 service
.variations_server_url_
=
391 VariationsService::GetVariationsServerURL(&prefs
, std::string());
392 service
.set_intercepts_fetch(false);
394 net::TestURLFetcherFactory factory
;
395 service
.DoActualFetch();
397 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
398 scoped_refptr
<net::HttpResponseHeaders
> headers
=
399 SimulateServerResponse(net::HTTP_OK
, fetcher
);
400 headers
->AddHeader("X-Country: test");
401 fetcher
->SetResponseString(SerializeSeed(CreateTestSeed()));
403 EXPECT_FALSE(service
.seed_stored());
404 service
.OnURLFetchComplete(fetcher
);
405 EXPECT_TRUE(service
.seed_stored());
406 EXPECT_EQ("test", service
.stored_country());
409 TEST_F(VariationsServiceTest
, Observer
) {
410 TestingPrefServiceSimple prefs
;
411 VariationsService::RegisterPrefs(prefs
.registry());
412 VariationsService
service(
413 make_scoped_ptr(new ChromeVariationsServiceClient()),
414 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
, NULL
);
418 int best_effort_count
;
420 int expected_best_effort_notifications
;
421 int expected_crtical_notifications
;
436 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
437 TestVariationsServiceObserver observer
;
438 service
.AddObserver(&observer
);
440 variations::VariationsSeedSimulator::Result result
;
441 result
.normal_group_change_count
= cases
[i
].normal_count
;
442 result
.kill_best_effort_group_change_count
= cases
[i
].best_effort_count
;
443 result
.kill_critical_group_change_count
= cases
[i
].critical_count
;
444 service
.NotifyObservers(result
);
446 EXPECT_EQ(cases
[i
].expected_best_effort_notifications
,
447 observer
.best_effort_changes_notified()) << i
;
448 EXPECT_EQ(cases
[i
].expected_crtical_notifications
,
449 observer
.crticial_changes_notified()) << i
;
451 service
.RemoveObserver(&observer
);
455 TEST_F(VariationsServiceTest
, LoadPermanentConsistencyCountry
) {
457 // Comma separated list, NULL if the pref isn't set initially.
458 const char* pref_value_before
;
460 // NULL indicates that no latest country code is present.
461 const char* latest_country_code
;
462 // Comma separated list.
463 const char* expected_pref_value_after
;
464 std::string expected_country
;
465 VariationsService::LoadPermanentConsistencyCountryResult expected_result
;
467 // Existing pref value present for this version.
468 {"20.0.0.0,us", "20.0.0.0", "ca", "20.0.0.0,us", "us",
469 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ
},
470 {"20.0.0.0,us", "20.0.0.0", "us", "20.0.0.0,us", "us",
471 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ
},
472 {"20.0.0.0,us", "20.0.0.0", nullptr, "20.0.0.0,us", "us",
473 VariationsService::LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ
},
475 // Existing pref value present for a different version.
476 {"19.0.0.0,ca", "20.0.0.0", "us", "20.0.0.0,us", "us",
477 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ
},
478 {"19.0.0.0,us", "20.0.0.0", "us", "20.0.0.0,us", "us",
479 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ
},
480 {"19.0.0.0,ca", "20.0.0.0", nullptr, "19.0.0.0,ca", "",
481 VariationsService::LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ
},
483 // No existing pref value present.
484 {nullptr, "20.0.0.0", "us", "20.0.0.0,us", "us",
485 VariationsService::LOAD_COUNTRY_NO_PREF_HAS_SEED
},
486 {nullptr, "20.0.0.0", nullptr, "", "",
487 VariationsService::LOAD_COUNTRY_NO_PREF_NO_SEED
},
488 {"", "20.0.0.0", "us", "20.0.0.0,us", "us",
489 VariationsService::LOAD_COUNTRY_NO_PREF_HAS_SEED
},
490 {"", "20.0.0.0", nullptr, "", "",
491 VariationsService::LOAD_COUNTRY_NO_PREF_NO_SEED
},
493 // Invalid existing pref value.
494 {"20.0.0.0", "20.0.0.0", "us", "20.0.0.0,us", "us",
495 VariationsService::LOAD_COUNTRY_INVALID_PREF_HAS_SEED
},
496 {"20.0.0.0", "20.0.0.0", nullptr, "", "",
497 VariationsService::LOAD_COUNTRY_INVALID_PREF_NO_SEED
},
498 {"20.0.0.0,us,element3", "20.0.0.0", "us", "20.0.0.0,us", "us",
499 VariationsService::LOAD_COUNTRY_INVALID_PREF_HAS_SEED
},
500 {"20.0.0.0,us,element3", "20.0.0.0", nullptr, "", "",
501 VariationsService::LOAD_COUNTRY_INVALID_PREF_NO_SEED
},
502 {"badversion,ca", "20.0.0.0", "us", "20.0.0.0,us", "us",
503 VariationsService::LOAD_COUNTRY_INVALID_PREF_HAS_SEED
},
504 {"badversion,ca", "20.0.0.0", nullptr, "", "",
505 VariationsService::LOAD_COUNTRY_INVALID_PREF_NO_SEED
},
508 for (const auto& test
: test_cases
) {
509 TestingPrefServiceSimple prefs
;
510 VariationsService::RegisterPrefs(prefs
.registry());
511 VariationsService
service(
512 make_scoped_ptr(new ChromeVariationsServiceClient()),
513 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
, NULL
);
515 if (test
.pref_value_before
) {
516 base::ListValue list_value
;
517 for (const std::string
& component
:
518 base::SplitString(test
.pref_value_before
, ",", base::TRIM_WHITESPACE
,
519 base::SPLIT_WANT_ALL
)) {
520 list_value
.AppendString(component
);
522 prefs
.Set(prefs::kVariationsPermanentConsistencyCountry
, list_value
);
525 variations::VariationsSeed
seed(CreateTestSeed());
526 std::string latest_country
;
527 if (test
.latest_country_code
)
528 latest_country
= test
.latest_country_code
;
530 base::HistogramTester histogram_tester
;
531 EXPECT_EQ(test
.expected_country
,
532 service
.LoadPermanentConsistencyCountry(
533 base::Version(test
.version
), latest_country
))
534 << test
.pref_value_before
<< ", " << test
.version
<< ", "
535 << test
.latest_country_code
;
537 base::ListValue expected_list_value
;
538 for (const std::string
& component
:
539 base::SplitString(test
.expected_pref_value_after
, ",",
540 base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
)) {
541 expected_list_value
.AppendString(component
);
543 const base::ListValue
* pref_value
=
544 prefs
.GetList(prefs::kVariationsPermanentConsistencyCountry
);
545 EXPECT_EQ(ListValueToString(expected_list_value
),
546 ListValueToString(*pref_value
))
547 << test
.pref_value_before
<< ", " << test
.version
<< ", "
548 << test
.latest_country_code
;
550 histogram_tester
.ExpectUniqueSample(
551 "Variations.LoadPermanentConsistencyCountryResult",
552 test
.expected_result
, 1);
556 } // namespace chrome_variations