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_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/test/histogram_tester.h"
16 #include "base/version.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/test/base/testing_browser_process.h"
19 #include "chrome/test/base/testing_pref_service_syncable.h"
20 #include "components/variations/proto/study.pb.h"
21 #include "components/variations/proto/variations_seed.pb.h"
22 #include "components/web_resource/resource_request_allowed_notifier_test_util.h"
23 #include "content/public/test/test_browser_thread.h"
24 #include "content/public/test/test_browser_thread_bundle.h"
25 #include "net/base/url_util.h"
26 #include "net/http/http_response_headers.h"
27 #include "net/http/http_status_code.h"
28 #include "net/url_request/test_url_fetcher_factory.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 #if defined(OS_CHROMEOS)
32 #include "chrome/browser/chromeos/settings/cros_settings.h"
33 #include "chrome/browser/chromeos/settings/device_settings_service.h"
34 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
37 namespace chrome_variations
{
41 // A test class used to validate expected functionality in VariationsService.
42 class TestVariationsService
: public VariationsService
{
44 TestVariationsService(web_resource::TestRequestAllowedNotifier
* test_notifier
,
45 PrefService
* local_state
)
46 : VariationsService(test_notifier
, local_state
, NULL
),
47 intercepts_fetch_(true),
48 fetch_attempted_(false),
50 // Set this so StartRepeatedVariationsSeedFetch can be called in tests.
51 SetCreateTrialsFromSeedCalledForTesting(true);
54 ~TestVariationsService() override
{}
56 void set_intercepts_fetch(bool value
) {
57 intercepts_fetch_
= value
;
60 bool fetch_attempted() const { return fetch_attempted_
; }
62 bool seed_stored() const { return seed_stored_
; }
64 void DoActualFetch() override
{
65 if (intercepts_fetch_
) {
66 fetch_attempted_
= true;
70 VariationsService::DoActualFetch();
74 void StoreSeed(const std::string
& seed_data
,
75 const std::string
& seed_signature
,
76 const base::Time
& date_fetched
) override
{
81 bool intercepts_fetch_
;
82 bool fetch_attempted_
;
85 DISALLOW_COPY_AND_ASSIGN(TestVariationsService
);
88 class TestVariationsServiceObserver
: public VariationsService::Observer
{
90 TestVariationsServiceObserver()
91 : best_effort_changes_notified_(0),
92 crticial_changes_notified_(0) {
94 ~TestVariationsServiceObserver() override
{}
96 void OnExperimentChangesDetected(Severity severity
) override
{
99 ++best_effort_changes_notified_
;
102 ++crticial_changes_notified_
;
107 int best_effort_changes_notified() const {
108 return best_effort_changes_notified_
;
111 int crticial_changes_notified() const {
112 return crticial_changes_notified_
;
116 // Number of notification received with BEST_EFFORT severity.
117 int best_effort_changes_notified_
;
119 // Number of notification received with CRITICAL severity.
120 int crticial_changes_notified_
;
122 DISALLOW_COPY_AND_ASSIGN(TestVariationsServiceObserver
);
125 // Populates |seed| with simple test data. The resulting seed will contain one
126 // study called "test", which contains one experiment called "abc" with
127 // probability weight 100. |seed|'s study field will be cleared before adding
129 variations::VariationsSeed
CreateTestSeed() {
130 variations::VariationsSeed seed
;
131 variations::Study
* study
= seed
.add_study();
132 study
->set_name("test");
133 study
->set_default_experiment_name("abc");
134 variations::Study_Experiment
* experiment
= study
->add_experiment();
135 experiment
->set_name("abc");
136 experiment
->set_probability_weight(100);
137 seed
.set_serial_number("123");
141 // Serializes |seed| to protobuf binary format.
142 std::string
SerializeSeed(const variations::VariationsSeed
& seed
) {
143 std::string serialized_seed
;
144 seed
.SerializeToString(&serialized_seed
);
145 return serialized_seed
;
148 // Simulates a variations service response by setting a date header and the
149 // specified HTTP |response_code| on |fetcher|.
150 void SimulateServerResponse(int response_code
, net::TestURLFetcher
* fetcher
) {
151 ASSERT_TRUE(fetcher
);
152 scoped_refptr
<net::HttpResponseHeaders
> headers(
153 new net::HttpResponseHeaders("date:Wed, 13 Feb 2013 00:25:24 GMT\0\0"));
154 fetcher
->set_response_headers(headers
);
155 fetcher
->set_response_code(response_code
);
158 // Helper class that abstracts away platform-specific details relating to the
159 // pref store used for the "restrict" param policy.
160 class TestVariationsPrefsStore
{
162 TestVariationsPrefsStore() {
163 #if defined(OS_ANDROID)
164 // Android uses profile prefs as the PrefService to generate the URL.
165 VariationsService::RegisterProfilePrefs(prefs_
.registry());
167 VariationsService::RegisterPrefs(prefs_
.registry());
170 #if defined(OS_CHROMEOS)
171 cros_settings_
= chromeos::CrosSettings::Get();
172 DCHECK(cros_settings_
!= NULL
);
173 // Remove the real DeviceSettingsProvider and replace it with a stub that
174 // allows modifications in a test.
175 // TODO(asvitkine): Make a scoped helper class for this operation.
176 device_settings_provider_
= cros_settings_
->GetProvider(
177 chromeos::kReportDeviceVersionInfo
);
178 EXPECT_TRUE(device_settings_provider_
!= NULL
);
179 EXPECT_TRUE(cros_settings_
->RemoveSettingsProvider(
180 device_settings_provider_
));
181 cros_settings_
->AddSettingsProvider(&stub_settings_provider_
);
185 ~TestVariationsPrefsStore() {
186 #if defined(OS_CHROMEOS)
187 // Restore the real DeviceSettingsProvider.
189 cros_settings_
->RemoveSettingsProvider(&stub_settings_provider_
));
190 cros_settings_
->AddSettingsProvider(device_settings_provider_
);
194 void SetVariationsRestrictParameterPolicyValue(const std::string
& value
) {
195 #if defined(OS_CHROMEOS)
196 cros_settings_
->SetString(chromeos::kVariationsRestrictParameter
, value
);
198 prefs_
.SetString(prefs::kVariationsRestrictParameter
, value
);
202 PrefService
* prefs() { return &prefs_
; }
205 #if defined(OS_ANDROID)
206 // Android uses profile prefs as the PrefService to generate the URL.
207 TestingPrefServiceSyncable prefs_
;
209 TestingPrefServiceSimple prefs_
;
212 #if defined(OS_CHROMEOS)
213 chromeos::CrosSettings
* cros_settings_
;
214 chromeos::StubCrosSettingsProvider stub_settings_provider_
;
215 chromeos::CrosSettingsProvider
* device_settings_provider_
;
218 DISALLOW_COPY_AND_ASSIGN(TestVariationsPrefsStore
);
223 class VariationsServiceTest
: public ::testing::Test
{
225 VariationsServiceTest() {}
228 content::TestBrowserThreadBundle thread_bundle_
;
229 #if defined(OS_CHROMEOS)
230 // Not used directly. Initializes CrosSettings for testing.
231 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_
;
232 chromeos::ScopedTestCrosSettings test_cros_settings_
;
235 DISALLOW_COPY_AND_ASSIGN(VariationsServiceTest
);
238 TEST_F(VariationsServiceTest
, GetVariationsServerURL
) {
239 TestVariationsPrefsStore prefs_store
;
240 PrefService
* prefs
= prefs_store
.prefs();
241 const std::string default_variations_url
=
242 VariationsService::GetDefaultVariationsServerURLForTesting();
245 GURL url
= VariationsService::GetVariationsServerURL(prefs
, std::string());
246 EXPECT_TRUE(base::StartsWith(url
.spec(), default_variations_url
,
247 base::CompareCase::SENSITIVE
));
248 EXPECT_FALSE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
250 prefs_store
.SetVariationsRestrictParameterPolicyValue("restricted");
251 url
= VariationsService::GetVariationsServerURL(prefs
, std::string());
252 EXPECT_TRUE(base::StartsWith(url
.spec(), default_variations_url
,
253 base::CompareCase::SENSITIVE
));
254 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
255 EXPECT_EQ("restricted", value
);
257 // The override value should take precedence over what's in prefs.
258 url
= VariationsService::GetVariationsServerURL(prefs
, "override");
259 EXPECT_TRUE(base::StartsWith(url
.spec(), default_variations_url
,
260 base::CompareCase::SENSITIVE
));
261 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
262 EXPECT_EQ("override", value
);
265 TEST_F(VariationsServiceTest
, VariationsURLHasOSNameParam
) {
266 TestingPrefServiceSimple prefs
;
267 VariationsService::RegisterPrefs(prefs
.registry());
269 VariationsService::GetVariationsServerURL(&prefs
, std::string());
272 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "osname", &value
));
273 EXPECT_FALSE(value
.empty());
276 TEST_F(VariationsServiceTest
, RequestsInitiallyNotAllowed
) {
277 TestingPrefServiceSimple prefs
;
278 VariationsService::RegisterPrefs(prefs
.registry());
280 // Pass ownership to TestVariationsService, but keep a weak pointer to
281 // manipulate it for this test.
282 web_resource::TestRequestAllowedNotifier
* test_notifier
=
283 new web_resource::TestRequestAllowedNotifier(&prefs
);
284 TestVariationsService
test_service(test_notifier
, &prefs
);
286 // Force the notifier to initially disallow requests.
287 test_notifier
->SetRequestsAllowedOverride(false);
288 test_service
.StartRepeatedVariationsSeedFetch();
289 EXPECT_FALSE(test_service
.fetch_attempted());
291 test_notifier
->NotifyObserver();
292 EXPECT_TRUE(test_service
.fetch_attempted());
295 TEST_F(VariationsServiceTest
, RequestsInitiallyAllowed
) {
296 TestingPrefServiceSimple prefs
;
297 VariationsService::RegisterPrefs(prefs
.registry());
299 // Pass ownership to TestVariationsService, but keep a weak pointer to
300 // manipulate it for this test.
301 web_resource::TestRequestAllowedNotifier
* test_notifier
=
302 new web_resource::TestRequestAllowedNotifier(&prefs
);
303 TestVariationsService
test_service(test_notifier
, &prefs
);
305 test_notifier
->SetRequestsAllowedOverride(true);
306 test_service
.StartRepeatedVariationsSeedFetch();
307 EXPECT_TRUE(test_service
.fetch_attempted());
310 TEST_F(VariationsServiceTest
, SeedStoredWhenOKStatus
) {
311 TestingPrefServiceSimple prefs
;
312 VariationsService::RegisterPrefs(prefs
.registry());
314 TestVariationsService
service(
315 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
);
316 service
.variations_server_url_
=
317 VariationsService::GetVariationsServerURL(&prefs
, std::string());
318 service
.set_intercepts_fetch(false);
320 net::TestURLFetcherFactory factory
;
321 service
.DoActualFetch();
323 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
324 SimulateServerResponse(net::HTTP_OK
, fetcher
);
325 fetcher
->SetResponseString(SerializeSeed(CreateTestSeed()));
327 EXPECT_FALSE(service
.seed_stored());
328 service
.OnURLFetchComplete(fetcher
);
329 EXPECT_TRUE(service
.seed_stored());
332 TEST_F(VariationsServiceTest
, SeedNotStoredWhenNonOKStatus
) {
333 const int non_ok_status_codes
[] = {
334 net::HTTP_NO_CONTENT
,
335 net::HTTP_NOT_MODIFIED
,
337 net::HTTP_INTERNAL_SERVER_ERROR
,
338 net::HTTP_SERVICE_UNAVAILABLE
,
341 TestingPrefServiceSimple prefs
;
342 VariationsService::RegisterPrefs(prefs
.registry());
344 VariationsService
service(
345 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
, NULL
);
346 service
.variations_server_url_
=
347 VariationsService::GetVariationsServerURL(&prefs
, std::string());
348 for (size_t i
= 0; i
< arraysize(non_ok_status_codes
); ++i
) {
349 net::TestURLFetcherFactory factory
;
350 service
.DoActualFetch();
351 EXPECT_TRUE(prefs
.FindPreference(prefs::kVariationsSeed
)->IsDefaultValue());
353 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
354 SimulateServerResponse(non_ok_status_codes
[i
], fetcher
);
355 service
.OnURLFetchComplete(fetcher
);
357 EXPECT_TRUE(prefs
.FindPreference(prefs::kVariationsSeed
)->IsDefaultValue());
361 TEST_F(VariationsServiceTest
, SeedDateUpdatedOn304Status
) {
362 TestingPrefServiceSimple prefs
;
363 VariationsService::RegisterPrefs(prefs
.registry());
365 net::TestURLFetcherFactory factory
;
366 VariationsService
service(
367 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
, NULL
);
368 service
.variations_server_url_
=
369 VariationsService::GetVariationsServerURL(&prefs
, std::string());
370 service
.DoActualFetch();
372 prefs
.FindPreference(prefs::kVariationsSeedDate
)->IsDefaultValue());
374 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
375 SimulateServerResponse(net::HTTP_NOT_MODIFIED
, fetcher
);
376 service
.OnURLFetchComplete(fetcher
);
378 prefs
.FindPreference(prefs::kVariationsSeedDate
)->IsDefaultValue());
381 TEST_F(VariationsServiceTest
, Observer
) {
382 TestingPrefServiceSimple prefs
;
383 VariationsService::RegisterPrefs(prefs
.registry());
384 VariationsService
service(
385 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
, NULL
);
389 int best_effort_count
;
391 int expected_best_effort_notifications
;
392 int expected_crtical_notifications
;
407 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
408 TestVariationsServiceObserver observer
;
409 service
.AddObserver(&observer
);
411 variations::VariationsSeedSimulator::Result result
;
412 result
.normal_group_change_count
= cases
[i
].normal_count
;
413 result
.kill_best_effort_group_change_count
= cases
[i
].best_effort_count
;
414 result
.kill_critical_group_change_count
= cases
[i
].critical_count
;
415 service
.NotifyObservers(result
);
417 EXPECT_EQ(cases
[i
].expected_best_effort_notifications
,
418 observer
.best_effort_changes_notified()) << i
;
419 EXPECT_EQ(cases
[i
].expected_crtical_notifications
,
420 observer
.crticial_changes_notified()) << i
;
422 service
.RemoveObserver(&observer
);
426 TEST_F(VariationsServiceTest
, LoadPermanentConsistencyCountry
) {
428 // Comma separated list, NULL if the pref isn't set initially.
429 const char* pref_value_before
;
431 // NULL indicates that no country code is present in the seed.
432 const char* seed_country_code
;
433 // Comma separated list.
434 const char* expected_pref_value_after
;
435 std::string expected_country
;
436 VariationsService::LoadPermanentConsistencyCountryResult expected_result
;
438 // Existing pref value present for this version.
439 {"20.0.0.0,us", "20.0.0.0", "ca", "20.0.0.0,us", "us",
440 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ
},
441 {"20.0.0.0,us", "20.0.0.0", "us", "20.0.0.0,us", "us",
442 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ
},
443 {"20.0.0.0,us", "20.0.0.0", nullptr, "20.0.0.0,us", "us",
444 VariationsService::LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ
},
446 // Existing pref value present for a different version.
447 {"19.0.0.0,ca", "20.0.0.0", "us", "20.0.0.0,us", "us",
448 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ
},
449 {"19.0.0.0,us", "20.0.0.0", "us", "20.0.0.0,us", "us",
450 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ
},
451 {"19.0.0.0,ca", "20.0.0.0", nullptr, "", "",
452 VariationsService::LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ
},
454 // No existing pref value present.
455 {nullptr, "20.0.0.0", "us", "20.0.0.0,us", "us",
456 VariationsService::LOAD_COUNTRY_NO_PREF_HAS_SEED
},
457 {nullptr, "20.0.0.0", nullptr, "", "",
458 VariationsService::LOAD_COUNTRY_NO_PREF_NO_SEED
},
459 {"", "20.0.0.0", "us", "20.0.0.0,us", "us",
460 VariationsService::LOAD_COUNTRY_NO_PREF_HAS_SEED
},
461 {"", "20.0.0.0", nullptr, "", "",
462 VariationsService::LOAD_COUNTRY_NO_PREF_NO_SEED
},
464 // Invalid existing pref value.
465 {"20.0.0.0", "20.0.0.0", "us", "20.0.0.0,us", "us",
466 VariationsService::LOAD_COUNTRY_INVALID_PREF_HAS_SEED
},
467 {"20.0.0.0", "20.0.0.0", nullptr, "", "",
468 VariationsService::LOAD_COUNTRY_INVALID_PREF_NO_SEED
},
469 {"20.0.0.0,us,element3", "20.0.0.0", "us", "20.0.0.0,us", "us",
470 VariationsService::LOAD_COUNTRY_INVALID_PREF_HAS_SEED
},
471 {"20.0.0.0,us,element3", "20.0.0.0", nullptr, "", "",
472 VariationsService::LOAD_COUNTRY_INVALID_PREF_NO_SEED
},
473 {"badversion,ca", "20.0.0.0", "us", "20.0.0.0,us", "us",
474 VariationsService::LOAD_COUNTRY_INVALID_PREF_HAS_SEED
},
475 {"badversion,ca", "20.0.0.0", nullptr, "", "",
476 VariationsService::LOAD_COUNTRY_INVALID_PREF_NO_SEED
},
479 for (const auto& test
: test_cases
) {
480 TestingPrefServiceSimple prefs
;
481 VariationsService::RegisterPrefs(prefs
.registry());
482 VariationsService
service(
483 new web_resource::TestRequestAllowedNotifier(&prefs
), &prefs
, NULL
);
485 if (test
.pref_value_before
) {
486 base::ListValue list_value
;
487 for (const std::string
& component
: base::SplitString(
488 test
.pref_value_before
, ",",
489 base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
))
490 list_value
.AppendString(component
);
491 prefs
.Set(prefs::kVariationsPermanentConsistencyCountry
, list_value
);
494 variations::VariationsSeed
seed(CreateTestSeed());
495 if (test
.seed_country_code
)
496 seed
.set_country_code(test
.seed_country_code
);
498 base::HistogramTester histogram_tester
;
499 EXPECT_EQ(test
.expected_country
, service
.LoadPermanentConsistencyCountry(
500 base::Version(test
.version
), seed
));
502 base::ListValue expected_list_value
;
503 for (const std::string
& component
: base::SplitString(
504 test
.expected_pref_value_after
, ",",
505 base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
))
506 expected_list_value
.AppendString(component
);
507 EXPECT_TRUE(expected_list_value
.Equals(
508 prefs
.GetList(prefs::kVariationsPermanentConsistencyCountry
)));
510 histogram_tester
.ExpectUniqueSample(
511 "Variations.LoadPermanentConsistencyCountryResult",
512 test
.expected_result
, 1);
516 } // namespace chrome_variations