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