1 // Copyright 2018 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.
4
5 #include "chrome/browser/android/explore_sites/explore_sites_service_impl.h"
6
7 #include "base/bind.h"
8 #include "base/test/bind.h"
9 #include "base/test/metrics/histogram_tester.h"
10 #include "base/test/mock_entropy_provider.h"
11 #include "base/test/scoped_feature_list.h"
12 #include "base/test/task_environment.h"
13 #include "chrome/browser/android/explore_sites/catalog.pb.h"
14 #include "chrome/browser/flags/android/chrome_feature_list.h"
15 #include "services/network/public/cpp/resource_request.h"
16 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
17 #include "services/network/test/test_url_loader_factory.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 namespace {
22 const char kTechnologyCategoryName[] = "Technology";
23 const char kScienceCategoryName[] = "Science";
24 const char kBooksCategoryName[] = "Books";
25 const char kCountryCode[] = "zz";
26 const char kSite1UrlNoTrailingSlash[] = "https://example.com";
27 const char kSite1Url[] = "https://example.com/";
28 const char kSite2Url[] = "https://sample.com/";
29 const char kSite4Url[] = "https://exemplar.com/";
30 const char kSite1Name[] = "example";
31 const char kSite2Name[] = "sample";
32 const char kSite3Name[] = "exemplar";
33 const char kAcceptLanguages[] = "en-US,en;q=0.5";
34 } // namespace
35
36 namespace explore_sites {
37
38 using testing::HasSubstr;
39 using testing::Not;
40
41 class ExploreSitesServiceImplTest : public testing::Test {
42 public:
43 ExploreSitesServiceImplTest();
44 ~ExploreSitesServiceImplTest() override = default;
45
SetUp()46 void SetUp() override {
47 std::unique_ptr<ExploreSitesStore> store =
48 std::make_unique<ExploreSitesStore>(
49 task_environment_.GetMainThreadTaskRunner());
50 auto history_stats_reporter =
51 std::make_unique<HistoryStatisticsReporter>(nullptr, nullptr);
52 service_ = std::make_unique<ExploreSitesServiceImpl>(
53 std::move(store),
54 std::make_unique<TestURLLoaderFactoryGetter>(
55 test_shared_url_loader_factory_),
56 std::move(history_stats_reporter));
57 success_ = false;
58 test_data_ = CreateTestDataProto();
59 mostly_valid_test_data_ = CreateMostlyValidTestDataProto();
60 bad_test_data_ = CreateBadTestDataProto();
61 histogram_tester_ = std::make_unique<base::HistogramTester>();
62 }
63
UpdateCatalogDoneCallback(bool success)64 void UpdateCatalogDoneCallback(bool success) {
65 success_ = success;
66 callback_count_++;
67 }
68
CatalogCallback(GetCatalogStatus status,std::unique_ptr<std::vector<ExploreSitesCategory>> categories)69 void CatalogCallback(
70 GetCatalogStatus status,
71 std::unique_ptr<std::vector<ExploreSitesCategory>> categories) {
72 database_status_ = status;
73 if (categories != nullptr) {
74 database_categories_ = std::move(categories);
75 }
76 }
OverrideFinchCountry(std::string country_code)77 void OverrideFinchCountry(std::string country_code) {
78 const char kCountryOverride[] = "country_override";
79 SetUpExperimentOption(kCountryOverride, country_code);
80 }
81
EnableFeatureWithNoOptions()82 void EnableFeatureWithNoOptions() { SetUpExperimentOption("", ""); }
83
SetUpExperimentOption(std::string option,std::string data)84 void SetUpExperimentOption(std::string option, std::string data) {
85 base::FieldTrialParams params = {{option, data}};
86 scoped_feature_list_.InitAndEnableFeatureWithParameters(
87 chrome::android::kExploreSites, params);
88 }
89
success() const90 bool success() const { return success_; }
callback_count() const91 int callback_count() const { return callback_count_; }
92
database_status()93 GetCatalogStatus database_status() { return database_status_; }
database_categories()94 std::vector<ExploreSitesCategory>* database_categories() {
95 return database_categories_.get();
96 }
97
service()98 ExploreSitesServiceImpl* service() { return service_.get(); }
99
test_data()100 std::string test_data() { return test_data_; }
101
mostly_valid_test_data()102 std::string mostly_valid_test_data() { return mostly_valid_test_data_; }
103
bad_test_data()104 std::string bad_test_data() { return bad_test_data_; }
105
PumpLoop()106 void PumpLoop() { task_environment_.RunUntilIdle(); }
107
108 std::string CreateTestDataProto();
109 std::string CreateMostlyValidTestDataProto();
110 std::string CreateBadTestDataProto();
111
112 void SimulateFetcherData(const std::string& response_data);
113 void SimulateFetchFailure();
114
histograms() const115 const base::HistogramTester* histograms() const {
116 return histogram_tester_.get();
117 }
118
119 network::TestURLLoaderFactory::PendingRequest* GetLastPendingRequest();
120
121 void ValidateTestCatalog();
122
123 private:
124 class TestURLLoaderFactoryGetter
125 : public ExploreSitesServiceImpl::URLLoaderFactoryGetter {
126 public:
TestURLLoaderFactoryGetter(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)127 explicit TestURLLoaderFactoryGetter(
128 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
129 : url_loader_factory_(url_loader_factory) {}
GetFactory()130 scoped_refptr<network::SharedURLLoaderFactory> GetFactory() override {
131 return url_loader_factory_;
132 }
133
134 private:
135 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
136 DISALLOW_COPY_AND_ASSIGN(TestURLLoaderFactoryGetter);
137 };
138
139 base::test::ScopedFeatureList scoped_feature_list_;
140
141 std::unique_ptr<explore_sites::ExploreSitesServiceImpl> service_;
142 bool success_;
143 int callback_count_;
144 GetCatalogStatus database_status_;
145 std::unique_ptr<std::vector<ExploreSitesCategory>> database_categories_;
146 std::string test_data_;
147 std::string mostly_valid_test_data_;
148 std::string bad_test_data_;
149 network::TestURLLoaderFactory test_url_loader_factory_;
150 scoped_refptr<network::SharedURLLoaderFactory>
151 test_shared_url_loader_factory_;
152 network::ResourceRequest last_resource_request_;
153 std::unique_ptr<base::HistogramTester> histogram_tester_;
154 base::test::SingleThreadTaskEnvironment task_environment_{
155 base::test::SingleThreadTaskEnvironment::MainThreadType::IO,
156 base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME};
157
158 DISALLOW_COPY_AND_ASSIGN(ExploreSitesServiceImplTest);
159 };
160
ExploreSitesServiceImplTest()161 ExploreSitesServiceImplTest::ExploreSitesServiceImplTest()
162 : success_(false),
163 callback_count_(0),
164 test_shared_url_loader_factory_(
165 base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
166 &test_url_loader_factory_)) {}
167
168 // Called by tests - response_data is the data we want to go back as the
169 // response from the network.
SimulateFetcherData(const std::string & response_data)170 void ExploreSitesServiceImplTest::SimulateFetcherData(
171 const std::string& response_data) {
172 PumpLoop();
173
174 DCHECK(test_url_loader_factory_.pending_requests()->size() > 0);
175 test_url_loader_factory_.SimulateResponseForPendingRequest(
176 GetLastPendingRequest()->request.url.spec(), response_data, net::HTTP_OK,
177 network::TestURLLoaderFactory::kMostRecentMatch);
178 }
179
180 // Called by tests - Will return a http failure.
SimulateFetchFailure()181 void ExploreSitesServiceImplTest::SimulateFetchFailure() {
182 PumpLoop();
183
184 DCHECK(test_url_loader_factory_.pending_requests()->size() > 0);
185 test_url_loader_factory_.SimulateResponseForPendingRequest(
186 GetLastPendingRequest()->request.url.spec(), "", net::HTTP_BAD_REQUEST,
187 network::TestURLLoaderFactory::kMostRecentMatch);
188 }
189
190 // Helper to check the next request for the network.
191 network::TestURLLoaderFactory::PendingRequest*
GetLastPendingRequest()192 ExploreSitesServiceImplTest::GetLastPendingRequest() {
193 EXPECT_GT(test_url_loader_factory_.pending_requests()->size(), 0U)
194 << "No pending request!";
195 network::TestURLLoaderFactory::PendingRequest* request =
196 &(test_url_loader_factory_.pending_requests()->back());
197 return request;
198 }
199
ValidateTestCatalog()200 void ExploreSitesServiceImplTest::ValidateTestCatalog() {
201 EXPECT_EQ(GetCatalogStatus::kSuccess, database_status());
202 EXPECT_NE(nullptr, database_categories());
203 EXPECT_EQ(1U, database_categories()->size());
204
205 const ExploreSitesCategory& database_category = database_categories()->at(0);
206 EXPECT_EQ(Category_CategoryType_TECHNOLOGY, database_category.category_type);
207 EXPECT_EQ(std::string(kTechnologyCategoryName), database_category.label);
208 EXPECT_EQ(2U, database_category.sites.size());
209
210 // Since the site name and url might come back in a different order than we
211 // started with, accept either order as long as one name and url match.
212 EXPECT_NE(database_category.sites[0].site_id,
213 database_category.sites[1].site_id);
214 std::string site1Url = database_category.sites[0].url.spec();
215 std::string site2Url = database_category.sites[1].url.spec();
216 std::string site1Name = database_category.sites[0].title;
217 std::string site2Name = database_category.sites[1].title;
218 EXPECT_TRUE(site1Url == kSite1Url || site1Url == kSite2Url);
219 EXPECT_TRUE(site2Url == kSite1Url || site2Url == kSite2Url);
220 EXPECT_TRUE(site1Name == kSite1Name || site1Name == kSite2Name);
221 EXPECT_TRUE(site2Name == kSite1Name || site2Name == kSite2Name);
222 }
223
224 // This is a helper to generate testing data to use in tests.
CreateTestDataProto()225 std::string ExploreSitesServiceImplTest::CreateTestDataProto() {
226 std::string serialized_protobuf;
227 explore_sites::GetCatalogResponse catalog_response;
228 catalog_response.set_version_token("abcd");
229 explore_sites::Catalog* catalog = catalog_response.mutable_catalog();
230 explore_sites::Category* category = catalog->add_categories();
231 explore_sites::Site* site1 = category->add_sites();
232 explore_sites::Site* site2 = category->add_sites();
233
234 // Fill in fields we need to add to the EoS database.
235
236 // Create two sites. We create one with no trailing slash. The trailing
237 // slash should be added when we convert it to a GURL for canonicalization.
238 site1->set_site_url(kSite1UrlNoTrailingSlash);
239 site1->set_title(kSite1Name);
240 site2->set_site_url(kSite2Url);
241 site2->set_title(kSite2Name);
242
243 // Create one category, technology.
244 category->set_type(Category_CategoryType_TECHNOLOGY);
245 category->set_localized_title(kTechnologyCategoryName);
246
247 // Serialize the catalog into a string.
248 catalog_response.SerializeToString(&serialized_protobuf);
249
250 // Print out the string
251 DVLOG(1) << "test data proto '" << serialized_protobuf << "'";
252
253 return serialized_protobuf;
254 }
255
256 // This is a helper to generate testing data to use in tests.
CreateMostlyValidTestDataProto()257 std::string ExploreSitesServiceImplTest::CreateMostlyValidTestDataProto() {
258 std::string serialized_protobuf;
259 explore_sites::GetCatalogResponse catalog_response;
260 catalog_response.set_version_token("abcd");
261 explore_sites::Catalog* catalog = catalog_response.mutable_catalog();
262 explore_sites::Category* category = catalog->add_categories();
263 explore_sites::Site* site1 = category->add_sites();
264 explore_sites::Site* site2 = category->add_sites();
265 explore_sites::Site* site3 = category->add_sites();
266 explore_sites::Site* site4 = category->add_sites();
267
268 // Fill in fields we need to add to the EoS database.
269
270 // Create some sites. The first two are valid, the third is missing a URL,
271 // the fourth is missing a title.
272 site1->set_site_url(kSite1UrlNoTrailingSlash);
273 site1->set_title(kSite1Name);
274 site2->set_site_url(kSite2Url);
275 site2->set_title(kSite2Name);
276 site3->set_title(kSite3Name);
277 site4->set_site_url(kSite4Url);
278
279 // Create one category, technology.
280 category->set_type(Category_CategoryType_TECHNOLOGY);
281 category->set_localized_title(kTechnologyCategoryName);
282
283 // Serialize the catalog into a string.
284 catalog_response.SerializeToString(&serialized_protobuf);
285
286 return serialized_protobuf;
287 }
288
289 // This is a helper to generate testing data to use in tests. We intentionally
290 // create catalogs and sites with problems to make the code emit histograms. We
291 // create categories and sites with the following intentional defects:
292 // A category with a type outside the known range.
293 // A category with no sites.
294 // A category with no title.
295 // A category with only one site, which is invalid and gets removed.
296 // A site with a malformed url.
297 // A site with a missing title, as the only site in one category.
CreateBadTestDataProto()298 std::string ExploreSitesServiceImplTest::CreateBadTestDataProto() {
299 std::string serialized_protobuf;
300 explore_sites::GetCatalogResponse catalog_response;
301 catalog_response.set_version_token("abcd");
302 explore_sites::Catalog* catalog = catalog_response.mutable_catalog();
303 explore_sites::Category* technology = catalog->add_categories();
304 explore_sites::Category* anime = catalog->add_categories();
305 explore_sites::Category* food = catalog->add_categories();
306 explore_sites::Category* books = catalog->add_categories();
307 explore_sites::Category* science = catalog->add_categories();
308 explore_sites::Site* site1 = technology->add_sites();
309 explore_sites::Site* site2 = technology->add_sites();
310 explore_sites::Site* site3 = books->add_sites();
311
312 // Site 1 will be a totally valid site.
313 site1->set_site_url(kSite1Url);
314 site1->set_title(kSite1Name);
315
316 // Site 2 will be missing a title.
317 site2->set_site_url(kSite2Url);
318 site2->set_title("");
319
320 // Site 3 will have a malformed URL.
321 site3->set_site_url("123456");
322 site3->set_title(kSite3Name);
323
324 // Fill out the technology category with valid data.
325 // It will get one good website, and one with a missing title.
326 // It should be left intact by validation, but with only one site.
327 technology->set_type(Category_CategoryType_TECHNOLOGY);
328 technology->set_localized_title(kTechnologyCategoryName);
329
330 // Fill out the anime category with a bad type value.
331 anime->set_type(static_cast<Category_CategoryType>(1000));
332 anime->set_localized_title("Anime");
333
334 // Don't add a title to the food category.
335 food->set_type(Category_CategoryType_FOOD);
336 food->set_localized_title("");
337
338 // The science category is valid, but has no web sites, so it should be
339 // removed by validation.
340 science->set_type(Category_CategoryType_SCIENCE);
341 science->set_localized_title(kScienceCategoryName);
342
343 // Fill out the books category. It will get a website with a bad URL, and it
344 // should end up getting removed since all its sites are invalid.
345 books->set_type(Category_CategoryType_BOOKS);
346 books->set_localized_title(kBooksCategoryName);
347
348 // Serialize the catalog into a string.
349 catalog_response.SerializeToString(&serialized_protobuf);
350
351 // Print out the string
352 DVLOG(1) << "bad test data proto '" << serialized_protobuf << "'";
353
354 return serialized_protobuf;
355 }
356
TEST_F(ExploreSitesServiceImplTest,UpdateCatalogFromNetwork)357 TEST_F(ExploreSitesServiceImplTest, UpdateCatalogFromNetwork) {
358 base::test::ScopedFeatureList scoped_feature_list;
359 scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
360
361 service()->UpdateCatalogFromNetwork(
362 true /*is_immediate_fetch*/, kAcceptLanguages,
363 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
364 base::Unretained(this)));
365
366 // Simulate fetching using the test loader factory and test data.
367 SimulateFetcherData(test_data());
368
369 // Wait for callback to get called.
370 PumpLoop();
371
372 EXPECT_TRUE(success());
373
374 // Get the catalog and verify the contents.
375
376 // First call is to get update_catalog out of the way. If GetCatalog has
377 // never been called before in this session, it won't return anything, it will
378 // just start the update process. For our test, we've already put data into
379 // the catalog, but GetCatalog doesn't know that.
380 service()->GetCatalog(base::BindOnce(
381 &ExploreSitesServiceImplTest::CatalogCallback, base::Unretained(this)));
382 PumpLoop();
383
384 ValidateTestCatalog();
385
386 histograms()->ExpectBucketCount(
387 "ExploreSites.CatalogRequestResult",
388 ExploreSitesCatalogUpdateRequestResult::kNewCatalog, 1);
389 }
390
TEST_F(ExploreSitesServiceImplTest,MultipleUpdateCatalogFromNetwork)391 TEST_F(ExploreSitesServiceImplTest, MultipleUpdateCatalogFromNetwork) {
392 base::test::ScopedFeatureList scoped_feature_list;
393 scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
394
395 service()->UpdateCatalogFromNetwork(
396 false /*is_immediate_fetch*/, kAcceptLanguages,
397 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
398 base::Unretained(this)));
399
400 service()->UpdateCatalogFromNetwork(
401 true /*is_immediate_fetch*/, kAcceptLanguages,
402 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
403 base::Unretained(this)));
404
405 service()->UpdateCatalogFromNetwork(
406 true /*is_immediate_fetch*/, kAcceptLanguages,
407 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
408 base::Unretained(this)));
409
410 // Simulate fetching using the test loader factory and test data.
411 SimulateFetcherData(test_data());
412
413 // Wait for callback to get called.
414 PumpLoop();
415
416 EXPECT_TRUE(success());
417 EXPECT_EQ(3, callback_count());
418 }
419
TEST_F(ExploreSitesServiceImplTest,GetCachedCatalogFromNetwork)420 TEST_F(ExploreSitesServiceImplTest, GetCachedCatalogFromNetwork) {
421 base::test::ScopedFeatureList scoped_feature_list;
422 scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
423
424 service()->UpdateCatalogFromNetwork(
425 false /*is_immediate_fetch*/, kAcceptLanguages,
426 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
427 base::Unretained(this)));
428 PumpLoop();
429 EXPECT_THAT(GetLastPendingRequest()->request.url.query(),
430 Not(HasSubstr("version_token=abcd")));
431
432 SimulateFetcherData(test_data());
433 PumpLoop();
434 EXPECT_TRUE(success());
435
436 service()->UpdateCatalogFromNetwork(
437 false /*is_immediate_fetch*/, kAcceptLanguages,
438 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
439 base::Unretained(this)));
440 PumpLoop();
441 EXPECT_THAT(GetLastPendingRequest()->request.url.query(),
442 HasSubstr("version_token=abcd"));
443
444 explore_sites::GetCatalogResponse catalog_response;
445 catalog_response.set_version_token("abcd");
446 std::string serialized_response;
447 catalog_response.SerializeToString(&serialized_response);
448 SimulateFetcherData(serialized_response);
449
450 PumpLoop();
451
452 // Get the catalog and verify the contents.
453
454 // First call is to get update_catalog out of the way. If GetCatalog has
455 // never been called before in this session, it won't return anything, it will
456 // just start the update process. For our test, we've already put data into
457 // the catalog, but GetCatalog doesn't know that.
458 // TODO(petewil): Fix get catalog so it always returns data if it has some.
459 service()->GetCatalog(base::BindOnce(
460 &ExploreSitesServiceImplTest::CatalogCallback, base::Unretained(this)));
461 PumpLoop();
462
463 ValidateTestCatalog();
464 }
465
TEST_F(ExploreSitesServiceImplTest,UpdateCatalogReturnsNoProtobuf)466 TEST_F(ExploreSitesServiceImplTest, UpdateCatalogReturnsNoProtobuf) {
467 base::test::ScopedFeatureList scoped_feature_list;
468 scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
469
470 // Pretend that a catalog has been fetched and returns with an empty protobuf.
471 std::unique_ptr<std::string> empty_serialized_protobuf;
472 service()->OnCatalogFetchedForTest(ExploreSitesRequestStatus::kSuccess,
473 std::move(empty_serialized_protobuf));
474
475 histograms()->ExpectBucketCount(
476 "ExploreSites.CatalogRequestResult",
477 ExploreSitesCatalogUpdateRequestResult::kExistingCatalogIsCurrent, 1);
478 }
479
TEST_F(ExploreSitesServiceImplTest,FailedCatalogFetch)480 TEST_F(ExploreSitesServiceImplTest, FailedCatalogFetch) {
481 base::test::ScopedFeatureList scoped_feature_list;
482 scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
483
484 service()->UpdateCatalogFromNetwork(
485 true /*is_immediate_fetch*/, kAcceptLanguages,
486 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
487 base::Unretained(this)));
488
489 // Simulate fetching using the test loader factory and test data.
490 SimulateFetchFailure();
491
492 // Wait for callback to get called.
493 PumpLoop();
494
495 histograms()->ExpectBucketCount(
496 "ExploreSites.CatalogRequestResult",
497 ExploreSitesCatalogUpdateRequestResult::kFailure, 1);
498 }
499
TEST_F(ExploreSitesServiceImplTest,BadCatalogHistograms)500 TEST_F(ExploreSitesServiceImplTest, BadCatalogHistograms) {
501 base::test::ScopedFeatureList scoped_feature_list;
502 scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
503
504 service()->UpdateCatalogFromNetwork(
505 true /*is_immediate_fetch*/, kAcceptLanguages,
506 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
507 base::Unretained(this)));
508
509 // Simulate fetching using the test loader factory and test data.
510 SimulateFetcherData(bad_test_data());
511
512 // Wait for callback to get called.
513 PumpLoop();
514
515 // Expect that we detected flaws in the data and reported the following
516 // histograms.
517 histograms()->ExpectTotalCount("ExploreSites.CatalogError", 7);
518 histograms()->ExpectBucketCount(
519 "ExploreSites.CatalogError",
520 ExploreSitesCatalogError::kCategoryMissingTitle, 1);
521 histograms()->ExpectBucketCount(
522 "ExploreSites.CatalogError",
523 ExploreSitesCatalogError::kCategoryWithUnknownType, 1);
524 histograms()->ExpectBucketCount(
525 "ExploreSites.CatalogError",
526 ExploreSitesCatalogError::kCategoryWithNoSites, 2);
527 histograms()->ExpectBucketCount("ExploreSites.CatalogError",
528 ExploreSitesCatalogError::kSiteWithBadUrl, 1);
529 histograms()->ExpectBucketCount("ExploreSites.CatalogError",
530 ExploreSitesCatalogError::kSiteMissingTitle,
531 1);
532 }
533
TEST_F(ExploreSitesServiceImplTest,MostlyValidCatalogHistograms)534 TEST_F(ExploreSitesServiceImplTest, MostlyValidCatalogHistograms) {
535 base::test::ScopedFeatureList scoped_feature_list;
536 scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
537
538 service()->UpdateCatalogFromNetwork(
539 true /*is_immediate_fetch*/, kAcceptLanguages,
540 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
541 base::Unretained(this)));
542
543 // Simulate fetching using the test loader factory and test data.
544 SimulateFetcherData(mostly_valid_test_data());
545
546 // Wait for callback to get called.
547 PumpLoop();
548
549 // Expect that we detected flaws in the data and reported the following
550 // histograms.
551 histograms()->ExpectBucketCount("ExploreSites.CatalogError",
552 ExploreSitesCatalogError::kSiteWithBadUrl, 1);
553 histograms()->ExpectBucketCount("ExploreSites.CatalogError",
554 ExploreSitesCatalogError::kSiteMissingTitle,
555 1);
556 }
557
TEST_F(ExploreSitesServiceImplTest,UnparseableCatalogHistograms)558 TEST_F(ExploreSitesServiceImplTest, UnparseableCatalogHistograms) {
559 base::test::ScopedFeatureList scoped_feature_list;
560 scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
561
562 service()->UpdateCatalogFromNetwork(
563 true /*is_immediate_fetch*/, kAcceptLanguages,
564 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
565 base::Unretained(this)));
566
567 // Simulate fetching using a URL where the code expects a serialized protobuf.
568 SimulateFetcherData(kSite1Url);
569
570 // Wait for callback to get called.
571 PumpLoop();
572
573 histograms()->ExpectBucketCount("ExploreSites.CatalogError",
574 ExploreSitesCatalogError::kParseFailure, 1);
575 }
576
TEST_F(ExploreSitesServiceImplTest,BlockNonCanonicalUrls)577 TEST_F(ExploreSitesServiceImplTest, BlockNonCanonicalUrls) {
578 base::test::ScopedFeatureList scoped_feature_list;
579 scoped_feature_list.InitAndEnableFeature(chrome::android::kExploreSites);
580
581 service()->UpdateCatalogFromNetwork(
582 false /*is_immediate_fetch*/, kAcceptLanguages,
583 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
584 base::Unretained(this)));
585 // Simulate fetching using the test loader factory and test data.
586 SimulateFetcherData(test_data());
587
588 // Wait for callback to get called.
589 PumpLoop();
590 ASSERT_TRUE(success());
591 ASSERT_EQ(1, callback_count());
592 service()->GetCatalog(base::BindOnce(
593 &ExploreSitesServiceImplTest::CatalogCallback, base::Unretained(this)));
594 PumpLoop();
595 ValidateTestCatalog();
596
597 // This will fail if canonicalization does not work correctly because
598 // kSite1Url is the canonicalized version of the URL inserted in to the
599 // database.
600 service()->BlockSite(kSite1Url);
601 PumpLoop();
602
603 service()->GetCatalog(base::BindOnce(
604 &ExploreSitesServiceImplTest::CatalogCallback, base::Unretained(this)));
605 PumpLoop();
606
607 EXPECT_EQ(2U, database_categories()->at(0).sites.size());
608 EXPECT_TRUE(database_categories()->at(0).sites.at(0).is_blocked);
609 EXPECT_FALSE(database_categories()->at(0).sites.at(1).is_blocked);
610 }
611
TEST_F(ExploreSitesServiceImplTest,CountryCodeDefault)612 TEST_F(ExploreSitesServiceImplTest, CountryCodeDefault) {
613 EnableFeatureWithNoOptions();
614
615 ASSERT_EQ("DEFAULT", service()->GetCountryCode());
616 service()->UpdateCatalogFromNetwork(
617 false, kAcceptLanguages,
618 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
619 base::Unretained(this)));
620 PumpLoop();
621 EXPECT_THAT(GetLastPendingRequest()->request.url.query(),
622 HasSubstr("country_code=DEFAULT"));
623 }
624
TEST_F(ExploreSitesServiceImplTest,CountryCodeFinch)625 TEST_F(ExploreSitesServiceImplTest, CountryCodeFinch) {
626 OverrideFinchCountry(kCountryCode);
627
628 EXPECT_EQ(kCountryCode, service()->GetCountryCode());
629 service()->UpdateCatalogFromNetwork(
630 false, kAcceptLanguages,
631 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
632 base::Unretained(this)));
633 PumpLoop();
634 EXPECT_THAT(GetLastPendingRequest()->request.url.query(),
635 HasSubstr("country_code=zz"));
636 }
637
TEST_F(ExploreSitesServiceImplTest,CountryCodeOverride)638 TEST_F(ExploreSitesServiceImplTest, CountryCodeOverride) {
639 OverrideFinchCountry("should_not_appear_country_code");
640 service()->OverrideCountryCodeForDebugging(kCountryCode);
641
642 EXPECT_EQ(kCountryCode, service()->GetCountryCode());
643 service()->UpdateCatalogFromNetwork(
644 false, kAcceptLanguages,
645 base::BindOnce(&ExploreSitesServiceImplTest::UpdateCatalogDoneCallback,
646 base::Unretained(this)));
647 PumpLoop();
648 EXPECT_THAT(GetLastPendingRequest()->request.url.query(),
649 HasSubstr("country_code=zz"));
650 }
651
652 } // namespace explore_sites
653