popular_sites_impl_unittest.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. // Copyright 2016 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. #include "components/ntp_tiles/popular_sites_impl.h"
  5. #include <map>
  6. #include <memory>
  7. #include <string>
  8. #include <utility>
  9. #include <vector>
  10. #include "base/bind.h"
  11. #include "base/command_line.h"
  12. #include "base/json/json_writer.h"
  13. #include "base/run_loop.h"
  14. #include "base/strings/string_number_conversions.h"
  15. #include "base/strings/utf_string_conversions.h"
  16. #include "base/test/scoped_feature_list.h"
  17. #include "base/test/task_environment.h"
  18. #include "base/threading/thread_task_runner_handle.h"
  19. #include "base/values.h"
  20. #include "build/branding_buildflags.h"
  21. #include "build/build_config.h"
  22. #include "components/ntp_tiles/features.h"
  23. #include "components/ntp_tiles/pref_names.h"
  24. #include "components/ntp_tiles/tile_source.h"
  25. #include "components/pref_registry/pref_registry_syncable.h"
  26. #include "components/sync_preferences/testing_pref_service_syncable.h"
  27. #include "net/http/http_status_code.h"
  28. #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
  29. #include "services/network/public/cpp/shared_url_loader_factory.h"
  30. #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
  31. #include "services/network/test/test_url_loader_factory.h"
  32. #include "testing/gmock/include/gmock/gmock.h"
  33. #include "testing/gtest/include/gtest/gtest.h"
  34. #include "third_party/abseil-cpp/absl/types/optional.h"
  35. using testing::_;
  36. using testing::Contains;
  37. using testing::ElementsAre;
  38. using testing::Eq;
  39. using testing::Gt;
  40. using testing::IsEmpty;
  41. using testing::Not;
  42. using testing::Pair;
  43. using testing::SizeIs;
  44. namespace ntp_tiles {
  45. namespace {
  46. const char kTitle[] = "title";
  47. const char kUrl[] = "url";
  48. const char kLargeIconUrl[] = "large_icon_url";
  49. const char kFaviconUrl[] = "favicon_url";
  50. const char kSection[] = "section";
  51. const char kSites[] = "sites";
  52. const char kTitleSource[] = "title_source";
  53. using TestPopularSite = std::map<std::string, std::string>;
  54. using TestPopularSiteVector = std::vector<TestPopularSite>;
  55. using TestPopularSection = std::pair<SectionType, TestPopularSiteVector>;
  56. using TestPopularSectionVector = std::vector<TestPopularSection>;
  57. ::testing::Matcher<const std::u16string&> Str16Eq(const std::string& s) {
  58. return ::testing::Eq(base::UTF8ToUTF16(s));
  59. }
  60. ::testing::Matcher<const GURL&> URLEq(const std::string& s) {
  61. return ::testing::Eq(GURL(s));
  62. }
  63. size_t GetNumberOfDefaultPopularSitesForPlatform() {
  64. #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
  65. return 8ul;
  66. #else
  67. return 0ul;
  68. #endif
  69. }
  70. class PopularSitesTest : public ::testing::Test {
  71. protected:
  72. PopularSitesTest()
  73. : kWikipedia{
  74. {kTitle, "Wikipedia, fhta Ph'nglui mglw'nafh"},
  75. {kUrl, "https://zz.m.wikipedia.org/"},
  76. {kLargeIconUrl, "https://zz.m.wikipedia.org/wikipedia.png"},
  77. {kTitleSource, "3"}, // Title extracted from title tag.
  78. },
  79. kYouTube{
  80. {kTitle, "YouTube"},
  81. {kUrl, "https://m.youtube.com/"},
  82. {kLargeIconUrl, "https://s.ytimg.com/apple-touch-icon.png"},
  83. {kTitleSource, "1"}, // Title extracted from manifest.
  84. },
  85. kChromium{
  86. {kTitle, "The Chromium Project"},
  87. {kUrl, "https://www.chromium.org/"},
  88. {kFaviconUrl, "https://www.chromium.org/favicon.ico"},
  89. // No "title_source" (like in v5 or earlier). Defaults to TITLE_TAG.
  90. },
  91. prefs_(new sync_preferences::TestingPrefServiceSyncable()),
  92. test_shared_loader_factory_(
  93. base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
  94. &test_url_loader_factory_)) {
  95. PopularSitesImpl::RegisterProfilePrefs(prefs_->registry());
  96. }
  97. void SetCountryAndVersion(const std::string& country,
  98. const std::string& version) {
  99. prefs_->SetString(prefs::kPopularSitesOverrideCountry, country);
  100. prefs_->SetString(prefs::kPopularSitesOverrideVersion, version);
  101. }
  102. base::Value::List CreateListFromTestSites(
  103. const TestPopularSiteVector& sites) {
  104. base::Value::List sites_value;
  105. for (const TestPopularSite& site : sites) {
  106. base::Value::Dict site_value;
  107. for (const std::pair<const std::string, std::string>& kv : site) {
  108. if (kv.first == kTitleSource) {
  109. int source;
  110. bool convert_success = base::StringToInt(kv.second, &source);
  111. DCHECK(convert_success);
  112. site_value.Set(kv.first, source);
  113. continue;
  114. }
  115. site_value.Set(kv.first, kv.second);
  116. }
  117. sites_value.Append(std::move(site_value));
  118. }
  119. return sites_value;
  120. }
  121. void RespondWithV5JSON(const std::string& url,
  122. const TestPopularSiteVector& sites) {
  123. std::string sites_string;
  124. base::JSONWriter::Write(CreateListFromTestSites(sites), &sites_string);
  125. test_url_loader_factory_.AddResponse(url, sites_string);
  126. }
  127. void RespondWithV6JSON(const std::string& url,
  128. const TestPopularSectionVector& sections) {
  129. base::Value::List sections_value;
  130. sections_value.reserve(sections.size());
  131. for (const TestPopularSection& section : sections) {
  132. base::Value::Dict section_value;
  133. section_value.Set(kSection, static_cast<int>(section.first));
  134. section_value.Set(kSites, CreateListFromTestSites(section.second));
  135. sections_value.Append(std::move(section_value));
  136. }
  137. std::string sites_string;
  138. base::JSONWriter::Write(sections_value, &sites_string);
  139. test_url_loader_factory_.AddResponse(url, sites_string);
  140. }
  141. void RespondWithData(const std::string& url, const std::string& data) {
  142. test_url_loader_factory_.AddResponse(url, data);
  143. }
  144. void RespondWith404(const std::string& url) {
  145. test_url_loader_factory_.AddResponse(url, "", net::HTTP_NOT_FOUND);
  146. }
  147. void ReregisterProfilePrefs() {
  148. prefs_ = std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
  149. PopularSitesImpl::RegisterProfilePrefs(prefs_->registry());
  150. }
  151. // Returns an optional bool representing whether the completion callback was
  152. // called at all, and if yes which was the returned bool value.
  153. absl::optional<bool> FetchPopularSites(bool force_download,
  154. PopularSites::SitesVector* sites) {
  155. std::map<SectionType, PopularSites::SitesVector> sections;
  156. absl::optional<bool> save_success =
  157. FetchAllSections(force_download, &sections);
  158. *sites = sections.at(SectionType::PERSONALIZED);
  159. return save_success;
  160. }
  161. // Returns an optional bool representing whether the completion callback was
  162. // called at all, and if yes which was the returned bool value.
  163. absl::optional<bool> FetchAllSections(
  164. bool force_download,
  165. std::map<SectionType, PopularSites::SitesVector>* sections) {
  166. std::unique_ptr<PopularSites> popular_sites = CreatePopularSites();
  167. base::RunLoop loop;
  168. absl::optional<bool> save_success;
  169. if (popular_sites->MaybeStartFetch(
  170. force_download, base::BindOnce(
  171. [](absl::optional<bool>* save_success,
  172. base::RunLoop* loop, bool success) {
  173. save_success->emplace(success);
  174. loop->Quit();
  175. },
  176. &save_success, &loop))) {
  177. loop.Run();
  178. }
  179. *sections = popular_sites->sections();
  180. return save_success;
  181. }
  182. std::unique_ptr<PopularSites> CreatePopularSites() {
  183. return std::make_unique<PopularSitesImpl>(prefs_.get(),
  184. /*template_url_service=*/nullptr,
  185. /*variations_service=*/nullptr,
  186. test_shared_loader_factory_);
  187. }
  188. const TestPopularSite kWikipedia;
  189. const TestPopularSite kYouTube;
  190. const TestPopularSite kChromium;
  191. base::test::SingleThreadTaskEnvironment task_environment_{
  192. base::test::SingleThreadTaskEnvironment::MainThreadType::UI};
  193. data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
  194. std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> prefs_;
  195. network::TestURLLoaderFactory test_url_loader_factory_;
  196. scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
  197. };
  198. TEST_F(PopularSitesTest, ContainsDefaultTilesRightAfterConstruction) {
  199. auto popular_sites = CreatePopularSites();
  200. EXPECT_THAT(
  201. popular_sites->sections(),
  202. ElementsAre(Pair(SectionType::PERSONALIZED,
  203. SizeIs(GetNumberOfDefaultPopularSitesForPlatform()))));
  204. }
  205. TEST_F(PopularSitesTest, IsEmptyOnConstructionIfDisabledByTrial) {
  206. base::test::ScopedFeatureList override_features;
  207. override_features.InitAndDisableFeature(kPopularSitesBakedInContentFeature);
  208. ReregisterProfilePrefs();
  209. auto popular_sites = CreatePopularSites();
  210. EXPECT_THAT(popular_sites->sections(),
  211. ElementsAre(Pair(SectionType::PERSONALIZED, IsEmpty())));
  212. }
  213. TEST_F(PopularSitesTest, ShouldSucceedFetching) {
  214. SetCountryAndVersion("ZZ", "5");
  215. RespondWithV5JSON(
  216. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
  217. {kWikipedia});
  218. PopularSites::SitesVector sites;
  219. EXPECT_THAT(FetchPopularSites(/*force_download=*/true, &sites),
  220. Eq(absl::optional<bool>(true)));
  221. ASSERT_THAT(sites.size(), Eq(1u));
  222. EXPECT_THAT(sites[0].title, Str16Eq("Wikipedia, fhta Ph'nglui mglw'nafh"));
  223. EXPECT_THAT(sites[0].url, URLEq("https://zz.m.wikipedia.org/"));
  224. EXPECT_THAT(sites[0].large_icon_url,
  225. URLEq("https://zz.m.wikipedia.org/wikipedia.png"));
  226. EXPECT_THAT(sites[0].favicon_url, URLEq(""));
  227. EXPECT_THAT(sites[0].title_source, Eq(TileTitleSource::TITLE_TAG));
  228. }
  229. TEST_F(PopularSitesTest, Fallback) {
  230. SetCountryAndVersion("ZZ", "5");
  231. RespondWith404(
  232. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json");
  233. RespondWithV5JSON(
  234. "https://www.gstatic.com/chrome/ntp/suggested_sites_DEFAULT_5.json",
  235. {kYouTube, kChromium});
  236. PopularSites::SitesVector sites;
  237. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  238. Eq(absl::optional<bool>(true)));
  239. ASSERT_THAT(sites.size(), Eq(2u));
  240. EXPECT_THAT(sites[0].title, Str16Eq("YouTube"));
  241. EXPECT_THAT(sites[0].url, URLEq("https://m.youtube.com/"));
  242. EXPECT_THAT(sites[0].large_icon_url,
  243. URLEq("https://s.ytimg.com/apple-touch-icon.png"));
  244. EXPECT_THAT(sites[0].favicon_url, URLEq(""));
  245. EXPECT_THAT(sites[0].title_source, Eq(TileTitleSource::MANIFEST));
  246. EXPECT_THAT(sites[1].title, Str16Eq("The Chromium Project"));
  247. EXPECT_THAT(sites[1].url, URLEq("https://www.chromium.org/"));
  248. EXPECT_THAT(sites[1].large_icon_url, URLEq(""));
  249. EXPECT_THAT(sites[1].favicon_url,
  250. URLEq("https://www.chromium.org/favicon.ico"));
  251. // Fall back to TITLE_TAG if there is no "title_source". Version 5 or before
  252. // haven't had this property and get titles from <title> tags exclusively.
  253. EXPECT_THAT(sites[1].title_source, Eq(TileTitleSource::TITLE_TAG));
  254. }
  255. TEST_F(PopularSitesTest, PopulatesWithDefaultResoucesOnFailure) {
  256. SetCountryAndVersion("ZZ", "5");
  257. RespondWith404(
  258. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json");
  259. RespondWith404(
  260. "https://www.gstatic.com/chrome/ntp/suggested_sites_DEFAULT_5.json");
  261. PopularSites::SitesVector sites;
  262. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  263. Eq(absl::optional<bool>(false)));
  264. EXPECT_THAT(sites.size(), Eq(GetNumberOfDefaultPopularSitesForPlatform()));
  265. }
  266. #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
  267. TEST_F(PopularSitesTest, AddsIconResourcesToDefaultPages) {
  268. std::unique_ptr<PopularSites> popular_sites = CreatePopularSites();
  269. const PopularSites::SitesVector& sites =
  270. popular_sites->sections().at(SectionType::PERSONALIZED);
  271. ASSERT_FALSE(sites.empty());
  272. for (const auto& site : sites) {
  273. EXPECT_TRUE(site.baked_in);
  274. #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  275. EXPECT_THAT(site.default_icon_resource, Gt(0));
  276. #endif
  277. }
  278. }
  279. #endif
  280. TEST_F(PopularSitesTest, ProvidesDefaultSitesUntilCallbackReturns) {
  281. SetCountryAndVersion("ZZ", "5");
  282. RespondWithV5JSON(
  283. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
  284. {kWikipedia});
  285. std::unique_ptr<PopularSites> popular_sites = CreatePopularSites();
  286. base::RunLoop loop;
  287. absl::optional<bool> save_success = false;
  288. bool callback_was_scheduled = popular_sites->MaybeStartFetch(
  289. /*force_download=*/true, base::BindOnce(
  290. [](absl::optional<bool>* save_success,
  291. base::RunLoop* loop, bool success) {
  292. save_success->emplace(success);
  293. loop->Quit();
  294. },
  295. &save_success, &loop));
  296. // Assert that callback was scheduled so we can wait for its completion.
  297. ASSERT_TRUE(callback_was_scheduled);
  298. // There should be 8 default sites as nothing was fetched yet.
  299. EXPECT_THAT(popular_sites->sections().at(SectionType::PERSONALIZED).size(),
  300. Eq(GetNumberOfDefaultPopularSitesForPlatform()));
  301. loop.Run(); // Wait for the fetch to finish and the callback to return.
  302. EXPECT_TRUE(save_success.value());
  303. // The 1 fetched site should replace the default sites.
  304. EXPECT_THAT(popular_sites->sections(),
  305. ElementsAre(Pair(SectionType::PERSONALIZED, SizeIs(1ul))));
  306. }
  307. TEST_F(PopularSitesTest, UsesCachedJson) {
  308. SetCountryAndVersion("ZZ", "5");
  309. RespondWithV5JSON(
  310. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
  311. {kWikipedia});
  312. // First request succeeds and gets cached.
  313. PopularSites::SitesVector sites;
  314. ASSERT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  315. Eq(absl::optional<bool>(true)));
  316. // File disappears from server, but we don't need it because it's cached.
  317. RespondWith404(
  318. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json");
  319. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  320. Eq(absl::nullopt));
  321. EXPECT_THAT(sites[0].url, URLEq("https://zz.m.wikipedia.org/"));
  322. }
  323. TEST_F(PopularSitesTest, CachesEmptyFile) {
  324. SetCountryAndVersion("ZZ", "5");
  325. RespondWithData(
  326. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json", "[]");
  327. RespondWithV5JSON(
  328. "https://www.gstatic.com/chrome/ntp/suggested_sites_DEFAULT_5.json",
  329. {kWikipedia});
  330. // First request succeeds and caches empty suggestions list (no fallback).
  331. PopularSites::SitesVector sites;
  332. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  333. Eq(absl::optional<bool>(true)));
  334. EXPECT_THAT(sites, IsEmpty());
  335. // File appears on server, but we continue to use our cached empty file.
  336. RespondWithV5JSON(
  337. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
  338. {kWikipedia});
  339. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  340. Eq(absl::nullopt));
  341. EXPECT_THAT(sites, IsEmpty());
  342. }
  343. TEST_F(PopularSitesTest, DoesntUseCachedFileIfDownloadForced) {
  344. SetCountryAndVersion("ZZ", "5");
  345. RespondWithV5JSON(
  346. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
  347. {kWikipedia});
  348. // First request succeeds and gets cached.
  349. PopularSites::SitesVector sites;
  350. EXPECT_THAT(FetchPopularSites(/*force_download=*/true, &sites),
  351. Eq(absl::optional<bool>(true)));
  352. EXPECT_THAT(sites[0].url, URLEq("https://zz.m.wikipedia.org/"));
  353. // File disappears from server. Download is forced, so we get the new file.
  354. RespondWithV5JSON(
  355. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
  356. {kChromium});
  357. EXPECT_THAT(FetchPopularSites(/*force_download=*/true, &sites),
  358. Eq(absl::optional<bool>(true)));
  359. EXPECT_THAT(sites[0].url, URLEq("https://www.chromium.org/"));
  360. }
  361. TEST_F(PopularSitesTest, DoesntUseCacheWithDeprecatedVersion) {
  362. SetCountryAndVersion("ZZ", "5");
  363. RespondWithV5JSON(
  364. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
  365. {kWikipedia});
  366. // First request succeeds and gets cached.
  367. PopularSites::SitesVector sites;
  368. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  369. Eq(absl::optional<bool>(true)));
  370. EXPECT_THAT(sites[0].url, URLEq("https://zz.m.wikipedia.org/"));
  371. EXPECT_THAT(prefs_->GetInteger(prefs::kPopularSitesVersionPref), Eq(5));
  372. // The client is updated to use V6. Drop old data and refetch.
  373. SetCountryAndVersion("ZZ", "6");
  374. RespondWithV6JSON(
  375. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_6.json",
  376. {{SectionType::PERSONALIZED, {kChromium}}});
  377. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  378. Eq(absl::optional<bool>(true)));
  379. EXPECT_THAT(sites[0].url, URLEq("https://www.chromium.org/"));
  380. EXPECT_THAT(prefs_->GetInteger(prefs::kPopularSitesVersionPref), Eq(6));
  381. }
  382. TEST_F(PopularSitesTest, FallsBackToDefaultParserIfVersionContainsNoNumber) {
  383. SetCountryAndVersion("ZZ", "staging");
  384. // The version is used in the URL, as planned when setting it.
  385. RespondWithV5JSON(
  386. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_staging.json",
  387. {kChromium});
  388. PopularSites::SitesVector sites;
  389. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  390. Eq(absl::optional<bool>(true)));
  391. ASSERT_THAT(sites.size(), Eq(1u));
  392. EXPECT_THAT(sites[0].url, URLEq("https://www.chromium.org/"));
  393. }
  394. TEST_F(PopularSitesTest, RefetchesAfterCountryMoved) {
  395. RespondWithV5JSON(
  396. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
  397. {kWikipedia});
  398. RespondWithV5JSON(
  399. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZX_5.json",
  400. {kChromium});
  401. PopularSites::SitesVector sites;
  402. // First request (in ZZ) saves Wikipedia.
  403. SetCountryAndVersion("ZZ", "5");
  404. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  405. Eq(absl::optional<bool>(true)));
  406. EXPECT_THAT(sites[0].url, URLEq("https://zz.m.wikipedia.org/"));
  407. // Second request (now in ZX) saves Chromium.
  408. SetCountryAndVersion("ZX", "5");
  409. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  410. absl::optional<bool>(true));
  411. EXPECT_THAT(sites[0].url, URLEq("https://www.chromium.org/"));
  412. }
  413. TEST_F(PopularSitesTest, DoesntCacheInvalidFile) {
  414. SetCountryAndVersion("ZZ", "5");
  415. RespondWithData(
  416. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
  417. "ceci n'est pas un json");
  418. RespondWith404(
  419. "https://www.gstatic.com/chrome/ntp/suggested_sites_DEFAULT_5.json");
  420. // First request falls back and gets nothing there either.
  421. PopularSites::SitesVector sites;
  422. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  423. Eq(absl::optional<bool>(false)));
  424. // Second request refetches ZZ_9, which now has data.
  425. RespondWithV5JSON(
  426. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
  427. {kChromium});
  428. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  429. Eq(absl::optional<bool>(true)));
  430. ASSERT_THAT(sites.size(), Eq(1u));
  431. EXPECT_THAT(sites[0].url, URLEq("https://www.chromium.org/"));
  432. }
  433. TEST_F(PopularSitesTest, RefetchesAfterFallback) {
  434. SetCountryAndVersion("ZZ", "5");
  435. RespondWith404(
  436. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json");
  437. RespondWithV5JSON(
  438. "https://www.gstatic.com/chrome/ntp/suggested_sites_DEFAULT_5.json",
  439. {kWikipedia});
  440. // First request falls back.
  441. PopularSites::SitesVector sites;
  442. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  443. Eq(absl::optional<bool>(true)));
  444. ASSERT_THAT(sites.size(), Eq(1u));
  445. EXPECT_THAT(sites[0].url, URLEq("https://zz.m.wikipedia.org/"));
  446. // Second request refetches ZZ_9, which now has data.
  447. RespondWithV5JSON(
  448. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
  449. {kChromium});
  450. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  451. Eq(absl::optional<bool>(true)));
  452. ASSERT_THAT(sites.size(), Eq(1u));
  453. EXPECT_THAT(sites[0].url, URLEq("https://www.chromium.org/"));
  454. }
  455. TEST_F(PopularSitesTest, ShouldOverrideDirectory) {
  456. SetCountryAndVersion("ZZ", "5");
  457. prefs_->SetString(prefs::kPopularSitesOverrideDirectory, "foo/bar/");
  458. RespondWithV5JSON("https://www.gstatic.com/foo/bar/suggested_sites_ZZ_5.json",
  459. {kWikipedia});
  460. PopularSites::SitesVector sites;
  461. EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
  462. Eq(absl::optional<bool>(true)));
  463. EXPECT_THAT(sites.size(), Eq(1u));
  464. }
  465. TEST_F(PopularSitesTest, DoesNotFetchExplorationSites) {
  466. SetCountryAndVersion("ZZ", "6");
  467. RespondWithV6JSON(
  468. "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_6.json",
  469. {{SectionType::PERSONALIZED, {kChromium}},
  470. {SectionType::NEWS, {kYouTube}}});
  471. std::map<SectionType, PopularSites::SitesVector> sections;
  472. EXPECT_THAT(FetchAllSections(/*force_download=*/false, &sections),
  473. Eq(absl::optional<bool>(true)));
  474. // The fetched news section should not be propagated without enabled feature.
  475. EXPECT_THAT(sections, Not(Contains(Pair(SectionType::NEWS, _))));
  476. }
  477. } // namespace
  478. } // namespace ntp_tiles