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 "components/variations/service/variations_service.h"
9 #include "base/base64.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/prefs/testing_pref_service.h"
13 #include "base/sha1.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/test/histogram_tester.h"
18 #include "base/version.h"
19 #include "components/variations/pref_names.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 "net/base/url_util.h"
24 #include "net/http/http_response_headers.h"
25 #include "net/http/http_status_code.h"
26 #include "net/url_request/test_url_fetcher_factory.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 namespace variations
{
33 class TestVariationsServiceClient
: public VariationsServiceClient
{
35 TestVariationsServiceClient() {}
36 ~TestVariationsServiceClient() override
{}
38 // variations::VariationsServiceClient:
39 std::string
GetApplicationLocale() override
{ return std::string(); }
40 base::SequencedWorkerPool
* GetBlockingPool() override
{ return nullptr; }
41 base::Callback
<base::Version(void)> GetVersionForSimulationCallback()
43 return base::Callback
<base::Version(void)>();
45 net::URLRequestContextGetter
* GetURLRequestContext() override
{
48 network_time::NetworkTimeTracker
* GetNetworkTimeTracker() override
{
51 version_info::Channel
GetChannel() override
{
52 return version_info::Channel::UNKNOWN
;
54 bool OverridesRestrictParameter(std::string
* parameter
) override
{
55 if (restrict_parameter_
.empty())
57 *parameter
= restrict_parameter_
;
60 void OverrideUIString(uint32_t hash
, const base::string16
& string
) override
{}
61 void OnInitialStartup() override
{}
63 void set_restrict_parameter(std::string value
) {
64 restrict_parameter_
= value
;
68 std::string restrict_parameter_
;
70 DISALLOW_COPY_AND_ASSIGN(TestVariationsServiceClient
);
73 // A test class used to validate expected functionality in VariationsService.
74 class TestVariationsService
: public VariationsService
{
76 TestVariationsService(
77 scoped_ptr
<web_resource::TestRequestAllowedNotifier
> test_notifier
,
78 PrefService
* local_state
)
79 : VariationsService(make_scoped_ptr(new TestVariationsServiceClient()),
83 intercepts_fetch_(true),
84 fetch_attempted_(false),
86 // Set this so StartRepeatedVariationsSeedFetch can be called in tests.
87 SetCreateTrialsFromSeedCalledForTesting(true);
90 ~TestVariationsService() override
{}
92 void set_intercepts_fetch(bool value
) {
93 intercepts_fetch_
= value
;
96 bool fetch_attempted() const { return fetch_attempted_
; }
97 bool seed_stored() const { return seed_stored_
; }
98 const std::string
& stored_country() const { return stored_country_
; }
100 void DoActualFetch() override
{
101 if (intercepts_fetch_
) {
102 fetch_attempted_
= true;
106 VariationsService::DoActualFetch();
110 bool StoreSeed(const std::string
& seed_data
,
111 const std::string
& seed_signature
,
112 const std::string
& country_code
,
113 const base::Time
& date_fetched
,
114 bool is_delta_compressed
) override
{
116 stored_country_
= country_code
;
121 bool intercepts_fetch_
;
122 bool fetch_attempted_
;
124 std::string stored_country_
;
126 DISALLOW_COPY_AND_ASSIGN(TestVariationsService
);
129 class TestVariationsServiceObserver
: public VariationsService::Observer
{
131 TestVariationsServiceObserver()
132 : best_effort_changes_notified_(0),
133 crticial_changes_notified_(0) {
135 ~TestVariationsServiceObserver() override
{}
137 void OnExperimentChangesDetected(Severity severity
) override
{
140 ++best_effort_changes_notified_
;
143 ++crticial_changes_notified_
;
148 int best_effort_changes_notified() const {
149 return best_effort_changes_notified_
;
152 int crticial_changes_notified() const {
153 return crticial_changes_notified_
;
157 // Number of notification received with BEST_EFFORT severity.
158 int best_effort_changes_notified_
;
160 // Number of notification received with CRITICAL severity.
161 int crticial_changes_notified_
;
163 DISALLOW_COPY_AND_ASSIGN(TestVariationsServiceObserver
);
166 // Populates |seed| with simple test data. The resulting seed will contain one
167 // study called "test", which contains one experiment called "abc" with
168 // probability weight 100. |seed|'s study field will be cleared before adding
170 variations::VariationsSeed
CreateTestSeed() {
171 variations::VariationsSeed seed
;
172 variations::Study
* study
= seed
.add_study();
173 study
->set_name("test");
174 study
->set_default_experiment_name("abc");
175 variations::Study_Experiment
* experiment
= study
->add_experiment();
176 experiment
->set_name("abc");
177 experiment
->set_probability_weight(100);
178 seed
.set_serial_number("123");
182 // Serializes |seed| to protobuf binary format.
183 std::string
SerializeSeed(const variations::VariationsSeed
& seed
) {
184 std::string serialized_seed
;
185 seed
.SerializeToString(&serialized_seed
);
186 return serialized_seed
;
189 // Simulates a variations service response by setting a date header and the
190 // specified HTTP |response_code| on |fetcher|.
191 scoped_refptr
<net::HttpResponseHeaders
> SimulateServerResponse(
193 net::TestURLFetcher
* fetcher
) {
194 EXPECT_TRUE(fetcher
);
195 scoped_refptr
<net::HttpResponseHeaders
> headers(
196 new net::HttpResponseHeaders("date:Wed, 13 Feb 2013 00:25:24 GMT\0\0"));
197 fetcher
->set_response_headers(headers
);
198 fetcher
->set_response_code(response_code
);
202 // Converts |list_value| to a string, to make it easier for debugging.
203 std::string
ListValueToString(const base::ListValue
& list_value
) {
205 JSONStringValueSerializer
serializer(&json
);
206 serializer
.set_pretty_print(true);
207 serializer
.Serialize(list_value
);
213 class VariationsServiceTest
: public ::testing::Test
{
215 VariationsServiceTest() {}
218 base::MessageLoop message_loop_
;
220 DISALLOW_COPY_AND_ASSIGN(VariationsServiceTest
);
223 TEST_F(VariationsServiceTest
, GetVariationsServerURL
) {
224 TestingPrefServiceSimple prefs
;
225 VariationsService::RegisterPrefs(prefs
.registry());
226 const std::string default_variations_url
=
227 VariationsService::GetDefaultVariationsServerURLForTesting();
230 scoped_ptr
<TestVariationsServiceClient
> client
=
231 make_scoped_ptr(new TestVariationsServiceClient());
232 TestVariationsServiceClient
* raw_client
= client
.get();
233 VariationsService
service(
235 make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs
)),
237 GURL url
= service
.GetVariationsServerURL(&prefs
, std::string());
238 EXPECT_TRUE(base::StartsWith(url
.spec(), default_variations_url
,
239 base::CompareCase::SENSITIVE
));
240 EXPECT_FALSE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
242 prefs
.SetString(prefs::kVariationsRestrictParameter
, "restricted");
243 url
= service
.GetVariationsServerURL(&prefs
, std::string());
244 EXPECT_TRUE(base::StartsWith(url
.spec(), default_variations_url
,
245 base::CompareCase::SENSITIVE
));
246 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
247 EXPECT_EQ("restricted", value
);
249 // A client override should take precedence over what's in prefs.
250 raw_client
->set_restrict_parameter("client");
251 url
= service
.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("client", value
);
257 // The override value passed to the method should take precedence over
258 // what's in prefs and a client override.
259 url
= service
.GetVariationsServerURL(&prefs
, "override");
260 EXPECT_TRUE(base::StartsWith(url
.spec(), default_variations_url
,
261 base::CompareCase::SENSITIVE
));
262 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "restrict", &value
));
263 EXPECT_EQ("override", value
);
266 TEST_F(VariationsServiceTest
, VariationsURLHasOSNameParam
) {
267 TestingPrefServiceSimple prefs
;
268 VariationsService::RegisterPrefs(prefs
.registry());
269 TestVariationsService
service(
270 make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs
)),
272 const GURL url
= service
.GetVariationsServerURL(&prefs
, std::string());
275 EXPECT_TRUE(net::GetValueForKeyInQuery(url
, "osname", &value
));
276 EXPECT_FALSE(value
.empty());
279 TEST_F(VariationsServiceTest
, RequestsInitiallyNotAllowed
) {
280 TestingPrefServiceSimple prefs
;
281 VariationsService::RegisterPrefs(prefs
.registry());
283 // Pass ownership to TestVariationsService, but keep a weak pointer to
284 // manipulate it for this test.
285 scoped_ptr
<web_resource::TestRequestAllowedNotifier
> test_notifier
=
286 make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs
));
287 web_resource::TestRequestAllowedNotifier
* raw_notifier
= test_notifier
.get();
288 TestVariationsService
test_service(test_notifier
.Pass(), &prefs
);
290 // Force the notifier to initially disallow requests.
291 raw_notifier
->SetRequestsAllowedOverride(false);
292 test_service
.StartRepeatedVariationsSeedFetch();
293 EXPECT_FALSE(test_service
.fetch_attempted());
295 raw_notifier
->NotifyObserver();
296 EXPECT_TRUE(test_service
.fetch_attempted());
299 TEST_F(VariationsServiceTest
, RequestsInitiallyAllowed
) {
300 TestingPrefServiceSimple prefs
;
301 VariationsService::RegisterPrefs(prefs
.registry());
303 // Pass ownership to TestVariationsService, but keep a weak pointer to
304 // manipulate it for this test.
305 scoped_ptr
<web_resource::TestRequestAllowedNotifier
> test_notifier
=
306 make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs
));
307 web_resource::TestRequestAllowedNotifier
* raw_notifier
= test_notifier
.get();
308 TestVariationsService
test_service(test_notifier
.Pass(), &prefs
);
310 raw_notifier
->SetRequestsAllowedOverride(true);
311 test_service
.StartRepeatedVariationsSeedFetch();
312 EXPECT_TRUE(test_service
.fetch_attempted());
315 TEST_F(VariationsServiceTest
, SeedStoredWhenOKStatus
) {
316 TestingPrefServiceSimple prefs
;
317 VariationsService::RegisterPrefs(prefs
.registry());
319 TestVariationsService
service(
320 make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs
)),
322 service
.variations_server_url_
=
323 service
.GetVariationsServerURL(&prefs
, std::string());
324 service
.set_intercepts_fetch(false);
326 net::TestURLFetcherFactory factory
;
327 service
.DoActualFetch();
329 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
330 SimulateServerResponse(net::HTTP_OK
, fetcher
);
331 fetcher
->SetResponseString(SerializeSeed(CreateTestSeed()));
333 EXPECT_FALSE(service
.seed_stored());
334 service
.OnURLFetchComplete(fetcher
);
335 EXPECT_TRUE(service
.seed_stored());
338 TEST_F(VariationsServiceTest
, SeedNotStoredWhenNonOKStatus
) {
339 const int non_ok_status_codes
[] = {
340 net::HTTP_NO_CONTENT
,
341 net::HTTP_NOT_MODIFIED
,
343 net::HTTP_INTERNAL_SERVER_ERROR
,
344 net::HTTP_SERVICE_UNAVAILABLE
,
347 TestingPrefServiceSimple prefs
;
348 VariationsService::RegisterPrefs(prefs
.registry());
350 VariationsService
service(
351 make_scoped_ptr(new TestVariationsServiceClient()),
352 make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs
)),
354 service
.variations_server_url_
=
355 service
.GetVariationsServerURL(&prefs
, std::string());
356 for (size_t i
= 0; i
< arraysize(non_ok_status_codes
); ++i
) {
357 net::TestURLFetcherFactory factory
;
358 service
.DoActualFetch();
359 EXPECT_TRUE(prefs
.FindPreference(prefs::kVariationsSeed
)->IsDefaultValue());
361 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
362 SimulateServerResponse(non_ok_status_codes
[i
], fetcher
);
363 service
.OnURLFetchComplete(fetcher
);
365 EXPECT_TRUE(prefs
.FindPreference(prefs::kVariationsSeed
)->IsDefaultValue());
369 TEST_F(VariationsServiceTest
, CountryHeader
) {
370 TestingPrefServiceSimple prefs
;
371 VariationsService::RegisterPrefs(prefs
.registry());
373 TestVariationsService
service(
374 make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs
)),
376 service
.variations_server_url_
=
377 service
.GetVariationsServerURL(&prefs
, std::string());
378 service
.set_intercepts_fetch(false);
380 net::TestURLFetcherFactory factory
;
381 service
.DoActualFetch();
383 net::TestURLFetcher
* fetcher
= factory
.GetFetcherByID(0);
384 scoped_refptr
<net::HttpResponseHeaders
> headers
=
385 SimulateServerResponse(net::HTTP_OK
, fetcher
);
386 headers
->AddHeader("X-Country: test");
387 fetcher
->SetResponseString(SerializeSeed(CreateTestSeed()));
389 EXPECT_FALSE(service
.seed_stored());
390 service
.OnURLFetchComplete(fetcher
);
391 EXPECT_TRUE(service
.seed_stored());
392 EXPECT_EQ("test", service
.stored_country());
395 TEST_F(VariationsServiceTest
, Observer
) {
396 TestingPrefServiceSimple prefs
;
397 VariationsService::RegisterPrefs(prefs
.registry());
398 VariationsService
service(
399 make_scoped_ptr(new TestVariationsServiceClient()),
400 make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs
)),
405 int best_effort_count
;
407 int expected_best_effort_notifications
;
408 int expected_crtical_notifications
;
423 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
424 TestVariationsServiceObserver observer
;
425 service
.AddObserver(&observer
);
427 variations::VariationsSeedSimulator::Result result
;
428 result
.normal_group_change_count
= cases
[i
].normal_count
;
429 result
.kill_best_effort_group_change_count
= cases
[i
].best_effort_count
;
430 result
.kill_critical_group_change_count
= cases
[i
].critical_count
;
431 service
.NotifyObservers(result
);
433 EXPECT_EQ(cases
[i
].expected_best_effort_notifications
,
434 observer
.best_effort_changes_notified()) << i
;
435 EXPECT_EQ(cases
[i
].expected_crtical_notifications
,
436 observer
.crticial_changes_notified()) << i
;
438 service
.RemoveObserver(&observer
);
442 TEST_F(VariationsServiceTest
, LoadPermanentConsistencyCountry
) {
444 // Comma separated list, NULL if the pref isn't set initially.
445 const char* pref_value_before
;
447 // NULL indicates that no latest country code is present.
448 const char* latest_country_code
;
449 // Comma separated list.
450 const char* expected_pref_value_after
;
451 std::string expected_country
;
452 VariationsService::LoadPermanentConsistencyCountryResult expected_result
;
454 // Existing pref value present for this version.
455 {"20.0.0.0,us", "20.0.0.0", "ca", "20.0.0.0,us", "us",
456 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ
},
457 {"20.0.0.0,us", "20.0.0.0", "us", "20.0.0.0,us", "us",
458 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ
},
459 {"20.0.0.0,us", "20.0.0.0", nullptr, "20.0.0.0,us", "us",
460 VariationsService::LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ
},
462 // Existing pref value present for a different version.
463 {"19.0.0.0,ca", "20.0.0.0", "us", "20.0.0.0,us", "us",
464 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ
},
465 {"19.0.0.0,us", "20.0.0.0", "us", "20.0.0.0,us", "us",
466 VariationsService::LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ
},
467 {"19.0.0.0,ca", "20.0.0.0", nullptr, "19.0.0.0,ca", "",
468 VariationsService::LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ
},
470 // No existing pref value present.
471 {nullptr, "20.0.0.0", "us", "20.0.0.0,us", "us",
472 VariationsService::LOAD_COUNTRY_NO_PREF_HAS_SEED
},
473 {nullptr, "20.0.0.0", nullptr, "", "",
474 VariationsService::LOAD_COUNTRY_NO_PREF_NO_SEED
},
475 {"", "20.0.0.0", "us", "20.0.0.0,us", "us",
476 VariationsService::LOAD_COUNTRY_NO_PREF_HAS_SEED
},
477 {"", "20.0.0.0", nullptr, "", "",
478 VariationsService::LOAD_COUNTRY_NO_PREF_NO_SEED
},
480 // Invalid existing pref value.
481 {"20.0.0.0", "20.0.0.0", "us", "20.0.0.0,us", "us",
482 VariationsService::LOAD_COUNTRY_INVALID_PREF_HAS_SEED
},
483 {"20.0.0.0", "20.0.0.0", nullptr, "", "",
484 VariationsService::LOAD_COUNTRY_INVALID_PREF_NO_SEED
},
485 {"20.0.0.0,us,element3", "20.0.0.0", "us", "20.0.0.0,us", "us",
486 VariationsService::LOAD_COUNTRY_INVALID_PREF_HAS_SEED
},
487 {"20.0.0.0,us,element3", "20.0.0.0", nullptr, "", "",
488 VariationsService::LOAD_COUNTRY_INVALID_PREF_NO_SEED
},
489 {"badversion,ca", "20.0.0.0", "us", "20.0.0.0,us", "us",
490 VariationsService::LOAD_COUNTRY_INVALID_PREF_HAS_SEED
},
491 {"badversion,ca", "20.0.0.0", nullptr, "", "",
492 VariationsService::LOAD_COUNTRY_INVALID_PREF_NO_SEED
},
495 for (const auto& test
: test_cases
) {
496 TestingPrefServiceSimple prefs
;
497 VariationsService::RegisterPrefs(prefs
.registry());
498 VariationsService
service(
499 make_scoped_ptr(new TestVariationsServiceClient()),
500 make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs
)),
503 if (test
.pref_value_before
) {
504 base::ListValue list_value
;
505 for (const std::string
& component
:
506 base::SplitString(test
.pref_value_before
, ",", base::TRIM_WHITESPACE
,
507 base::SPLIT_WANT_ALL
)) {
508 list_value
.AppendString(component
);
510 prefs
.Set(prefs::kVariationsPermanentConsistencyCountry
, list_value
);
513 variations::VariationsSeed
seed(CreateTestSeed());
514 std::string latest_country
;
515 if (test
.latest_country_code
)
516 latest_country
= test
.latest_country_code
;
518 base::HistogramTester histogram_tester
;
519 EXPECT_EQ(test
.expected_country
,
520 service
.LoadPermanentConsistencyCountry(
521 base::Version(test
.version
), latest_country
))
522 << test
.pref_value_before
<< ", " << test
.version
<< ", "
523 << test
.latest_country_code
;
525 base::ListValue expected_list_value
;
526 for (const std::string
& component
:
527 base::SplitString(test
.expected_pref_value_after
, ",",
528 base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
)) {
529 expected_list_value
.AppendString(component
);
531 const base::ListValue
* pref_value
=
532 prefs
.GetList(prefs::kVariationsPermanentConsistencyCountry
);
533 EXPECT_EQ(ListValueToString(expected_list_value
),
534 ListValueToString(*pref_value
))
535 << test
.pref_value_before
<< ", " << test
.version
<< ", "
536 << test
.latest_country_code
;
538 histogram_tester
.ExpectUniqueSample(
539 "Variations.LoadPermanentConsistencyCountryResult",
540 test
.expected_result
, 1);
544 } // namespace variations