Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / components / variations / service / variations_service_unittest.cc
blob2d75afbc504947e0330aea2a9c47b5b89ad73de9
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"
7 #include <vector>
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 {
31 namespace {
33 class TestVariationsServiceClient : public VariationsServiceClient {
34 public:
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()
42 override {
43 return base::Callback<base::Version(void)>();
45 net::URLRequestContextGetter* GetURLRequestContext() override {
46 return nullptr;
48 network_time::NetworkTimeTracker* GetNetworkTimeTracker() override {
49 return nullptr;
51 version_info::Channel GetChannel() override {
52 return version_info::Channel::UNKNOWN;
54 bool OverridesRestrictParameter(std::string* parameter) override {
55 if (restrict_parameter_.empty())
56 return false;
57 *parameter = restrict_parameter_;
58 return true;
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;
67 private:
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 {
75 public:
76 TestVariationsService(
77 scoped_ptr<web_resource::TestRequestAllowedNotifier> test_notifier,
78 PrefService* local_state)
79 : VariationsService(make_scoped_ptr(new TestVariationsServiceClient()),
80 test_notifier.Pass(),
81 local_state,
82 NULL),
83 intercepts_fetch_(true),
84 fetch_attempted_(false),
85 seed_stored_(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;
103 return;
106 VariationsService::DoActualFetch();
109 protected:
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 {
115 seed_stored_ = true;
116 stored_country_ = country_code;
117 return true;
120 private:
121 bool intercepts_fetch_;
122 bool fetch_attempted_;
123 bool seed_stored_;
124 std::string stored_country_;
126 DISALLOW_COPY_AND_ASSIGN(TestVariationsService);
129 class TestVariationsServiceObserver : public VariationsService::Observer {
130 public:
131 TestVariationsServiceObserver()
132 : best_effort_changes_notified_(0),
133 crticial_changes_notified_(0) {
135 ~TestVariationsServiceObserver() override {}
137 void OnExperimentChangesDetected(Severity severity) override {
138 switch (severity) {
139 case BEST_EFFORT:
140 ++best_effort_changes_notified_;
141 break;
142 case CRITICAL:
143 ++crticial_changes_notified_;
144 break;
148 int best_effort_changes_notified() const {
149 return best_effort_changes_notified_;
152 int crticial_changes_notified() const {
153 return crticial_changes_notified_;
156 private:
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
169 // the new study.
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");
179 return seed;
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(
192 int response_code,
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);
199 return headers;
202 // Converts |list_value| to a string, to make it easier for debugging.
203 std::string ListValueToString(const base::ListValue& list_value) {
204 std::string json;
205 JSONStringValueSerializer serializer(&json);
206 serializer.set_pretty_print(true);
207 serializer.Serialize(list_value);
208 return json;
211 } // namespace
213 class VariationsServiceTest : public ::testing::Test {
214 protected:
215 VariationsServiceTest() {}
217 private:
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();
229 std::string value;
230 scoped_ptr<TestVariationsServiceClient> client =
231 make_scoped_ptr(new TestVariationsServiceClient());
232 TestVariationsServiceClient* raw_client = client.get();
233 VariationsService service(
234 client.Pass(),
235 make_scoped_ptr(new web_resource::TestRequestAllowedNotifier(&prefs)),
236 &prefs, NULL);
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)),
271 &prefs);
272 const GURL url = service.GetVariationsServerURL(&prefs, std::string());
274 std::string value;
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)),
321 &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,
342 net::HTTP_NOT_FOUND,
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)),
353 &prefs, NULL);
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)),
375 &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)),
401 &prefs, NULL);
403 struct {
404 int normal_count;
405 int best_effort_count;
406 int critical_count;
407 int expected_best_effort_notifications;
408 int expected_crtical_notifications;
409 } cases[] = {
410 {0, 0, 0, 0, 0},
411 {1, 0, 0, 0, 0},
412 {10, 0, 0, 0, 0},
413 {0, 1, 0, 1, 0},
414 {0, 10, 0, 1, 0},
415 {0, 0, 1, 0, 1},
416 {0, 0, 10, 0, 1},
417 {0, 1, 1, 0, 1},
418 {1, 1, 1, 0, 1},
419 {1, 1, 0, 1, 0},
420 {1, 0, 1, 0, 1},
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) {
443 struct {
444 // Comma separated list, NULL if the pref isn't set initially.
445 const char* pref_value_before;
446 const char* version;
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;
453 } test_cases[] = {
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)),
501 &prefs, NULL);
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